diff --git a/chatto/src/main/javascript/app.css b/chatto/src/main/javascript/app.css
new file mode 100644
index 0000000..059d217
--- /dev/null
+++ b/chatto/src/main/javascript/app.css
@@ -0,0 +1,3 @@
+@import url("node_modules/alertifyjs/build/css/alertify.css");
+@import url("node_modules/alertifyjs/build/css/themes/bootstrap.min.css");
+@import url("node_modules/alertifyjs/build/css/themes/default.min.css");
\ No newline at end of file
diff --git a/chatto/src/main/javascript/chat.js b/chatto/src/main/javascript/chat.js
new file mode 100644
index 0000000..f09b282
--- /dev/null
+++ b/chatto/src/main/javascript/chat.js
@@ -0,0 +1,557 @@
+// import { sprintf } from 'sprintf-js';
+// import { vsprintf } from 'sprintf-js';
+/*var off_payment_method = document.getElementsByName('offline_payment_method');
+var ischecked_method = false;
+for ( var i = 0; i < off_payment_method.length; i++) {
+ if(off_payment_method[i].checked) {
+ ischecked_method = true;
+ break;
+ }
+}
+if(!ischecked_method) { //payment method button is not checked
+ alert("Please choose Offline Payment Method");
+}*/
+var css = require('./app.css');
+console.log(css);
+var log = require('loglevel');
+var alertify = require('alertifyjs');
+require('sjcl');
+var md = require('markdown-it')();
+var Handlebars = require('handlebars');
+var DOMPurify = require('dompurify');
+require('fuse.js');
+
+var toUserRadios = document.getElementsByName('toUser');
+var isCheckedUser = false;
+var chatTextArea = document.getElementById('chatTextArea');
+
+var postNewMessageUrl = `http://${hostAddress}/api/chat/post/message`; //hostAddress variable is set in the thymeleaf head fragment
+var getAllMessagesUrl = `http://${hostAddress}/api/chat/get/messages/`;
+var getNewMessagesUrl = `http://${hostAddress}/api/chat/get/messages/`;
+var getActiveUsersUrl = `http://${hostAddress}/api/chat/get/active-users/`;
+// var postNewMessageUrl = "http://localhost:8080/api/chat/post/message";
+// var getAllMessagesUrl = "http://localhost:8080/api/chat/get/messages/";
+// var getNewMessagesUrl = "http://localhost:8080/api/chat/get/messages/";
+// var messageLog = [];
+var username = localStorage.getItem('username');
+var authToken = localStorage.getItem('authToken');
+
+var passphraseInput = document.getElementById('passphrase');
+var iterations = 100000;
+
+var source = document.getElementById("msg_container_template").innerHTML;
+var msgContainerTemplate = Handlebars.compile(source);
+var source = document.getElementById("msg_container_send_template").innerHTML;
+var msgContainerSendTemplate = Handlebars.compile(source);
+var source = document.getElementById("user-contact-online-template").innerHTML;
+var userContactOnlineTemplate = Handlebars.compile(source);
+var source = document.getElementById("user-contact-offline-template").innerHTML;
+var userContactOfflineTemplate = Handlebars.compile(source);
+
+var chatAreaNew = document.getElementById('chat_area_new');
+
+var userBoxes = document.getElementsByName('user-box');
+
+
+
+var activeUsers = {};
+
+var fuseOptions = {
+ shouldSort: true,
+ threshold: 0.01,
+ location: 0,
+ distance: 100,
+ maxPatternLength: 32,
+ minMatchCharLength: 1,
+ keys: [
+ "userName",
+ ]
+};
+
+log.setLevel('TRACE');
+
+alertify.set('notifier', 'position', 'top-center');
+
+// Loop through the buttons and add the active class to the current/clicked button
+// for (var i = 0; i < btns.length; i++) {
+// btns[i].addEventListener("click", function() {
+// var current = document.getElementsByClassName("active");
+
+// // If there's no active class
+// if (current.length > 0) {
+// current[0].className = current[0].className.replace(" active", "");
+// }
+
+// // Add the active class to the current/clicked button
+// this.className += " active";
+// });
+// }
+
+getActiveUsers(authToken)
+ .then(data => {
+ // activeUsers = data;
+ sessionStorage.setItem('activeUsers', JSON.stringify(data));
+ log.log(sessionStorage.getItem('activeUsers'));
+ })
+
+for (let i = 0; i < userBoxes.length; i++) {
+ userBoxes[i].addEventListener('click', userCallBack)
+}
+
+function addUserCallBacks() {
+ for (let i = 0; i < userBoxes.length; i++) {
+ userBoxes[i].addEventListener('click', userCallBack)
+ }
+}
+
+function userCallBack() {
+ let current = document.getElementsByClassName('user-box active');
+ let passphrase = passphraseInput.value;
+ if (current.length > 0) {
+ if (passphrase == '') {
+ // alert('Please input passphrase')
+ alertify.error('Please enter a passphrase');
+ return;
+ }
+ current[0].className = current[0].className.replace(" active", "");
+
+ }
+ // Add the active class to the current/clicked button
+ else if (current.length == 0) {
+ let elem = document.getElementById('passphrase-initial');
+ passphrase = elem.value;
+ if (passphrase == '') {
+ // alert('Please input passphrase')
+ alertify.error('Please enter a passphrase');
+ return;
+ }
+ document.getElementById('no-user-selected').hidden = true;
+ document.getElementById('chat-card').hidden = false;
+ elem.hidden = true;
+ }
+ // console.log(this.getElementsByClassName('to-user-span'));
+ let userName = this.getElementsByClassName('to-user-span')[0].innerText;
+ document.getElementById('user-name-span').innerText = userName;
+ populateMessages(userName, passphrase);
+ sessionStorage.setItem('selectedUser', userName);
+ this.className += " active";
+}
+
+function populateMessages(userName, passphrase) {
+ console.log('Selected user = ' + userName);
+ if (passphrase == '') {
+ alert('Please input passphrase')
+ return;
+ }
+ // console.log(userName);
+ if (sessionStorage.getItem(userName) == null) {
+ chatTextArea.textContent = '';
+ chatAreaNew.innerHTML = '';
+ getAllMessages(userName)
+ .then(json => {
+ if (json == null) return;
+ console.log(json);
+ let i = 0;
+ let messageLog = [];
+ let messageLogNew = [];
+ let lastMessageTimeStamp;
+
+ if (json.length > 0) {
+ json.forEach(function(obj) {
+ // console.log(obj.toUser);
+ messageCipher = JSON.stringify(obj.messageCipher);
+ console.log(messageCipher);
+ // let message = sjcl.decrypt("password", messageCipher);
+ let message = md.render(sjcl.decrypt(passphrase, messageCipher));
+ let utcDate = obj.messageTime;
+ lastMessageTimeStamp = utcDate;
+ let localDate = new Date(utcDate);
+ let messageLine = sprintf('%s %s: %s ', localDate, obj.fromUser, message);
+
+
+ // localDate.``
+ // console.log('localDate = ' + localDate);
+ console.log(messageLine);
+ // chatTextArea.append(obj.fromUser + ": " + message + "\n");
+ chatTextArea.append(messageLine + '\n');
+ messageLog[i++] = messageLine;
+ chatTextArea.scrollTop = chatTextArea.scrollHeight;
+ // console.log('Message log = ' + messageLog);
+
+ let context = { fromUser: obj.fromUser, message: message, time: localDate.toLocaleString() };
+ let msgContainer;
+ if (obj.fromUser == username) {
+ msgContainer = msgContainerSendTemplate(context);
+ } else {
+ msgContainer = msgContainerTemplate(context);
+ }
+
+ messageLogNew.push(JSON.stringify(context));
+ $(chatAreaNew).append(DOMPurify.sanitize(msgContainer));
+
+
+ });
+ sessionStorage.setItem(userName, JSON.stringify(messageLog));
+ sessionStorage.setItem(userName + username + 'new', JSON.stringify(messageLogNew));
+ // console.log()
+ // sessionStorage.clear();
+ console.log('Last message time = ' + lastMessageTimeStamp);
+ sessionStorage.setItem(userName + '-time', lastMessageTimeStamp);
+
+
+ }
+ });
+ } else {
+
+
+ console.log("Stored messages = " + sessionStorage.getItem(userName));
+ let storedMessages = JSON.parse(sessionStorage.getItem(userName));
+ let storedMessagesNew = JSON.parse(sessionStorage.getItem(userName + username + 'new'));
+ let lastMessageTime = sessionStorage.getItem(userName + '-time');
+ console.log("last message time stamp = " + lastMessageTime);
+ if (lastMessageTime != null) {
+ getNewMessages(userName, lastMessageTime)
+ .then(json => {
+ if (json == null) return;
+ console.log(json)
+ if (json.length > 0) {
+ json.forEach(function(obj) {
+ let messageCipher = JSON.stringify(obj.messageCipher);
+ let message = md.render(sjcl.decrypt(passphrase, messageCipher));
+ // console.log(message);
+ // chatTextArea.append(message + "\n");
+ let utcDate = obj.messageTime;
+ lastMessageTimeStamp = utcDate;
+ let localDate = new Date(utcDate);
+ let messageLine = sprintf('%s %s: %s', localDate, obj.fromUser, message);
+
+ // localDate.``
+ // console.log('localDate = ' + localDate);
+ console.log(messageLine);
+ // chatTextArea.append(obj.fromUser + ": " + message + "\n");
+ chatTextArea.append(messageLine + '\n');
+ chatTextArea.scrollTop = chatTextArea.scrollHeight;
+ storedMessages.push(messageLine);
+
+ let context = { fromUser: obj.fromUser, message: message, time: localDate.toLocaleString() };
+ let msgContainer;
+ if (obj.fromUser == username) {
+ msgContainer = msgContainerSendTemplate(context);
+ } else {
+ msgContainer = msgContainerTemplate(context);
+ }
+
+ storedMessagesNew.push(JSON.stringify(context));
+ $(chatAreaNew).append(DOMPurify.sanitize(msgContainer));
+
+ })
+ sessionStorage.setItem(userName + '-time', lastMessageTimeStamp);
+ sessionStorage.setItem(userName, JSON.stringify(storedMessages));
+ sessionStorage.setItem(userName + username + 'new', JSON.stringify(storedMessagesNew));
+ console.log("this value stored" + sessionStorage.getItem(userName))
+ console.log("last message time stamp = " + lastMessageTimeStamp);
+ console.log(sessionStorage.getItem(userName + '-time'));
+
+ }
+ chatTextArea.textContent = '';
+ chatAreaNew.innerHTML = '';
+ console.log("Stored messages 2 = " + storedMessages);
+ storedMessages.forEach(function(messageLine) {
+ chatTextArea.append(messageLine + '\n');
+ chatTextArea.scrollTop = chatTextArea.scrollHeight;
+
+ // let context = {message: messageLine};
+ // let msgContainer;
+ // if(obj.fromUser == username)
+ // {
+ // msgContainer = msgContainerSendTemplate(context);
+ // }
+ // else{
+ // msgContainer = msgContainerTemplate(context);
+ // }
+
+ // $(chatAreaNew).append(msgContainer);
+ })
+
+ storedMessagesNew.forEach(function(contextString) {
+ let context = JSON.parse(contextString);
+ let msgContainer;
+ if (context.fromUser == username) {
+ msgContainer = msgContainerSendTemplate(context);
+ } else {
+ msgContainer = msgContainerTemplate(context);
+ }
+
+ $(chatAreaNew).append(DOMPurify.sanitize(msgContainer));
+ scrollChatAreaAnimated(2400);
+ })
+
+
+ });
+
+
+ }
+ // chatTextArea.append(JSON.stringify(storedMessages));
+
+ }
+ // sessionStorage.setItem('status', 'ready');
+ // sessionStorage.setItem('userName', messageLog);
+ // console.log('Message log = ' + messageLog);
+ // }
+
+ // let passphraseKey = userName + '-passphrase';
+ // sessionStorage.setItem(passphraseKey, passphrase);
+ // console.log(sessionStorage.getItem(passphraseKey));
+}
+
+// var lastMessageTimeStamp;
+
+// console.log(authToken);
+// 'Basic ' + btoa("hmm" + ":" + "hmm")
+
+Handlebars.registerHelper('avatar', function() {
+ return '
';
+});
+
+
+// var user;
+function getSelectedUser() {
+ for (var i = 0; i < toUserRadios.length; i++) {
+ if (toUserRadios[i].checked) {
+ let user = toUserRadios[i].value;
+ console.log('sending to user = ' + user);
+ isCheckedUser = true;
+ return user;
+ }
+ }
+
+}
+
+function getSelectedUserNew() {
+ return sessionStorage.getItem('selectedUser');
+}
+
+document.getElementById('chatMessageForm').addEventListener('submit', function(e) {
+ let chatInput = document.getElementById('chatInput');
+ e.preventDefault();
+ let user = getSelectedUserNew();
+
+ if (!this.checkValidity()) {
+ console.log("error");
+ this.classList.add('was-validated');
+ return;
+ }
+ this.classList.add('was-validated');
+
+ if (user == null) {
+ // window.alert('please select a user');
+ alertify.error('Please select a user');
+ return;
+ }
+ let messageContent = chatInput.value;
+ let context = { fromUser: username, message: md.render(messageContent), time: new Date().toLocaleString() };
+ let msgContainer = msgContainerSendTemplate(context);
+ $(chatAreaNew).append(DOMPurify.sanitize(msgContainer));
+ scrollChatAreaAnimated(2400);
+ let messageCipher = sjcl.encrypt(passphraseInput.value, messageContent, { mode: "gcm", ts: 128, adata: "", iter: iterations });
+ let messageCipherJson = JSON.parse(messageCipher);
+ let chatMessageDTO = {
+ "toUser": user,
+ "messageCipher": messageCipherJson
+ }
+ messageSend(JSON.stringify(chatMessageDTO));
+})
+
+document.getElementById('user-search').addEventListener('submit', function(e) {
+ e.preventDefault();
+ let contactsBox = document.getElementById('contacts-box');
+ let temp = contactsBox.innerHTML;
+ // log.trace(temp);
+ let searchTerm = document.getElementById('user-search-term').value;
+ log.debug("search term value = " + searchTerm);
+ let list = JSON.parse(sessionStorage.getItem('activeUsers'));
+ log.debug("active users");
+ log.debug(list);
+ let fuse = new Fuse(list, fuseOptions);
+ let searchResult = fuse.search(searchTerm);
+ populateContactsBox(contactsBox, searchResult);
+ addUserCallBacks();
+ log.debug(searchResult);
+})
+document.getElementById('user-search-term').addEventListener('input', function(e) {
+ e.preventDefault();
+ if (this.value.length < 2) {
+ log.debug("inputted")
+ let cancelButton = document.getElementById('user-search-cancel');
+ cancelButton.hidden = false;
+ }
+})
+document.getElementById('user-search-cancel').addEventListener('click', function(e) {
+ e.preventDefault();
+ let list = JSON.parse(sessionStorage.getItem('activeUsers'));
+ let contactsBox = document.getElementById('contacts-box');
+ populateContactsBox(contactsBox,list);
+ addUserCallBacks();
+ document.getElementById('user-search-term').value = "";
+ this.hidden = true;
+})
+
+function populateContactsBox(contactsBox, list)
+{
+ let userContactBoxList = "";
+ list.forEach(function(activeUser) {
+ log.debug(activeUser);
+ if (activeUser.online) {
+ userContactBoxList += userContactOnlineTemplate(activeUser);
+ } else {
+ userContactBoxList += userContactOfflineTemplate(activeUser);
+ }
+ })
+ contactsBox.innerHTML = userContactBoxList;
+}
+
+
+
+// console.log('Credentials = ' + JSON.parse(sessionStorage.getItem('credentials')));
+
+
+function messageSend(chatMessageDTO) {
+ let headers = new Headers();
+ // console.log("Token = " + btoa("hmm" + ":" + "hmm"))
+
+ // headers.append('Accept','application/json')
+ headers.append('Content-Type', 'application/json');
+ // headers.append('Authorization', basicAuthToken);
+ headers.append('X-AUTH-TOKEN', authToken);
+ fetch(postNewMessageUrl, {
+ method: 'POST',
+ headers: headers,
+ body: chatMessageDTO
+ })
+ .then(response => {
+ console.log(response);
+ return response.clone();
+ })
+ .then(response => fetchHandler(response));
+}
+
+
+async function getAllMessages(toUser) {
+ let headers = new Headers();
+ // headers.append('Accept','application/json')
+ // headers.append('Content-Type', 'application/json');
+ // headers.append('Authorization', basicAuthToken);
+ headers.append('X-AUTH-TOKEN', authToken);
+ let response = await fetch(getAllMessagesUrl + toUser, {
+ method: 'GET',
+ headers: headers
+ });
+ console.log(response);
+ if (fetchErrorHandler(response.clone())) {
+ return null;
+ }
+ // if (response.status == 440) {
+ // window.alert('Token has expired. Please login again');
+ // return null;
+ // }
+ let data = await response.json();
+ return data;
+}
+
+async function getNewMessages(toUser, lastMessageTimeStamp) {
+ let headers = new Headers();
+ // headers.append('Authorization', basicAuthToken);
+ headers.append('X-AUTH-TOKEN', authToken);
+ let response = await fetch(`${getNewMessagesUrl}${toUser}/${lastMessageTimeStamp}`, {
+ method: 'GET',
+ headers: headers
+ });
+ console.log(response.clone());
+ if (fetchErrorHandler(response.clone())) {
+ return null;
+ }
+ let data = await response.json();
+ return data;
+}
+
+
+async function getActiveUsers(authToken2) {
+ let headers = new Headers();
+ // headers.append('Authorization', basicAuthToken);
+ headers.append('X-AUTH-TOKEN', authToken2);
+ let response = await fetch(getActiveUsersUrl, {
+ method: 'GET',
+ headers: headers
+ });
+ console.log(response.clone());
+ if (fetchErrorHandler(response.clone())) {
+ return null;
+ }
+ let data = await response.json();
+ return data;
+}
+
+$(document).ready(function() {
+ $('#action_menu_btn').click(function() {
+ $('.action_menu').toggle();
+ });
+});
+
+function fetchHandler(response) {
+ if (response.ok) {
+ return response.json().then(json => {
+ // the status was ok and there is a json body
+ // return Promise.resolve({ json: json, response: response });
+ alertify.success('Message sent succesfully' + sprintf(" (http code %d)", response.status));
+ }).catch(err => {
+ // the status was ok but there is no json body
+ // return Promise.resolve({ response: response });
+ alertify.success('Message sent succesfully' + sprintf(" (http code %d)", response.status));
+ });
+
+ } else {
+ return response.json().catch(err => {
+ // the status was not ok and there is no json body
+ // throw new Error(response.statusText);
+ alertify.error('Some error occured. Please try again.');
+ }).then(json => {
+ // the status was not ok but there is a json body
+ // throw new Error(json.error.message); // example error message returned by a REST
+ let delay = alertify.get('notifier', 'delay');
+ alertify.set('notifier', 'delay', 30);
+ let errorMessage = "";
+ json.errors.forEach(function(data) {
+ errorMessage += sprintf("Field Name: %s \n Rejected value: %s \n Reason: %s \n", data.field_name, data.rejected_value, data.error_message);
+ });
+ alertify.error(sprintf('There were errors in your message - %s', errorMessage));
+ alertify.set('notifier', 'delay', delay);
+ });
+ }
+}
+
+
+function fetchErrorHandler(response) {
+ // alertify.success('Current position : ' + alertify.get('notifier', 'position'));
+ if (!response.ok) {
+ return response.text().catch(err => {
+ // the status was not ok and there is no json body
+ // throw new Error(response.statusText);
+ // window.alert(sprintf('Some error occured. Http code is %s', response.status));
+ alertify.error(sprintf('Some error occured. Http code is %s', response.status));
+ return true;
+ }).then(json => {
+ // the status was not ok but there is a json body
+ // throw new Error(json.error.message); // example error message returned by a REST API
+ // window.alert(sprintf('Error: %s (Http code %s)', json, response.status));
+ alertify.error(sprintf('Some error occured. Http code is %s', response.status));
+ console.log(json);
+ return true;
+ });
+ }
+}
+
+function scrollChatAreaAnimated(delay) {
+ $(chatAreaNew).stop().animate({
+ scrollTop: $(chatAreaNew)[0].scrollHeight
+ }, delay);
+}
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/.bin/atob b/chatto/src/main/javascript/node_modules/.bin/atob
new file mode 120000
index 0000000..a68344a
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/.bin/atob
@@ -0,0 +1 @@
+../atob/bin/atob.js
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/.bin/handlebars b/chatto/src/main/javascript/node_modules/.bin/handlebars
new file mode 120000
index 0000000..fb7d090
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/.bin/handlebars
@@ -0,0 +1 @@
+../handlebars/bin/handlebars
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/.bin/markdown-it b/chatto/src/main/javascript/node_modules/.bin/markdown-it
new file mode 120000
index 0000000..894bcdb
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/.bin/markdown-it
@@ -0,0 +1 @@
+../markdown-it/bin/markdown-it.js
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/.bin/mime b/chatto/src/main/javascript/node_modules/.bin/mime
new file mode 120000
index 0000000..fbb7ee0
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/.bin/mime
@@ -0,0 +1 @@
+../mime/cli.js
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/.bin/uglifyjs b/chatto/src/main/javascript/node_modules/.bin/uglifyjs
new file mode 120000
index 0000000..fef3468
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/.bin/uglifyjs
@@ -0,0 +1 @@
+../uglify-js/bin/uglifyjs
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/.bin/which b/chatto/src/main/javascript/node_modules/.bin/which
new file mode 120000
index 0000000..f62471c
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/.bin/which
@@ -0,0 +1 @@
+../which/bin/which
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/.yarn-integrity b/chatto/src/main/javascript/node_modules/.yarn-integrity
new file mode 100644
index 0000000..0701b33
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/.yarn-integrity
@@ -0,0 +1,51 @@
+{
+ "systemParams": "linux-x64-72",
+ "modulesFolders": [
+ "node_modules"
+ ],
+ "flags": [],
+ "linkedModules": [],
+ "topLevelPatterns": [
+ "alertifyjs@^1.12.0",
+ "chart.js@^2.9.3",
+ "dompurify@^2.0.7",
+ "fuse.js@^3.4.6",
+ "handlebars@^4.5.3",
+ "loglevel@^1.6.6",
+ "markdown-it@^10.0.0",
+ "sjcl@^1.0.8"
+ ],
+ "lockfileEntries": {
+ "alertifyjs@^1.12.0": "https://registry.yarnpkg.com/alertifyjs/-/alertifyjs-1.12.0.tgz#75be1eac23e059d3b990ab076979d36a45aa21ac",
+ "argparse@^1.0.7": "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911",
+ "chart.js@^2.9.3": "https://registry.yarnpkg.com/chart.js/-/chart.js-2.9.3.tgz#ae3884114dafd381bc600f5b35a189138aac1ef7",
+ "chartjs-color-string@^0.6.0": "https://registry.yarnpkg.com/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz#1df096621c0e70720a64f4135ea171d051402f71",
+ "chartjs-color@^2.1.0": "https://registry.yarnpkg.com/chartjs-color/-/chartjs-color-2.4.1.tgz#6118bba202fe1ea79dd7f7c0f9da93467296c3b0",
+ "color-convert@^1.9.3": "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8",
+ "color-name@1.1.3": "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25",
+ "color-name@^1.0.0": "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2",
+ "commander@~2.20.3": "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33",
+ "dompurify@^2.0.7": "https://registry.yarnpkg.com/dompurify/-/dompurify-2.0.7.tgz#f8266ad38fe1602fb5b3222f31eedbf5c16c4fd5",
+ "entities@~2.0.0": "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4",
+ "fuse.js@^3.4.6": "https://registry.yarnpkg.com/fuse.js/-/fuse.js-3.4.6.tgz#545c3411fed88bf2e27c457cab6e73e7af697a45",
+ "handlebars@^4.5.3": "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482",
+ "linkify-it@^2.0.0": "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf",
+ "loglevel@^1.6.6": "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.6.tgz#0ee6300cc058db6b3551fa1c4bf73b83bb771312",
+ "markdown-it@^10.0.0": "https://registry.yarnpkg.com/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc",
+ "mdurl@^1.0.1": "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e",
+ "minimist@~0.0.1": "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf",
+ "moment@^2.10.2": "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b",
+ "neo-async@^2.6.0": "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c",
+ "optimist@^0.6.1": "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686",
+ "sjcl@^1.0.8": "https://registry.yarnpkg.com/sjcl/-/sjcl-1.0.8.tgz#f2ec8d7dc1f0f21b069b8914a41a8f236b0e252a",
+ "source-map@^0.6.1": "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263",
+ "source-map@~0.6.1": "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263",
+ "sprintf-js@~1.0.2": "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c",
+ "uc.micro@^1.0.1": "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac",
+ "uc.micro@^1.0.5": "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac",
+ "uglify-js@^3.1.4": "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.1.tgz#35c7de17971a4aa7689cd2eae0a5b39bb838c0c5",
+ "wordwrap@~0.0.2": "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
+ },
+ "files": [],
+ "artifacts": {}
+}
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/.jshintrc b/chatto/src/main/javascript/node_modules/alertifyjs/.jshintrc
new file mode 100644
index 0000000..a7c64c3
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/.jshintrc
@@ -0,0 +1,38 @@
+{
+ "boss": true,
+ "curly": true,
+ "eqeqeq": true,
+ "eqnull": true,
+ "es5": false,
+ "forin": true,
+ "immed": true,
+ "indent": 4,
+ "latedef": true,
+ "newcap": true,
+ "noarg": true,
+ "node": true,
+ "noempty": true,
+ "plusplus": true,
+ "quotmark": "single",
+ "smarttabs": false,
+ "scripturl": true,
+ "strict": false,
+ "sub": true,
+ "trailing": true,
+ "undef": true,
+ "unused": true,
+
+ "globals": {
+ "window": true,
+ "document": true,
+ "alertify": true,
+ "define": true,
+
+ "triggerEvent": true,
+ "suite": true,
+ "test": true,
+ "assert": true,
+ "setup": true,
+ "teardown": true
+ }
+}
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/CONTRIBUTING.md b/chatto/src/main/javascript/node_modules/alertifyjs/CONTRIBUTING.md
new file mode 100644
index 0000000..9ddac8e
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/CONTRIBUTING.md
@@ -0,0 +1,22 @@
+Bugs
+====
+If you are reporting a bug you must create a test-case. You can fork this [codepen](http://codepen.io/anon/pen/raohK) or this [jsfiddle](http://jsfiddle.net/g2o52zq7/) to get started.
+
+Also include re-produce steps and environement specs, such as OS and Browser (**We support last 2 versions only**).
+
+> Remember, calls to `alertify.alert`, `alertify.confirm` and `alertify.prompt` are none blocking.
+ ```
+ //bad
+ if(alertify.confirm('Are you sure?')){
+ // this would execute immediatly
+ }
+ //good
+ alertify.confirm('Are you sure?', function(){
+ //this would execute when the user clicks ok
+ });
+ ```
+
+
+Questions
+====
+For how-to questions, please post on [stackoverflow](http://stackoverflow.com/questions/tagged/alertifyjs) and don't forget to tag with `alertifyjs`.
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/Gruntfile.js b/chatto/src/main/javascript/node_modules/alertifyjs/Gruntfile.js
new file mode 100644
index 0000000..36dd757
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/Gruntfile.js
@@ -0,0 +1,235 @@
+module.exports = function (grunt) {
+ 'use strict';
+
+ // Project configuration.
+ grunt.initConfig({
+ pkg: grunt.file.readJSON('package.json'),
+
+ clean: {
+ build: ['staging', 'build']
+ },
+
+ usebanner: {
+ dist: {
+ options: {
+ position: 'top',
+ banner: '/**\n' +
+ ' * <%= pkg.name %> <%= pkg.version %> <%= pkg.homepage %>\n' +
+ ' * <%= pkg.description %>\n' +
+ ' * Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author %> \n' +
+ ' * Licensed under <%= pkg.licenses[0].type %> <<%= pkg.licenses[0].url %>>*/\n',
+ linebreak: false
+ },
+ files: {
+ src: ['build/**/*.css']
+ }
+ }
+ },
+
+ less: {
+ options: {
+ paths: ['./src/less'],
+ compress: false,
+ },
+ compile: {
+ expand: true,
+ cwd: 'src/less',
+ src: ['**/*.less'],
+ ext: '.css',
+ dest: 'staging'
+ }
+ },
+
+ postcss: {
+ options: {
+ map: false,
+ processors: [
+ require('autoprefixer')({browsers: 'last 2 versions'}),
+ ]
+ },
+ build: {
+ expand: true,
+ cwd: 'staging/',
+ dest: 'staging/',
+ src: ['**/*.css'],
+ },
+ },
+
+ rtlcss: {
+ prefixRules: {
+ options: {
+ rules: [
+ {
+ 'name': 'prefix',
+ 'expr': /.*/img,
+ 'important': true,
+ 'action': function (rule) {
+ //prefix rules with ajs- and ignore further processing
+ rule.selector = rule.selector.replace(/\.(?!alertify)/g, '.ajs-');
+ return true;
+ }
+ }
+ ]
+ },
+ expand: true,
+ cwd: 'staging',
+ src: ['**/*.css'],
+ dest: 'staging',
+ },
+ build: {
+ expand : true,
+ cwd: 'staging',
+ src: ['**/*.css'],
+ dest: 'staging/rtl',
+ ext: '.css',
+ }
+ },
+
+ cssmin: {
+ options: {
+ report: 'gzip'
+ },
+ min: {
+ expand:true,
+ cwd:'staging',
+ src:['*.css', 'themes/*.css'],
+ dest:'build/css',
+ ext: '.min.css',
+ },
+ rtl: {
+ expand:true,
+ cwd:'staging/rtl',
+ src:['**/*.css'],
+ dest:'build/css',
+ ext: '.rtl.min.css',
+ }
+ },
+
+ copy: {
+ ltr:{
+ expand:true,
+ cwd:'staging',
+ src:['*.css', 'themes/*.css'],
+ dest:'build/css',
+ ext:'.css'
+ },
+ rtl:{
+ expand:true,
+ cwd:'staging/rtl',
+ src:['**/*.css'],
+ dest:'build/css',
+ ext:'.rtl.css'
+ },
+ build:{
+ expand:true,
+ cwd:'build',
+ src:['**'],
+ dest:'docpad/files/build'
+ }
+ },
+
+ concat: {
+ options: {
+ stripBanners: false,
+ banner: '/**\n' +
+ ' * <%= pkg.name %> <%= pkg.version %> <%= pkg.homepage %>\n' +
+ ' * <%= pkg.description %>\n' +
+ ' * Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author %> \n' +
+ ' * Licensed under <%= pkg.licenses[0].type %> <<%= pkg.licenses[0].url %>>*/\n',
+ },
+ dist: {
+ src: [
+ 'src/js/intro.js',
+ 'src/js/event.js',
+
+ 'src/js/dialog/intro.js',
+ 'src/js/dialog/commands.js',
+ 'src/js/dialog/actions.js',
+ 'src/js/dialog/focus.js',
+ 'src/js/dialog/transition.js',
+ 'src/js/dialog/move.js',
+ 'src/js/dialog/resize.js',
+ 'src/js/dialog/events.js',
+ 'src/js/dialog/dialog.js',
+ 'src/js/dialog/outro.js',
+
+ 'src/js/notifier.js',
+ 'src/js/alertify.js',
+ 'src/js/alert.js',
+ 'src/js/confirm.js',
+ 'src/js/prompt.js',
+ 'src/js/outro.js'
+ ],
+ dest: 'build/alertify.js'
+ }
+ },
+
+
+ jshint: {
+ files: {
+ src: [
+ 'Gruntfile.js',
+ 'build/alertify.js',
+ 'test/specs/*.js'
+ ]
+ },
+ options: {
+ jshintrc: '.jshintrc'
+ }
+ },
+
+ uglify: {
+ options: {
+ banner: '/*! <%= pkg.name %> - v<%= pkg.version %> - ' +
+ '<%= pkg.author %> */\n',
+ report: 'gzip'
+ },
+ dist: {
+ files: {
+ 'build/alertify.min.js': ['', 'build/alertify.js']
+ }
+ }
+ },
+
+ watch: {
+ src: {
+ files: ['src/**/*.js'],
+ tasks: ['build']
+ }
+ },
+
+ compress: {
+ options: {
+ archive: 'build/alertifyjs.zip'
+ },
+ build: {
+ files: [
+ {
+ expand:true,
+ cwd: 'build',
+ src: ['**']
+ }
+ ]
+ }
+ }
+ });
+
+ grunt.loadNpmTasks('grunt-rtlcss');
+ grunt.loadNpmTasks('grunt-postcss');
+ grunt.loadNpmTasks('grunt-contrib-less');
+ grunt.loadNpmTasks('grunt-contrib-clean');
+ grunt.loadNpmTasks('grunt-contrib-concat');
+ grunt.loadNpmTasks('grunt-contrib-connect');
+ grunt.loadNpmTasks('grunt-contrib-jshint');
+ grunt.loadNpmTasks('grunt-contrib-uglify');
+ grunt.loadNpmTasks('grunt-contrib-cssmin');
+ grunt.loadNpmTasks('grunt-contrib-copy');
+ grunt.loadNpmTasks('grunt-contrib-watch');
+ grunt.loadNpmTasks('grunt-contrib-compress');
+ grunt.loadNpmTasks('grunt-banner');
+
+ // Default task
+ grunt.registerTask('css', ['less', 'postcss:build', 'rtlcss', 'copy:rtl', 'copy:ltr', 'cssmin', 'usebanner']);
+ grunt.registerTask('build', ['clean:build', 'css', 'concat', 'uglify', 'compress', 'copy:build']);
+ grunt.registerTask('default', ['build', 'jshint']);
+};
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/LICENSE b/chatto/src/main/javascript/node_modules/alertifyjs/LICENSE
new file mode 100644
index 0000000..c65825e
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {one line to give the program's name and a brief idea of what it does.}
+ Copyright (C) {year} {name of author}
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ {project} Copyright (C) {year} {fullname}
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/README.md b/chatto/src/main/javascript/node_modules/alertifyjs/README.md
new file mode 100644
index 0000000..995e7f9
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/README.md
@@ -0,0 +1,85 @@
+[![GitHub version](https://badge.fury.io/gh/MohammadYounes%2FAlertifyJS.svg)](http://badge.fury.io/gh/MohammadYounes%2FAlertifyJS)
+[![NuGet version](https://badge.fury.io/nu/AlertifyJS.svg)](http://badge.fury.io/nu/AlertifyJS)
+[![npm version](https://badge.fury.io/js/alertifyjs.svg)](http://badge.fury.io/js/alertifyjs)
+[![jsDelivr Hits](https://data.jsdelivr.com/v1/package/npm/alertifyjs/badge?style=rounded)](https://www.jsdelivr.com/package/npm/alertifyjs)
+
+AlertifyJS
+==========
+
+[![Join the chat at https://gitter.im/MohammadYounes/AlertifyJS](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/MohammadYounes/AlertifyJS?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+AlertifyJS is a javascript framework for developing pretty browser dialogs and notifications.
+
+> AlertifyJS is an extreme makeover of alertify.js by @fabien-d
+
+
+
+### Install with [NuGet](https://www.nuget.org/packages/AlertifyJS/)
+
+```
+Install-Package AlertifyJS
+```
+
+### Install with [NPM](https://www.npmjs.com/package/alertifyjs/)
+
+```
+npm install alertifyjs --save
+```
+
+
+Documentation
+==========
+Check out the interactive documentation at http://alertifyjs.com
+
+[![alertifyjs-dialog](https://cloud.githubusercontent.com/assets/4712046/4170670/0d50b04c-3535-11e4-87a7-1ce62dd0d77e.png)](http://alertifyjs.com)
+
+
+Browser support
+==========
+Last two versions.
+
+
+
+Running documentation locally
+==========
+* Clone the repo
+```
+git clone git@github.com:MohammadYounes/AlertifyJS.git
+```
+
+* Install dev dependencies
+```
+npm update; npm install;
+```
+
+* Build the project
+```
+grunt
+```
+
+* Start documentation server
+```
+docpad run
+```
+* Open your browser to http://localhost:9778/
+
+
+Dependencies
+==========
+None.
+
+
+Community Contribution
+==========
+
+* [alertifyjs-rails](https://github.com/mkhairi/alertifyjs-rails) by **[@mkhairi](https://github.com/mkhairi)**
+* [meteor-alertifyjs](https://github.com/ovcharik/meteor-alertifyjs/) by **[@ovcharik](https://github.com/ovcharik)**
+
+Bugs & Questions
+==========
+Please review the guidelines for [contributing](https://github.com/MohammadYounes/AlertifyJS/blob/master/CONTRIBUTING.md).
+>You can fork this [codepen](http://codepen.io/anon/pen/raohK) or this [jsfiddle](http://jsfiddle.net/g2o52zq7/) to get started.
+
+
+------
+Contact: [Mohammad@alertifyjs.com](mailto:Mohammad@alertifyjs.com)
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/RELEASENOTES.md b/chatto/src/main/javascript/node_modules/alertifyjs/RELEASENOTES.md
new file mode 100644
index 0000000..b208e32
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/RELEASENOTES.md
@@ -0,0 +1,165 @@
+# Release Notes
+
+* **v1.12.0** [28 Sep. 2019]
+ * New API features:
+ * `invokeOnCloseOff` option. #218/#219
+ * Global pre/post init hooks. #216
+ * Expose notifier classes, allowing them to be renamed. #217
+ * Add `defaultFocusOff` option to Confirm dialog. #212
+ * Fix locking in tab cycle inside modals.
+
+* **v1.11.4** [17 Jun. 2018]
+ * Fix SCRIPT5045: Assignment to read-only properties is not allowed in strict mode (IE11). #210
+
+* **v1.11.3** [31 May. 2018]
+ * Prevent FOUC in case of async styles loading. #205
+ * Ensure `preventBodyShift` restores scrollbars. #206
+
+* **v1.11.2** [30 Oct. 2018]
+ * Prevent triggering duplicate callbacks - #199
+
+* **v1.11.1** [24 Mar. 2018]
+ * Set body `tabindex` only when a dialog is shown. #145
+ * Remove duplicate case statement. #181
+
+* **v1.11.0** [4 Aug. 2017]
+ * Adds `top-center` and `bottom-center` position options for notifier. #13
+
+* **v1.10.0** [12 Apr. 2017]
+ * New `onclosing` event: Gets or sets a function to invoke when the dialog is about to close. #140
+
+* **v1.9.0** [26 Jan. 2017]
+ * New notifier global option `closeButton` to add a close button to notifications, This feature helps users copy the contents of a message. Thanks (@pmusaraj - #134).
+
+* **v1.8.0** [30 Jul. 2016]
+ * Change license to GPLv3.
+ * New global option `preventBodyShift` to prevent body shifting when showing a modal dialog, You may get a double scrollbar when dialog content overflows the screen.
+ * Fixes a bug where some dialog options were uninitialized by the factory function. #108
+
+* **v1.7.1** [8 Jun. 2016]
+ * Reset Prompt Dialog default value on cancellation. #106
+
+* **v1.7.0** [26 May. 2016]
+ * New API feature - Extended set of event callbacks:
+ * `onmove`: Gets or sets a function to invoke when the dialog is about to move.
+ * `onmoved`: Gets or sets a function to invoke once the dialog has been moved.
+ * `onresize`: Gets or sets a function to invoke when the dialog is about to resize.
+ * `onresized`: Gets or sets a function to invoke once the dialog has been resized.
+ * `onmaximize`: Gets or sets a function to invoke when the dialog is about to maximize.
+ * `onmaximized`: Gets or sets a function to invoke once the dialog has been maximized.
+ * `onrestore`: Gets or sets a function to invoke when a maximized dialog is about to restore.
+ * `onrestored`: Gets or sets a function to invoke once a maximized dialog has been restored.
+
+* **v1.6.1** [20 Jan. 2016]
+ * Fixes a bug in removing classes from `body` element. #86
+
+* **v1.6.0** [23 Nov. 2015]
+ * New API feature: `bringToFront` method and `moveBounded` option.
+
+* **v1.5.0** [28 Sep. 2015]
+ * New API feature: `destroy` method.
+ * Fixes maintaining scroll position in IE. #76
+
+* **v1.4.1** [12 May. 2015]
+ * Fixes Prompt Dialog value handling. #59
+
+* **v1.4.0** [22 Apr. 2015]
+ * Prompt dialog: Added support for changing the HTML type of the input field.
+ * Support percent unit in `ResizeTo` method.
+ * Maintain document scroll position.
+ * Allow reusing dialog contents by not destroying the DOM, plus faster content clearing.
+ * Fixes the context for some callbacks.
+
+* **v1.3.0** [14 Mar. 2015]
+ * New API feature: `autoReset` option to control whether to reset dialog size/position on window resize or not.
+ * Always use a copy of buttons definition. Fixes #32
+
+* **v1.2.1** [06 Mar. 2015]
+ * Add default colors to core CSS, to make it easier to start a theme based on it.
+ * Fixes a problem with using AlertifyJS in Ember-CLI. #27
+
+* **v1.2.0** [17 Feb. 2015]
+ * New API feature: `closableByDimmer` option.
+ * Published to [NPM](https://www.npmjs.com/package/alertifyjs) **Thanks @dantman**
+ * Support installation via `npm install alertifyjs --save`.
+ * Support use of `require('alertifyjs')` in loaders such as Browserify.
+
+* **v1.1.0** [24 Jan. 2015]
+ * New API event hooks for dialog developers (onshow, onclose, onupdate)
+ * Support move for frameless dialogs.
+ * Fix Move/Resize mouse events capture when dialog contains an iframe.
+
+* **v1.0.1** [11 Jan. 2015]
+ * Re-append notifier div when body content is replaced. Fixes #17
+
+* **v1.0.0** [10 Jan. 2015]
+ * First official release.
+
+* **v0.10.2** [31 Oct. 2014]
+ * Add missing notifier styles to Semantic/Bootstrap themes.
+
+* **v0.10.1** [30 Oct. 2014]
+ * Fix ESC key handling for button-less dialogs.
+
+* **v0.10.0** [30 Oct. 2014]
+ * Ability to use arrow keys to switch between dialog buttons.
+ * New API features:
+ * Frameless dialog view mode.
+ * Start maximized option.
+ * Move the dialog to a specific X,Y coordinates: `moveTo`.
+ * Resize the dialog to a specific Width,Height : `resizeTo`.
+ * Fixes:
+ * Fix initial resizable width for IE.
+ * Fix resize bug when body contents height is smaller than window size.
+
+* **v0.9.0** [14 Oct. 2014]
+ * New API feature: new options to determine focus element.
+
+* **v0.8.0** [11 Oct. 2014]
+ * New dialog option: Basic view mode.
+ * Support creating button-less dialogs.
+ * Fixes tab cycle for Opera.
+ * Fixes missing focus outline for FireFox.
+ * Fixes prompt dialog 5-paramters constructor. **Thanks @TomTasche**
+
+* **v0.7.0** [27 Sep. 2014]
+ * New API features :
+ * Close all open dialogs
+ * Close all open dialogs except current.
+ * Dismiss all open notifications
+ * Dismiss all open notifications except current.
+
+* **v0.6.1** [22 Sep. 2014]
+ * Fixes Null reference error when including alertify script before body element.
+
+* **v0.6.0** [21 Sep. 2014]
+ * Prefix the names of all animations with `ajs-` (Prevents collision with other frameworks)
+ * Listen to `animationend` event instead of `transitionend`.
+ * Isolate transition fallback timers (per instance).
+
+* **v0.5.0** [20 Sep. 2014]
+ * Notifier API now returns notification object.
+ * Add `get`/`set` aliases for `.setting` API.
+ * New global option (`alertify.defaults.maintainFocus`:`true`), controls whether to maintain active element focus or not.
+
+* **v0.4.0** [07 Sep. 2014]
+ * Add touch devices support.
+
+* **v0.3.1** [03 Sep. 2014]
+ * Fixes bug where transition could accidentally hide the dialog on show.
+
+* **v0.3.0** [03 Sep. 2014]
+ * Fix dialog is invisible in Desktop Safari.
+ * Enable binding to Function Keys.
+
+* **v0.2.0** [25 Aug. 2014]
+ * disable move when there is an active resize (possible when mouse up is triggered outside browser window).
+ * clear movable/resizable on close.
+ * docs enhancements.
+
+* **v0.1.0** [12 Aug. 2014]
+ * Add custom `onfocus` callback.
+ * Fix content padding.
+
+* **v0.0.0** [1 Aug. 2014]
+ * Initial commit.
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/package.json b/chatto/src/main/javascript/node_modules/alertifyjs/package.json
new file mode 100644
index 0000000..31d8307
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/package.json
@@ -0,0 +1,56 @@
+{
+ "name": "alertifyjs",
+ "version": "1.12.0",
+ "description": "AlertifyJS is a javascript framework for developing pretty browser dialogs and notifications.",
+ "homepage": "http://alertifyjs.com",
+ "keywords": [
+ "alertifyjs",
+ "alert",
+ "confirm",
+ "prompt",
+ "dialog",
+ "alertify",
+ "javascript framework",
+ "pretty dialogs"
+ ],
+ "author": "Mohammad Younes (http://alertifyjs.com)",
+ "bugs": {
+ "url": "https://github.com/MohammadYounes/AlertifyJS/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git@github.com:MohammadYounes/AlertifyJS.git"
+ },
+ "main": "./build/alertify.js",
+ "devDependencies": {
+ "grunt": "~0.4.1",
+ "grunt-cli": "~0.1",
+ "grunt-rtlcss": "1.6.0",
+ "autoprefixer" : "x.x.x",
+ "grunt-postcss" : "~0.8.0",
+ "grunt-contrib-less": "x.x.x",
+ "grunt-contrib-cssmin": "x.x.x",
+ "grunt-contrib-copy": "x.x.x",
+ "grunt-contrib-jshint": "~0.6.4",
+ "grunt-contrib-uglify": "~0.9.1",
+ "grunt-contrib-clean": "~0.5.0",
+ "grunt-contrib-concat": "~0.3.0",
+ "grunt-contrib-watch": "~0.5.3",
+ "grunt-contrib-connect": "~0.5.0",
+ "grunt-contrib-compress": "x.x.x",
+ "grunt-banner": "~0.3.1",
+ "docpad": "~6.69.0",
+ "docpad-plugin-eco": "~2.1.0",
+ "docpad-plugin-marked": "~2.2.1",
+ "docpad-plugin-partials": "~2.9.1",
+ "docpad-plugin-highlightjs": "~2.2.2",
+ "docpad-plugin-ghpages": "~2.4.3",
+ "docpad-plugin-livereload": "~2.6.0"
+ },
+ "licenses": [
+ {
+ "type": "GPL 3",
+ "url": "https://opensource.org/licenses/gpl-3.0"
+ }
+ ]
+}
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/js/alert.js b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/alert.js
new file mode 100644
index 0000000..5288e55
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/alert.js
@@ -0,0 +1,93 @@
+ /**
+ * Alert dialog definition
+ *
+ * invoked by:
+ * alertify.alert(message);
+ * alertify.alert(title, message);
+ * alertify.alert(message, onok);
+ * alertify.alert(title, message, onok);
+ */
+ alertify.dialog('alert', function () {
+ return {
+ main: function (_title, _message, _onok) {
+ var title, message, onok;
+ switch (arguments.length) {
+ case 1:
+ message = _title;
+ break;
+ case 2:
+ if (typeof _message === 'function') {
+ message = _title;
+ onok = _message;
+ } else {
+ title = _title;
+ message = _message;
+ }
+ break;
+ case 3:
+ title = _title;
+ message = _message;
+ onok = _onok;
+ break;
+ }
+ this.set('title', title);
+ this.set('message', message);
+ this.set('onok', onok);
+ return this;
+ },
+ setup: function () {
+ return {
+ buttons: [
+ {
+ text: alertify.defaults.glossary.ok,
+ key: keys.ESC,
+ invokeOnClose: true,
+ className: alertify.defaults.theme.ok,
+ }
+ ],
+ focus: {
+ element: 0,
+ select: false
+ },
+ options: {
+ maximizable: false,
+ resizable: false
+ }
+ };
+ },
+ build: function () {
+ // nothing
+ },
+ prepare: function () {
+ //nothing
+ },
+ setMessage: function (message) {
+ this.setContent(message);
+ },
+ settings: {
+ message: undefined,
+ onok: undefined,
+ label: undefined,
+ },
+ settingUpdated: function (key, oldValue, newValue) {
+ switch (key) {
+ case 'message':
+ this.setMessage(newValue);
+ break;
+ case 'label':
+ if (this.__internal.buttons[0].element) {
+ this.__internal.buttons[0].element.innerHTML = newValue;
+ }
+ break;
+ }
+ },
+ callback: function (closeEvent) {
+ if (typeof this.get('onok') === 'function') {
+ var returnValue = this.get('onok').call(this, closeEvent);
+ if (typeof returnValue !== 'undefined') {
+ closeEvent.cancel = !returnValue;
+ }
+ }
+ }
+ };
+ });
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/js/alertify.js b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/alertify.js
new file mode 100644
index 0000000..c1a838b
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/alertify.js
@@ -0,0 +1,264 @@
+ /**
+ * Alertify public API
+ * This contains everything that is exposed through the alertify object.
+ *
+ * @return {Object}
+ */
+ function Alertify() {
+
+ // holds a references of created dialogs
+ var dialogs = {};
+
+ /**
+ * Extends a given prototype by merging properties from base into sub.
+ *
+ * @sub {Object} sub The prototype being overwritten.
+ * @base {Object} base The prototype being written.
+ *
+ * @return {Object} The extended prototype.
+ */
+ function extend(sub, base) {
+ // copy dialog pototype over definition.
+ for (var prop in base) {
+ if (base.hasOwnProperty(prop)) {
+ sub[prop] = base[prop];
+ }
+ }
+ return sub;
+ }
+
+
+ /**
+ * Helper: returns a dialog instance from saved dialogs.
+ * and initializes the dialog if its not already initialized.
+ *
+ * @name {String} name The dialog name.
+ *
+ * @return {Object} The dialog instance.
+ */
+ function get_dialog(name) {
+ var dialog = dialogs[name].dialog;
+ //initialize the dialog if its not already initialized.
+ if (dialog && typeof dialog.__init === 'function') {
+ dialog.__init(dialog);
+ }
+ return dialog;
+ }
+
+ /**
+ * Helper: registers a new dialog definition.
+ *
+ * @name {String} name The dialog name.
+ * @Factory {Function} Factory a function resposible for creating dialog prototype.
+ * @transient {Boolean} transient True to create a new dialog instance each time the dialog is invoked, false otherwise.
+ * @base {String} base the name of another dialog to inherit from.
+ *
+ * @return {Object} The dialog definition.
+ */
+ function register(name, Factory, transient, base) {
+ var definition = {
+ dialog: null,
+ factory: Factory
+ };
+
+ //if this is based on an existing dialog, create a new definition
+ //by applying the new protoype over the existing one.
+ if (base !== undefined) {
+ definition.factory = function () {
+ return extend(new dialogs[base].factory(), new Factory());
+ };
+ }
+
+ if (!transient) {
+ //create a new definition based on dialog
+ definition.dialog = extend(new definition.factory(), dialog);
+ }
+ return dialogs[name] = definition;
+ }
+
+ return {
+ /**
+ * Alertify defaults
+ *
+ * @type {Object}
+ */
+ defaults: defaults,
+ /**
+ * Dialogs factory
+ *
+ * @param {string} Dialog name.
+ * @param {Function} A Dialog factory function.
+ * @param {Boolean} Indicates whether to create a singleton or transient dialog.
+ * @param {String} The name of the base type to inherit from.
+ */
+ dialog: function (name, Factory, transient, base) {
+
+ // get request, create a new instance and return it.
+ if (typeof Factory !== 'function') {
+ return get_dialog(name);
+ }
+
+ if (this.hasOwnProperty(name)) {
+ throw new Error('alertify.dialog: name already exists');
+ }
+
+ // register the dialog
+ var definition = register(name, Factory, transient, base);
+
+ if (transient) {
+
+ // make it public
+ this[name] = function () {
+ //if passed with no params, consider it a get request
+ if (arguments.length === 0) {
+ return definition.dialog;
+ } else {
+ var instance = extend(new definition.factory(), dialog);
+ //ensure init
+ if (instance && typeof instance.__init === 'function') {
+ instance.__init(instance);
+ }
+ instance['main'].apply(instance, arguments);
+ return instance['show'].apply(instance);
+ }
+ };
+ } else {
+ // make it public
+ this[name] = function () {
+ //ensure init
+ if (definition.dialog && typeof definition.dialog.__init === 'function') {
+ definition.dialog.__init(definition.dialog);
+ }
+ //if passed with no params, consider it a get request
+ if (arguments.length === 0) {
+ return definition.dialog;
+ } else {
+ var dialog = definition.dialog;
+ dialog['main'].apply(definition.dialog, arguments);
+ return dialog['show'].apply(definition.dialog);
+ }
+ };
+ }
+ },
+ /**
+ * Close all open dialogs.
+ *
+ * @param {Object} excpet [optional] The dialog object to exclude from closing.
+ *
+ * @return {undefined}
+ */
+ closeAll: function (except) {
+ var clone = openDialogs.slice(0);
+ for (var x = 0; x < clone.length; x += 1) {
+ var instance = clone[x];
+ if (except === undefined || except !== instance) {
+ instance.close();
+ }
+ }
+ },
+ /**
+ * Gets or Sets dialog settings/options. if the dialog is transient, this call does nothing.
+ *
+ * @param {string} name The dialog name.
+ * @param {String|Object} key A string specifying a propery name or a collection of key/value pairs.
+ * @param {Variant} value Optional, the value associated with the key (in case it was a string).
+ *
+ * @return {undefined}
+ */
+ setting: function (name, key, value) {
+
+ if (name === 'notifier') {
+ return notifier.setting(key, value);
+ }
+
+ var dialog = get_dialog(name);
+ if (dialog) {
+ return dialog.setting(key, value);
+ }
+ },
+ /**
+ * [Alias] Sets dialog settings/options
+ */
+ set: function(name,key,value){
+ return this.setting(name, key,value);
+ },
+ /**
+ * [Alias] Gets dialog settings/options
+ */
+ get: function(name, key){
+ return this.setting(name, key);
+ },
+ /**
+ * Creates a new notification message.
+ * If a type is passed, a class name "ajs-{type}" will be added.
+ * This allows for custom look and feel for various types of notifications.
+ *
+ * @param {String | DOMElement} [message=undefined] Message text
+ * @param {String} [type=''] Type of log message
+ * @param {String} [wait=''] Time (in seconds) to wait before auto-close
+ * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
+ *
+ * @return {Object} Notification object.
+ */
+ notify: function (message, type, wait, callback) {
+ return notifier.create(type, callback).push(message, wait);
+ },
+ /**
+ * Creates a new notification message.
+ *
+ * @param {String} [message=undefined] Message text
+ * @param {String} [wait=''] Time (in seconds) to wait before auto-close
+ * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
+ *
+ * @return {Object} Notification object.
+ */
+ message: function (message, wait, callback) {
+ return notifier.create(null, callback).push(message, wait);
+ },
+ /**
+ * Creates a new notification message of type 'success'.
+ *
+ * @param {String} [message=undefined] Message text
+ * @param {String} [wait=''] Time (in seconds) to wait before auto-close
+ * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
+ *
+ * @return {Object} Notification object.
+ */
+ success: function (message, wait, callback) {
+ return notifier.create('success', callback).push(message, wait);
+ },
+ /**
+ * Creates a new notification message of type 'error'.
+ *
+ * @param {String} [message=undefined] Message text
+ * @param {String} [wait=''] Time (in seconds) to wait before auto-close
+ * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
+ *
+ * @return {Object} Notification object.
+ */
+ error: function (message, wait, callback) {
+ return notifier.create('error', callback).push(message, wait);
+ },
+ /**
+ * Creates a new notification message of type 'warning'.
+ *
+ * @param {String} [message=undefined] Message text
+ * @param {String} [wait=''] Time (in seconds) to wait before auto-close
+ * @param {Function} [callback=undefined] A callback function to be invoked when the log is closed.
+ *
+ * @return {Object} Notification object.
+ */
+ warning: function (message, wait, callback) {
+ return notifier.create('warning', callback).push(message, wait);
+ },
+ /**
+ * Dismisses all open notifications
+ *
+ * @return {undefined}
+ */
+ dismissAll: function () {
+ notifier.dismissAll();
+ }
+ };
+ }
+ var alertify = new Alertify();
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/js/confirm.js b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/confirm.js
new file mode 100644
index 0000000..ec1149e
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/confirm.js
@@ -0,0 +1,186 @@
+ /**
+ * Confirm dialog object
+ *
+ * alertify.confirm(message);
+ * alertify.confirm(message, onok);
+ * alertify.confirm(message, onok, oncancel);
+ * alertify.confirm(title, message, onok, oncancel);
+ */
+ alertify.dialog('confirm', function () {
+
+ var autoConfirm = {
+ timer: null,
+ index: null,
+ text: null,
+ duration: null,
+ task: function (event, self) {
+ if (self.isOpen()) {
+ self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text + ' (' + autoConfirm.duration + ') ';
+ autoConfirm.duration -= 1;
+ if (autoConfirm.duration === -1) {
+ clearAutoConfirm(self);
+ var button = self.__internal.buttons[autoConfirm.index];
+ var closeEvent = createCloseEvent(autoConfirm.index, button);
+
+ if (typeof self.callback === 'function') {
+ self.callback.apply(self, [closeEvent]);
+ }
+ //close the dialog.
+ if (closeEvent.close !== false) {
+ self.close();
+ }
+ }
+ } else {
+ clearAutoConfirm(self);
+ }
+ }
+ };
+
+ function clearAutoConfirm(self) {
+ if (autoConfirm.timer !== null) {
+ clearInterval(autoConfirm.timer);
+ autoConfirm.timer = null;
+ self.__internal.buttons[autoConfirm.index].element.innerHTML = autoConfirm.text;
+ }
+ }
+
+ function startAutoConfirm(self, index, duration) {
+ clearAutoConfirm(self);
+ autoConfirm.duration = duration;
+ autoConfirm.index = index;
+ autoConfirm.text = self.__internal.buttons[index].element.innerHTML;
+ autoConfirm.timer = setInterval(delegate(self, autoConfirm.task), 1000);
+ autoConfirm.task(null, self);
+ }
+
+
+ return {
+ main: function (_title, _message, _onok, _oncancel) {
+ var title, message, onok, oncancel;
+ switch (arguments.length) {
+ case 1:
+ message = _title;
+ break;
+ case 2:
+ message = _title;
+ onok = _message;
+ break;
+ case 3:
+ message = _title;
+ onok = _message;
+ oncancel = _onok;
+ break;
+ case 4:
+ title = _title;
+ message = _message;
+ onok = _onok;
+ oncancel = _oncancel;
+ break;
+ }
+ this.set('title', title);
+ this.set('message', message);
+ this.set('onok', onok);
+ this.set('oncancel', oncancel);
+ return this;
+ },
+ setup: function () {
+ return {
+ buttons: [
+ {
+ text: alertify.defaults.glossary.ok,
+ key: keys.ENTER,
+ className: alertify.defaults.theme.ok,
+ },
+ {
+ text: alertify.defaults.glossary.cancel,
+ key: keys.ESC,
+ invokeOnClose: true,
+ className: alertify.defaults.theme.cancel,
+ }
+ ],
+ focus: {
+ element: 0,
+ select: false
+ },
+ options: {
+ maximizable: false,
+ resizable: false
+ }
+ };
+ },
+ build: function () {
+ //nothing
+ },
+ prepare: function () {
+ //nothing
+ },
+ setMessage: function (message) {
+ this.setContent(message);
+ },
+ settings: {
+ message: null,
+ labels: null,
+ onok: null,
+ oncancel: null,
+ defaultFocus: null,
+ reverseButtons: null,
+ },
+ settingUpdated: function (key, oldValue, newValue) {
+ switch (key) {
+ case 'message':
+ this.setMessage(newValue);
+ break;
+ case 'labels':
+ if ('ok' in newValue && this.__internal.buttons[0].element) {
+ this.__internal.buttons[0].text = newValue.ok;
+ this.__internal.buttons[0].element.innerHTML = newValue.ok;
+ }
+ if ('cancel' in newValue && this.__internal.buttons[1].element) {
+ this.__internal.buttons[1].text = newValue.cancel;
+ this.__internal.buttons[1].element.innerHTML = newValue.cancel;
+ }
+ break;
+ case 'reverseButtons':
+ if (newValue === true) {
+ this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element);
+ } else {
+ this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element);
+ }
+ break;
+ case 'defaultFocus':
+ this.__internal.focus.element = newValue === 'ok' ? 0 : 1;
+ break;
+ }
+ },
+ callback: function (closeEvent) {
+ clearAutoConfirm(this);
+ var returnValue;
+ switch (closeEvent.index) {
+ case 0:
+ if (typeof this.get('onok') === 'function') {
+ returnValue = this.get('onok').call(this, closeEvent);
+ if (typeof returnValue !== 'undefined') {
+ closeEvent.cancel = !returnValue;
+ }
+ }
+ break;
+ case 1:
+ if (typeof this.get('oncancel') === 'function') {
+ returnValue = this.get('oncancel').call(this, closeEvent);
+ if (typeof returnValue !== 'undefined') {
+ closeEvent.cancel = !returnValue;
+ }
+ }
+ break;
+ }
+ },
+ autoOk: function (duration) {
+ startAutoConfirm(this, 0, duration);
+ return this;
+ },
+ autoCancel: function (duration) {
+ startAutoConfirm(this, 1, duration);
+ return this;
+ }
+ };
+ });
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/js/dialog/actions.js b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/dialog/actions.js
new file mode 100644
index 0000000..2fef89b
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/dialog/actions.js
@@ -0,0 +1,109 @@
+ // stores last call timestamp to prevent triggering the callback twice.
+ var callbackTS = 0;
+ // flag to cancel keyup event if already handled by click event (pressing Enter on a focusted button).
+ var cancelKeyup = false;
+ /**
+ * Helper: triggers a button callback
+ *
+ * @param {Object} The dilog instance.
+ * @param {Function} Callback to check which button triggered the event.
+ *
+ * @return {undefined}
+ */
+ function triggerCallback(instance, check) {
+ if(Date.now() - callbackTS > 200 && (callbackTS = Date.now())){
+ for (var idx = 0; idx < instance.__internal.buttons.length; idx += 1) {
+ var button = instance.__internal.buttons[idx];
+ if (!button.element.disabled && check(button)) {
+ var closeEvent = createCloseEvent(idx, button);
+ if (typeof instance.callback === 'function') {
+ instance.callback.apply(instance, [closeEvent]);
+ }
+ //close the dialog only if not canceled.
+ if (closeEvent.cancel === false) {
+ instance.close();
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Clicks event handler, attached to the dialog footer.
+ *
+ * @param {Event} DOM event object.
+ * @param {Object} The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function buttonsClickHandler(event, instance) {
+ var target = event.srcElement || event.target;
+ triggerCallback(instance, function (button) {
+ // if this button caused the click, cancel keyup event
+ return button.element === target && (cancelKeyup = true);
+ });
+ }
+
+ /**
+ * Keyup event handler, attached to the document.body
+ *
+ * @param {Event} DOM event object.
+ * @param {Object} The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function keyupHandler(event) {
+ //hitting enter while button has focus will trigger keyup too.
+ //ignore if handled by clickHandler
+ if (cancelKeyup) {
+ cancelKeyup = false;
+ return;
+ }
+ var instance = openDialogs[openDialogs.length - 1];
+ var keyCode = event.keyCode;
+ if (instance.__internal.buttons.length === 0 && keyCode === keys.ESC && instance.get('closable') === true) {
+ triggerClose(instance);
+ return false;
+ }else if (usedKeys.indexOf(keyCode) > -1) {
+ triggerCallback(instance, function (button) {
+ return button.key === keyCode;
+ });
+ return false;
+ }
+ }
+ /**
+ * Keydown event handler, attached to the document.body
+ *
+ * @param {Event} DOM event object.
+ * @param {Object} The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function keydownHandler(event) {
+ var instance = openDialogs[openDialogs.length - 1];
+ var keyCode = event.keyCode;
+ if (keyCode === keys.LEFT || keyCode === keys.RIGHT) {
+ var buttons = instance.__internal.buttons;
+ for (var x = 0; x < buttons.length; x += 1) {
+ if (document.activeElement === buttons[x].element) {
+ switch (keyCode) {
+ case keys.LEFT:
+ buttons[(x || buttons.length) - 1].element.focus();
+ return;
+ case keys.RIGHT:
+ buttons[(x + 1) % buttons.length].element.focus();
+ return;
+ }
+ }
+ }
+ }else if (keyCode < keys.F12 + 1 && keyCode > keys.F1 - 1 && usedKeys.indexOf(keyCode) > -1) {
+ event.preventDefault();
+ event.stopPropagation();
+ triggerCallback(instance, function (button) {
+ return button.key === keyCode;
+ });
+ return false;
+ }
+ }
+
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/js/dialog/commands.js b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/dialog/commands.js
new file mode 100644
index 0000000..ea1861c
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/dialog/commands.js
@@ -0,0 +1,274 @@
+
+ /**
+ * Triggers a close event.
+ *
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function triggerClose(instance) {
+ var found;
+ triggerCallback(instance, function (button) {
+ return found = instance.get('invokeOnCloseOff') !== true && (button.invokeOnClose === true);
+ });
+ //none of the buttons registered as onclose callback
+ //close the dialog
+ if (!found && instance.isOpen()) {
+ instance.close();
+ }
+ }
+
+ /**
+ * Dialogs commands event handler, attached to the dialog commands element.
+ *
+ * @param {Event} event DOM event object.
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function commandsClickHandler(event, instance) {
+ var target = event.srcElement || event.target;
+ switch (target) {
+ case instance.elements.commands.pin:
+ if (!instance.isPinned()) {
+ pin(instance);
+ } else {
+ unpin(instance);
+ }
+ break;
+ case instance.elements.commands.maximize:
+ if (!instance.isMaximized()) {
+ maximize(instance);
+ } else {
+ restore(instance);
+ }
+ break;
+ case instance.elements.commands.close:
+ triggerClose(instance);
+ break;
+ }
+ return false;
+ }
+
+ /**
+ * Helper: pins the modeless dialog.
+ *
+ * @param {Object} instance The dialog instance.
+ *
+ * @return {undefined}
+ */
+ function pin(instance) {
+ //pin the dialog
+ instance.set('pinned', true);
+ }
+
+ /**
+ * Helper: unpins the modeless dialog.
+ *
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function unpin(instance) {
+ //unpin the dialog
+ instance.set('pinned', false);
+ }
+
+
+ /**
+ * Helper: enlarges the dialog to fill the entire screen.
+ *
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function maximize(instance) {
+ // allow custom `onmaximize` method
+ dispatchEvent('onmaximize', instance);
+ //maximize the dialog
+ addClass(instance.elements.root, classes.maximized);
+ if (instance.isOpen()) {
+ ensureNoOverflow();
+ }
+ // allow custom `onmaximized` method
+ dispatchEvent('onmaximized', instance);
+ }
+
+ /**
+ * Helper: returns the dialog to its former size.
+ *
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function restore(instance) {
+ // allow custom `onrestore` method
+ dispatchEvent('onrestore', instance);
+ //maximize the dialog
+ removeClass(instance.elements.root, classes.maximized);
+ if (instance.isOpen()) {
+ ensureNoOverflow();
+ }
+ // allow custom `onrestored` method
+ dispatchEvent('onrestored', instance);
+ }
+
+ /**
+ * Show or hide the maximize box.
+ *
+ * @param {Object} instance The dilog instance.
+ * @param {Boolean} on True to add the behavior, removes it otherwise.
+ *
+ * @return {undefined}
+ */
+ function updatePinnable(instance) {
+ if (instance.get('pinnable')) {
+ // add class
+ addClass(instance.elements.root, classes.pinnable);
+ } else {
+ // remove class
+ removeClass(instance.elements.root, classes.pinnable);
+ }
+ }
+
+ /**
+ * Helper: Fixes the absolutly positioned modal div position.
+ *
+ * @param {Object} instance The dialog instance.
+ *
+ * @return {undefined}
+ */
+ function addAbsPositionFix(instance) {
+ var scrollLeft = getScrollLeft();
+ instance.elements.modal.style.marginTop = getScrollTop() + 'px';
+ instance.elements.modal.style.marginLeft = scrollLeft + 'px';
+ instance.elements.modal.style.marginRight = (-scrollLeft) + 'px';
+ }
+
+ /**
+ * Helper: Removes the absolutly positioned modal div position fix.
+ *
+ * @param {Object} instance The dialog instance.
+ *
+ * @return {undefined}
+ */
+ function removeAbsPositionFix(instance) {
+ var marginTop = parseInt(instance.elements.modal.style.marginTop, 10);
+ var marginLeft = parseInt(instance.elements.modal.style.marginLeft, 10);
+ instance.elements.modal.style.marginTop = '';
+ instance.elements.modal.style.marginLeft = '';
+ instance.elements.modal.style.marginRight = '';
+
+ if (instance.isOpen()) {
+ var top = 0,
+ left = 0
+ ;
+ if (instance.elements.dialog.style.top !== '') {
+ top = parseInt(instance.elements.dialog.style.top, 10);
+ }
+ instance.elements.dialog.style.top = (top + (marginTop - getScrollTop())) + 'px';
+
+ if (instance.elements.dialog.style.left !== '') {
+ left = parseInt(instance.elements.dialog.style.left, 10);
+ }
+ instance.elements.dialog.style.left = (left + (marginLeft - getScrollLeft())) + 'px';
+ }
+ }
+ /**
+ * Helper: Adds/Removes the absolutly positioned modal div position fix based on its pinned setting.
+ *
+ * @param {Object} instance The dialog instance.
+ *
+ * @return {undefined}
+ */
+ function updateAbsPositionFix(instance) {
+ // if modeless and unpinned add fix
+ if (!instance.get('modal') && !instance.get('pinned')) {
+ addAbsPositionFix(instance);
+ } else {
+ removeAbsPositionFix(instance);
+ }
+ }
+ /**
+ * Toggles the dialog position lock | modeless only.
+ *
+ * @param {Object} instance The dilog instance.
+ * @param {Boolean} on True to make it modal, false otherwise.
+ *
+ * @return {undefined}
+ */
+ function updatePinned(instance) {
+ if (instance.get('pinned')) {
+ removeClass(instance.elements.root, classes.unpinned);
+ if (instance.isOpen()) {
+ removeAbsPositionFix(instance);
+ }
+ } else {
+ addClass(instance.elements.root, classes.unpinned);
+ if (instance.isOpen() && !instance.isModal()) {
+ addAbsPositionFix(instance);
+ }
+ }
+ }
+
+ /**
+ * Show or hide the maximize box.
+ *
+ * @param {Object} instance The dilog instance.
+ * @param {Boolean} on True to add the behavior, removes it otherwise.
+ *
+ * @return {undefined}
+ */
+ function updateMaximizable(instance) {
+ if (instance.get('maximizable')) {
+ // add class
+ addClass(instance.elements.root, classes.maximizable);
+ } else {
+ // remove class
+ removeClass(instance.elements.root, classes.maximizable);
+ }
+ }
+
+ /**
+ * Show or hide the close box.
+ *
+ * @param {Object} instance The dilog instance.
+ * @param {Boolean} on True to add the behavior, removes it otherwise.
+ *
+ * @return {undefined}
+ */
+ function updateClosable(instance) {
+ if (instance.get('closable')) {
+ // add class
+ addClass(instance.elements.root, classes.closable);
+ bindClosableEvents(instance);
+ } else {
+ // remove class
+ removeClass(instance.elements.root, classes.closable);
+ unbindClosableEvents(instance);
+ }
+ }
+
+
+ var cancelClick = false,// flag to cancel click event if already handled by end resize event (the mousedown, mousemove, mouseup sequence fires a click event.).
+ modalClickHandlerTS=0 // stores last click timestamp to prevent executing the handler twice on double click.
+ ;
+
+ /**
+ * Helper: closes the modal dialog when clicking the modal
+ *
+ * @param {Event} event DOM event object.
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function modalClickHandler(event, instance) {
+ if(event.timeStamp - modalClickHandlerTS > 200 && (modalClickHandlerTS = event.timeStamp) && !cancelClick){
+ var target = event.srcElement || event.target;
+ if (instance.get('closableByDimmer') === true && target === instance.elements.modal) {
+ triggerClose(instance);
+ }
+ cancelClick = false;
+ return false;
+ }
+ }
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/js/dialog/dialog.js b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/dialog/dialog.js
new file mode 100644
index 0000000..909d4ec
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/dialog/dialog.js
@@ -0,0 +1,405 @@
+ // dialog API
+ return {
+ __init:initialize,
+ /**
+ * Check if dialog is currently open
+ *
+ * @return {Boolean}
+ */
+ isOpen: function () {
+ return this.__internal.isOpen;
+ },
+ isModal: function (){
+ return this.elements.root.className.indexOf(classes.modeless) < 0;
+ },
+ isMaximized:function(){
+ return this.elements.root.className.indexOf(classes.maximized) > -1;
+ },
+ isPinned:function(){
+ return this.elements.root.className.indexOf(classes.unpinned) < 0;
+ },
+ maximize:function(){
+ if(!this.isMaximized()){
+ maximize(this);
+ }
+ return this;
+ },
+ restore:function(){
+ if(this.isMaximized()){
+ restore(this);
+ }
+ return this;
+ },
+ pin:function(){
+ if(!this.isPinned()){
+ pin(this);
+ }
+ return this;
+ },
+ unpin:function(){
+ if(this.isPinned()){
+ unpin(this);
+ }
+ return this;
+ },
+ bringToFront:function(){
+ bringToFront(null, this);
+ return this;
+ },
+ /**
+ * Move the dialog to a specific x/y coordinates
+ *
+ * @param {Number} x The new dialog x coordinate in pixels.
+ * @param {Number} y The new dialog y coordinate in pixels.
+ *
+ * @return {Object} The dialog instance.
+ */
+ moveTo:function(x,y){
+ if(!isNaN(x) && !isNaN(y)){
+ // allow custom `onmove` method
+ dispatchEvent('onmove', this);
+
+ var element = this.elements.dialog,
+ current = element,
+ offsetLeft = 0,
+ offsetTop = 0;
+
+ //subtract existing left,top
+ if (element.style.left) {
+ offsetLeft -= parseInt(element.style.left, 10);
+ }
+ if (element.style.top) {
+ offsetTop -= parseInt(element.style.top, 10);
+ }
+ //calc offset
+ do {
+ offsetLeft += current.offsetLeft;
+ offsetTop += current.offsetTop;
+ } while (current = current.offsetParent);
+
+ //calc left, top
+ var left = (x - offsetLeft);
+ var top = (y - offsetTop);
+
+ //// rtl handling
+ if (isRightToLeft()) {
+ left *= -1;
+ }
+
+ element.style.left = left + 'px';
+ element.style.top = top + 'px';
+
+ // allow custom `onmoved` method
+ dispatchEvent('onmoved', this);
+ }
+ return this;
+ },
+ /**
+ * Resize the dialog to a specific width/height (the dialog must be 'resizable').
+ * The dialog can be resized to:
+ * A minimum width equal to the initial display width
+ * A minimum height equal to the sum of header/footer heights.
+ *
+ *
+ * @param {Number or String} width The new dialog width in pixels or in percent.
+ * @param {Number or String} height The new dialog height in pixels or in percent.
+ *
+ * @return {Object} The dialog instance.
+ */
+ resizeTo:function(width,height){
+ var w = parseFloat(width),
+ h = parseFloat(height),
+ regex = /(\d*\.\d+|\d+)%/
+ ;
+
+ if(!isNaN(w) && !isNaN(h) && this.get('resizable') === true){
+
+ // allow custom `onresize` method
+ dispatchEvent('onresize', this);
+
+ if(('' + width).match(regex)){
+ w = w / 100 * document.documentElement.clientWidth ;
+ }
+
+ if(('' + height).match(regex)){
+ h = h / 100 * document.documentElement.clientHeight;
+ }
+
+ var element = this.elements.dialog;
+ if (element.style.maxWidth !== 'none') {
+ element.style.minWidth = (minWidth = element.offsetWidth) + 'px';
+ }
+ element.style.maxWidth = 'none';
+ element.style.minHeight = this.elements.header.offsetHeight + this.elements.footer.offsetHeight + 'px';
+ element.style.width = w + 'px';
+ element.style.height = h + 'px';
+
+ // allow custom `onresized` method
+ dispatchEvent('onresized', this);
+ }
+ return this;
+ },
+ /**
+ * Gets or Sets dialog settings/options
+ *
+ * @param {String|Object} key A string specifying a propery name or a collection of key/value pairs.
+ * @param {Object} value Optional, the value associated with the key (in case it was a string).
+ *
+ * @return {undefined}
+ */
+ setting : function (key, value) {
+ var self = this;
+ var result = update(this, this.__internal.options, function(k,o,n){ optionUpdated(self,k,o,n); }, key, value);
+ if(result.op === 'get'){
+ if(result.found){
+ return result.value;
+ }else if(typeof this.settings !== 'undefined'){
+ return update(this, this.settings, this.settingUpdated || function(){}, key, value).value;
+ }else{
+ return undefined;
+ }
+ }else if(result.op === 'set'){
+ if(result.items.length > 0){
+ var callback = this.settingUpdated || function(){};
+ for(var x=0;x focus.element) {
+ //in basic view, skip focusing the buttons.
+ if (instance.get('basic') === true) {
+ element = instance.elements.reset[0];
+ } else {
+ element = instance.__internal.buttons[focus.element].element;
+ }
+ }
+ break;
+ // a string means querySelector to select from dialog body contents.
+ case 'string':
+ element = instance.elements.body.querySelector(focus.element);
+ break;
+ // a function should return the focus element.
+ case 'function':
+ element = focus.element.call(instance);
+ break;
+ }
+
+ // if no focus element, default to first reset element.
+ if (instance.get('defaultFocusOff') === true || ((typeof element === 'undefined' || element === null) && instance.__internal.buttons.length === 0)) {
+ element = instance.elements.reset[0];
+ }
+ // focus
+ if (element && element.focus) {
+ element.focus();
+ // if selectable
+ if (focus.select && element.select) {
+ element.select();
+ }
+ }
+ }
+ }
+
+ /**
+ * Focus event handler, attached to document.body and dialogs own reset links.
+ * handles the focus for modal dialogs only.
+ *
+ * @param {Event} event DOM focus event object.
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function onReset(event, instance) {
+
+ // should work on last modal if triggered from document.body
+ if (!instance) {
+ for (var x = openDialogs.length - 1; x > -1; x -= 1) {
+ if (openDialogs[x].isModal()) {
+ instance = openDialogs[x];
+ break;
+ }
+ }
+ }
+
+ if(instance) {
+ // if modal
+ if (instance.isModal()) {
+ // determine reset target to enable forward/backward tab cycle.
+ var firstReset = instance.elements.reset[0],
+ lastReset = instance.elements.reset[1],
+ lastFocusedElement = event.relatedTarget,
+ within = instance.elements.root.contains(lastFocusedElement),
+ target = event.srcElement || event.target,
+ resetTarget;
+
+ //if the previous focused element element was outside the modal do nthing
+ if( /*first show */
+ (target === firstReset && !within) ||
+ /*focus cycle */
+ (target === lastReset && lastFocusedElement == firstReset))
+ return
+ else if(target === lastReset || target === document.body)
+ resetTarget = firstReset
+ else if(target === firstReset && lastFocusedElement == lastReset){
+ resetTarget = findTabbable(instance)
+ }else if(target == firstReset && within){
+ resetTarget = findTabbable(instance, true)
+ }
+ // focus
+ setFocus(instance, resetTarget);
+ }
+ }
+ }
+ function findTabbable(instance, last){
+ var tabbables = [].slice.call(instance.elements.dialog.querySelectorAll(defaults.tabbable));
+ last && tabbables.reverse()
+ for(var x=0;x -1 && window.navigator.userAgent.indexOf('Chrome') < 0,
+ //dialog building blocks
+ templates = {
+ dimmer:'',
+ /*tab index required to fire click event before body focus*/
+ modal: '',
+ dialog: '',
+ reset: '',
+ commands: '',
+ header: '',
+ body: '',
+ content: '',
+ footer: '',
+ buttons: { primary: '', auxiliary: '' },
+ button: '',
+ resizeHandle: '',
+ },
+ //common class names
+ classes = {
+ animationIn: 'ajs-in',
+ animationOut: 'ajs-out',
+ base: 'alertify',
+ basic:'ajs-basic',
+ capture: 'ajs-capture',
+ closable:'ajs-closable',
+ fixed: 'ajs-fixed',
+ frameless:'ajs-frameless',
+ hidden: 'ajs-hidden',
+ maximize: 'ajs-maximize',
+ maximized: 'ajs-maximized',
+ maximizable:'ajs-maximizable',
+ modeless: 'ajs-modeless',
+ movable: 'ajs-movable',
+ noSelection: 'ajs-no-selection',
+ noOverflow: 'ajs-no-overflow',
+ noPadding:'ajs-no-padding',
+ pin:'ajs-pin',
+ pinnable:'ajs-pinnable',
+ prefix: 'ajs-',
+ resizable: 'ajs-resizable',
+ restore: 'ajs-restore',
+ shake:'ajs-shake',
+ unpinned:'ajs-unpinned',
+ };
+
+ /**
+ * Helper: initializes the dialog instance
+ *
+ * @return {Number} The total count of currently open modals.
+ */
+ function initialize(instance){
+
+ if(!instance.__internal){
+ //invoke preinit global hook
+ alertify.defaults.hooks.preinit(instance);
+ //no need to expose init after this.
+ delete instance.__init;
+
+ //keep a copy of initial dialog settings
+ if(!instance.__settings){
+ instance.__settings = copy(instance.settings);
+ }
+
+ //get dialog buttons/focus setup
+ var setup;
+ if(typeof instance.setup === 'function'){
+ setup = instance.setup();
+ setup.options = setup.options || {};
+ setup.focus = setup.focus || {};
+ }else{
+ setup = {
+ buttons:[],
+ focus:{
+ element:null,
+ select:false
+ },
+ options:{
+ }
+ };
+ }
+
+ //initialize hooks object.
+ if(typeof instance.hooks !== 'object'){
+ instance.hooks = {};
+ }
+
+ //copy buttons defintion
+ var buttonsDefinition = [];
+ if(Array.isArray(setup.buttons)){
+ for(var b=0;b= 0){
+ //last open modal or last maximized one
+ removeClass(document.body, classes.noOverflow);
+ preventBodyShift(false);
+ }else if(requiresNoOverflow > 0 && document.body.className.indexOf(classes.noOverflow) < 0){
+ //first open modal or first maximized one
+ preventBodyShift(true);
+ addClass(document.body, classes.noOverflow);
+ }
+ }
+ var top = '', topScroll = 0;
+ /**
+ * Helper: prevents body shift.
+ *
+ */
+ function preventBodyShift(add){
+ if(alertify.defaults.preventBodyShift){
+ if(add && document.documentElement.scrollHeight > document.documentElement.clientHeight ){//&& openDialogs[openDialogs.length-1].elements.dialog.clientHeight <= document.documentElement.clientHeight){
+ topScroll = scrollY;
+ top = window.getComputedStyle(document.body).top;
+ addClass(document.body, classes.fixed);
+ document.body.style.top = -scrollY + 'px';
+ } else if(!add) {
+ scrollY = topScroll;
+ document.body.style.top = top;
+ removeClass(document.body, classes.fixed);
+ restoreScrollPosition();
+ }
+ }
+ }
+
+ /**
+ * Sets the name of the transition used to show/hide the dialog
+ *
+ * @param {Object} instance The dilog instance.
+ *
+ */
+ function updateTransition(instance, value, oldValue){
+ if(typeof oldValue === 'string'){
+ removeClass(instance.elements.root,classes.prefix + oldValue);
+ }
+ addClass(instance.elements.root, classes.prefix + value);
+ reflow = instance.elements.root.offsetWidth;
+ }
+
+ /**
+ * Toggles the dialog display mode
+ *
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function updateDisplayMode(instance){
+ if(instance.get('modal')){
+
+ //make modal
+ removeClass(instance.elements.root, classes.modeless);
+
+ //only if open
+ if(instance.isOpen()){
+ unbindModelessEvents(instance);
+
+ //in case a pinned modless dialog was made modal while open.
+ updateAbsPositionFix(instance);
+
+ ensureNoOverflow();
+ }
+ }else{
+ //make modelss
+ addClass(instance.elements.root, classes.modeless);
+
+ //only if open
+ if(instance.isOpen()){
+ bindModelessEvents(instance);
+
+ //in case pin/unpin was called while a modal is open
+ updateAbsPositionFix(instance);
+
+ ensureNoOverflow();
+ }
+ }
+ }
+
+ /**
+ * Toggles the dialog basic view mode
+ *
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function updateBasicMode(instance){
+ if (instance.get('basic')) {
+ // add class
+ addClass(instance.elements.root, classes.basic);
+ } else {
+ // remove class
+ removeClass(instance.elements.root, classes.basic);
+ }
+ }
+
+ /**
+ * Toggles the dialog frameless view mode
+ *
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function updateFramelessMode(instance){
+ if (instance.get('frameless')) {
+ // add class
+ addClass(instance.elements.root, classes.frameless);
+ } else {
+ // remove class
+ removeClass(instance.elements.root, classes.frameless);
+ }
+ }
+
+ /**
+ * Helper: Brings the modeless dialog to front, attached to modeless dialogs.
+ *
+ * @param {Event} event Focus event
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function bringToFront(event, instance){
+
+ // Do not bring to front if preceeded by an open modal
+ var index = openDialogs.indexOf(instance);
+ for(var x=index+1;x startingWidth) {
+ //growing
+ element.style.left = (startingLeft + diff) + 'px';
+ } else if (element.offsetWidth >= minWidth) {
+ //shrinking
+ element.style.left = (startingLeft - diff) + 'px';
+ }
+ }
+ }
+
+ /**
+ * Triggers the start of a resize event, attached to the resize handle element mouse down event.
+ * Adds no-selection class to the body, disabling selection while moving.
+ *
+ * @param {Event} event DOM event object.
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {Boolean} false
+ */
+ function beginResize(event, instance) {
+ if (!instance.isMaximized()) {
+ var eventSrc;
+ if (event.type === 'touchstart') {
+ event.preventDefault();
+ eventSrc = event.targetTouches[0];
+ } else if (event.button === 0) {
+ eventSrc = event;
+ }
+ if (eventSrc) {
+ // allow custom `onresize` method
+ dispatchEvent('onresize', instance);
+
+ resizable = instance;
+ handleOffset = instance.elements.resizeHandle.offsetHeight / 2;
+ var element = instance.elements.dialog;
+ addClass(element, classes.capture);
+ startingLeft = parseInt(element.style.left, 10);
+ element.style.height = element.offsetHeight + 'px';
+ element.style.minHeight = instance.elements.header.offsetHeight + instance.elements.footer.offsetHeight + 'px';
+ element.style.width = (startingWidth = element.offsetWidth) + 'px';
+
+ if (element.style.maxWidth !== 'none') {
+ element.style.minWidth = (minWidth = element.offsetWidth) + 'px';
+ }
+ element.style.maxWidth = 'none';
+ addClass(document.body, classes.noSelection);
+ return false;
+ }
+ }
+ }
+
+ /**
+ * The actual resize handler, attached to document.body mousemove event.
+ *
+ * @param {Event} event DOM event object.
+ *
+ * @return {undefined}
+ */
+ function resize(event) {
+ if (resizable) {
+ var eventSrc;
+ if (event.type === 'touchmove') {
+ event.preventDefault();
+ eventSrc = event.targetTouches[0];
+ } else if (event.button === 0) {
+ eventSrc = event;
+ }
+ if (eventSrc) {
+ resizeElement(eventSrc, resizable.elements.dialog, !resizable.get('modal') && !resizable.get('pinned'));
+ }
+ }
+ }
+
+ /**
+ * Triggers the end of a resize event, attached to document.body mouseup event.
+ * Removes no-selection class from document.body, allowing selection.
+ *
+ * @return {undefined}
+ */
+ function endResize() {
+ if (resizable) {
+ var instance = resizable;
+ resizable = null;
+ removeClass(document.body, classes.noSelection);
+ removeClass(instance.elements.dialog, classes.capture);
+ cancelClick = true;
+ // allow custom `onresized` method
+ dispatchEvent('onresized', instance);
+ }
+ }
+
+ /**
+ * Resets any changes made by resizing the element to its original state.
+ *
+ * @param {Object} instance The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function resetResize(instance) {
+ resizable = null;
+ var element = instance.elements.dialog;
+ if (element.style.maxWidth === 'none') {
+ //clear inline styles.
+ element.style.maxWidth = element.style.minWidth = element.style.width = element.style.height = element.style.minHeight = element.style.left = '';
+ //reset variables.
+ startingLeft = Number.Nan;
+ startingWidth = minWidth = handleOffset = 0;
+ }
+ }
+
+
+ /**
+ * Updates the dialog move behavior.
+ *
+ * @param {Object} instance The dilog instance.
+ * @param {Boolean} on True to add the behavior, removes it otherwise.
+ *
+ * @return {undefined}
+ */
+ function updateResizable(instance) {
+ if (instance.get('resizable')) {
+ // add class
+ addClass(instance.elements.root, classes.resizable);
+ if (instance.isOpen()) {
+ bindResizableEvents(instance);
+ }
+ } else {
+ //reset
+ resetResize(instance);
+ // remove class
+ removeClass(instance.elements.root, classes.resizable);
+ if (instance.isOpen()) {
+ unbindResizableEvents(instance);
+ }
+ }
+ }
+
+ /**
+ * Reset move/resize on window resize.
+ *
+ * @param {Event} event window resize event object.
+ *
+ * @return {undefined}
+ */
+ function windowResize(/*event*/) {
+ for (var x = 0; x < openDialogs.length; x += 1) {
+ var instance = openDialogs[x];
+ if (instance.get('autoReset')) {
+ resetMove(instance);
+ resetResize(instance);
+ }
+ }
+ }
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/js/dialog/transition.js b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/dialog/transition.js
new file mode 100644
index 0000000..d7dc90b
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/dialog/transition.js
@@ -0,0 +1,65 @@
+ /**
+ * Transition in transitionend event handler.
+ *
+ * @param {Event} TransitionEnd event object.
+ * @param {Object} The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function handleTransitionInEvent(event, instance) {
+ // clear the timer
+ clearTimeout(instance.__internal.timerIn);
+
+ // once transition is complete, set focus
+ setFocus(instance);
+
+ //restore scroll to prevent document jump
+ restoreScrollPosition();
+
+ // allow handling key up after transition ended.
+ cancelKeyup = false;
+
+ // allow custom `onfocus` method
+ dispatchEvent('onfocus', instance);
+
+ // unbind the event
+ off(instance.elements.dialog, transition.type, instance.__internal.transitionInHandler);
+
+ removeClass(instance.elements.root, classes.animationIn);
+ }
+
+ /**
+ * Transition out transitionend event handler.
+ *
+ * @param {Event} TransitionEnd event object.
+ * @param {Object} The dilog instance.
+ *
+ * @return {undefined}
+ */
+ function handleTransitionOutEvent(event, instance) {
+ // clear the timer
+ clearTimeout(instance.__internal.timerOut);
+ // unbind the event
+ off(instance.elements.dialog, transition.type, instance.__internal.transitionOutHandler);
+
+ // reset move updates
+ resetMove(instance);
+ // reset resize updates
+ resetResize(instance);
+
+ // restore if maximized
+ if (instance.isMaximized() && !instance.get('startMaximized')) {
+ restore(instance);
+ }
+
+ // return focus to the last active element
+ if (alertify.defaults.maintainFocus && instance.__internal.activeElement) {
+ instance.__internal.activeElement.focus();
+ instance.__internal.activeElement = null;
+ }
+
+ //destory the instance
+ if (typeof instance.__internal.destroy === 'function') {
+ instance.__internal.destroy.apply(instance);
+ }
+ }
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/js/event.js b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/event.js
new file mode 100644
index 0000000..2cf6436
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/event.js
@@ -0,0 +1,131 @@
+ /**
+ * Use a closure to return proper event listener method. Try to use
+ * `addEventListener` by default but fallback to `attachEvent` for
+ * unsupported browser. The closure simply ensures that the test doesn't
+ * happen every time the method is called.
+ *
+ * @param {Node} el Node element
+ * @param {String} event Event type
+ * @param {Function} fn Callback of event
+ * @return {Function}
+ */
+ var on = (function () {
+ if (document.addEventListener) {
+ return function (el, event, fn, useCapture) {
+ el.addEventListener(event, fn, useCapture === true);
+ };
+ } else if (document.attachEvent) {
+ return function (el, event, fn) {
+ el.attachEvent('on' + event, fn);
+ };
+ }
+ }());
+
+ /**
+ * Use a closure to return proper event listener method. Try to use
+ * `removeEventListener` by default but fallback to `detachEvent` for
+ * unsupported browser. The closure simply ensures that the test doesn't
+ * happen every time the method is called.
+ *
+ * @param {Node} el Node element
+ * @param {String} event Event type
+ * @param {Function} fn Callback of event
+ * @return {Function}
+ */
+ var off = (function () {
+ if (document.removeEventListener) {
+ return function (el, event, fn, useCapture) {
+ el.removeEventListener(event, fn, useCapture === true);
+ };
+ } else if (document.detachEvent) {
+ return function (el, event, fn) {
+ el.detachEvent('on' + event, fn);
+ };
+ }
+ }());
+
+ /**
+ * Prevent default event from firing
+ *
+ * @param {Event} event Event object
+ * @return {undefined}
+
+ function prevent ( event ) {
+ if ( event ) {
+ if ( event.preventDefault ) {
+ event.preventDefault();
+ } else {
+ event.returnValue = false;
+ }
+ }
+ }
+ */
+ var transition = (function () {
+ var t, type;
+ var supported = false;
+ var transitions = {
+ 'animation' : 'animationend',
+ 'OAnimation' : 'oAnimationEnd oanimationend',
+ 'msAnimation' : 'MSAnimationEnd',
+ 'MozAnimation' : 'animationend',
+ 'WebkitAnimation' : 'webkitAnimationEnd'
+ };
+
+ for (t in transitions) {
+ if (document.documentElement.style[t] !== undefined) {
+ type = transitions[t];
+ supported = true;
+ break;
+ }
+ }
+
+ return {
+ type: type,
+ supported: supported
+ };
+ }());
+
+ /**
+ * Creates event handler delegate that sends the instance as last argument.
+ *
+ * @return {Function} a function wrapper which sends the instance as last argument.
+ */
+ function delegate(context, method) {
+ return function () {
+ if (arguments.length > 0) {
+ var args = [];
+ for (var x = 0; x < arguments.length; x += 1) {
+ args.push(arguments[x]);
+ }
+ args.push(context);
+ return method.apply(context, args);
+ }
+ return method.apply(context, [null, context]);
+ };
+ }
+ /**
+ * Helper for creating a dialog close event.
+ *
+ * @return {object}
+ */
+ function createCloseEvent(index, button) {
+ return {
+ index: index,
+ button: button,
+ cancel: false
+ };
+ }
+ /**
+ * Helper for dispatching events.
+ *
+ * @param {string} evenType The type of the event to disptach.
+ * @param {object} instance The dialog instance disptaching the event.
+ *
+ * @return {any} The result of the invoked function.
+ */
+ function dispatchEvent(eventType, instance) {
+ if ( typeof instance.get(eventType) === 'function' ) {
+ return instance.get(eventType).call(instance);
+ }
+ }
+
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/js/intro.js b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/intro.js
new file mode 100644
index 0000000..243b4f8
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/intro.js
@@ -0,0 +1,217 @@
+( function ( window ) {
+ 'use strict';
+ var NOT_DISABLED_NOT_RESET = ':not(:disabled):not(.ajs-reset)';
+ /**
+ * Keys enum
+ * @type {Object}
+ */
+ var keys = {
+ ENTER: 13,
+ ESC: 27,
+ F1: 112,
+ F12: 123,
+ LEFT: 37,
+ RIGHT: 39,
+ TAB: 9
+ };
+ /**
+ * Default options
+ * @type {Object}
+ */
+ var defaults = {
+ autoReset:true,
+ basic:false,
+ closable:true,
+ closableByDimmer:true,
+ invokeOnCloseOff:false,
+ frameless:false,
+ defaultFocusOff:false,
+ maintainFocus:true, //global default not per instance, applies to all dialogs
+ maximizable:true,
+ modal:true,
+ movable:true,
+ moveBounded:false,
+ overflow:true,
+ padding: true,
+ pinnable:true,
+ pinned:true,
+ preventBodyShift:false, //global default not per instance, applies to all dialogs
+ resizable:true,
+ startMaximized:false,
+ transition:'pulse',
+ tabbable:['button', '[href]', 'input', 'select', 'textarea', '[tabindex]:not([tabindex^="-"])'+NOT_DISABLED_NOT_RESET].join(NOT_DISABLED_NOT_RESET+','),//global
+ notifier:{
+ delay:5,
+ position:'bottom-right',
+ closeButton:false,
+ classes: {
+ base: 'alertify-notifier',
+ prefix:'ajs-',
+ message: 'ajs-message',
+ top: 'ajs-top',
+ right: 'ajs-right',
+ bottom: 'ajs-bottom',
+ left: 'ajs-left',
+ center: 'ajs-center',
+ visible: 'ajs-visible',
+ hidden: 'ajs-hidden',
+ close: 'ajs-close'
+ }
+ },
+ glossary:{
+ title:'AlertifyJS',
+ ok: 'OK',
+ cancel: 'Cancel',
+ acccpt: 'Accept',
+ deny: 'Deny',
+ confirm: 'Confirm',
+ decline: 'Decline',
+ close: 'Close',
+ maximize: 'Maximize',
+ restore: 'Restore',
+ },
+ theme:{
+ input:'ajs-input',
+ ok:'ajs-ok',
+ cancel:'ajs-cancel',
+ },
+ hooks:{
+ preinit:function(){},
+ postinit:function(){}
+ }
+ };
+
+ //holds open dialogs instances
+ var openDialogs = [];
+
+ /**
+ * [Helper] Adds the specified class(es) to the element.
+ *
+ * @element {node} The element
+ * @className {string} One or more space-separated classes to be added to the class attribute of the element.
+ *
+ * @return {undefined}
+ */
+ function addClass(element,classNames){
+ element.className += ' ' + classNames;
+ }
+
+ /**
+ * [Helper] Removes the specified class(es) from the element.
+ *
+ * @element {node} The element
+ * @className {string} One or more space-separated classes to be removed from the class attribute of the element.
+ *
+ * @return {undefined}
+ */
+ function removeClass(element, classNames) {
+ var original = element.className.split(' ');
+ var toBeRemoved = classNames.split(' ');
+ for (var x = 0; x < toBeRemoved.length; x += 1) {
+ var index = original.indexOf(toBeRemoved[x]);
+ if (index > -1){
+ original.splice(index,1);
+ }
+ }
+ element.className = original.join(' ');
+ }
+
+ /**
+ * [Helper] Checks if the document is RTL
+ *
+ * @return {Boolean} True if the document is RTL, false otherwise.
+ */
+ function isRightToLeft(){
+ return window.getComputedStyle(document.body).direction === 'rtl';
+ }
+ /**
+ * [Helper] Get the document current scrollTop
+ *
+ * @return {Number} current document scrollTop value
+ */
+ function getScrollTop(){
+ return ((document.documentElement && document.documentElement.scrollTop) || document.body.scrollTop);
+ }
+
+ /**
+ * [Helper] Get the document current scrollLeft
+ *
+ * @return {Number} current document scrollLeft value
+ */
+ function getScrollLeft(){
+ return ((document.documentElement && document.documentElement.scrollLeft) || document.body.scrollLeft);
+ }
+
+ /**
+ * Helper: clear contents
+ *
+ */
+ function clearContents(element){
+ while (element.lastChild) {
+ element.removeChild(element.lastChild);
+ }
+ }
+ /**
+ * Extends a given prototype by merging properties from base into sub.
+ *
+ * @sub {Object} sub The prototype being overwritten.
+ * @base {Object} base The prototype being written.
+ *
+ * @return {Object} The extended prototype.
+ */
+ function copy(src) {
+ if(null === src){
+ return src;
+ }
+ var cpy;
+ if(Array.isArray(src)){
+ cpy = [];
+ for(var x=0;x 0) {
+ var self = this;
+ this.__internal.timer = setTimeout(function () { self.dismiss(); }, this.__internal.delay * 1000);
+ }
+ return this;
+ },
+ /*
+ * Sets the notification message contents
+ * @param {string or DOMElement} content The notification message content
+ *
+ */
+ setContent: function (content) {
+ if (typeof content === 'string') {
+ clearContents(this.element);
+ this.element.innerHTML = content;
+ } else if (content instanceof window.HTMLElement && this.element.firstChild !== content) {
+ clearContents(this.element);
+ this.element.appendChild(content);
+ }
+ if(this.__internal.closeButton){
+ var close = document.createElement('span');
+ addClass(close, classes.close);
+ close.setAttribute('data-close', true);
+ this.element.appendChild(close);
+ }
+ return this;
+ },
+ /*
+ * Dismisses all open notifications except this.
+ *
+ */
+ dismissOthers: function () {
+ notifier.dismissAll(this);
+ return this;
+ }
+ });
+ }
+
+ //notifier api
+ return {
+ /**
+ * Gets or Sets notifier settings.
+ *
+ * @param {string} key The setting name
+ * @param {Variant} value The setting value.
+ *
+ * @return {Object} if the called as a setter, return the notifier instance.
+ */
+ setting: function (key, value) {
+ //ensure init
+ initialize(this);
+
+ if (typeof value === 'undefined') {
+ //get
+ return this.__internal[key];
+ } else {
+ //set
+ switch (key) {
+ case 'position':
+ this.__internal.position = value;
+ updatePosition(this);
+ break;
+ case 'delay':
+ this.__internal.delay = value;
+ break;
+ }
+ }
+ return this;
+ },
+ /**
+ * [Alias] Sets dialog settings/options
+ */
+ set:function(key,value){
+ this.setting(key,value);
+ return this;
+ },
+ /**
+ * [Alias] Gets dialog settings/options
+ */
+ get:function(key){
+ return this.setting(key);
+ },
+ /**
+ * Creates a new notification message
+ *
+ * @param {string} type The type of notification message (simply a CSS class name 'ajs-{type}' to be added).
+ * @param {Function} callback A callback function to be invoked when the message is dismissed.
+ *
+ * @return {undefined}
+ */
+ create: function (type, callback) {
+ //ensure notifier init
+ initialize(this);
+ //create new notification message
+ var div = document.createElement('div');
+ div.className = classes.message + ((typeof type === 'string' && type !== '') ? ' ' + classes.prefix + type : '');
+ return create(div, callback);
+ },
+ /**
+ * Dismisses all open notifications.
+ *
+ * @param {Object} excpet [optional] The notification object to exclude from dismissal.
+ *
+ */
+ dismissAll: function (except) {
+ var clone = openInstances.slice(0);
+ for (var x = 0; x < clone.length; x += 1) {
+ var instance = clone[x];
+ if (except === undefined || except !== instance) {
+ instance.dismiss();
+ }
+ }
+ }
+ };
+ })();
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/js/outro.js b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/outro.js
new file mode 100644
index 0000000..6323165
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/outro.js
@@ -0,0 +1,14 @@
+ // CommonJS
+ if ( typeof module === 'object' && typeof module.exports === 'object' ) {
+ module.exports = alertify;
+ // AMD
+ } else if ( typeof define === 'function' && define.amd) {
+ define( [], function () {
+ return alertify;
+ } );
+ // window
+ } else if ( !window.alertify ) {
+ window.alertify = alertify;
+ }
+
+} ( typeof window !== 'undefined' ? window : this ) );
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/js/prompt.js b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/prompt.js
new file mode 100644
index 0000000..4365343
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/js/prompt.js
@@ -0,0 +1,176 @@
+ /**
+ * Prompt dialog object
+ *
+ * invoked by:
+ * alertify.prompt(message);
+ * alertify.prompt(message, value);
+ * alertify.prompt(message, value, onok);
+ * alertify.prompt(message, value, onok, oncancel);
+ * alertify.prompt(title, message, value, onok, oncancel);
+ */
+ alertify.dialog('prompt', function () {
+ var input = document.createElement('INPUT');
+ var p = document.createElement('P');
+ return {
+ main: function (_title, _message, _value, _onok, _oncancel) {
+ var title, message, value, onok, oncancel;
+ switch (arguments.length) {
+ case 1:
+ message = _title;
+ break;
+ case 2:
+ message = _title;
+ value = _message;
+ break;
+ case 3:
+ message = _title;
+ value = _message;
+ onok = _value;
+ break;
+ case 4:
+ message = _title;
+ value = _message;
+ onok = _value;
+ oncancel = _onok;
+ break;
+ case 5:
+ title = _title;
+ message = _message;
+ value = _value;
+ onok = _onok;
+ oncancel = _oncancel;
+ break;
+ }
+ this.set('title', title);
+ this.set('message', message);
+ this.set('value', value);
+ this.set('onok', onok);
+ this.set('oncancel', oncancel);
+ return this;
+ },
+ setup: function () {
+ return {
+ buttons: [
+ {
+ text: alertify.defaults.glossary.ok,
+ key: keys.ENTER,
+ className: alertify.defaults.theme.ok,
+ },
+ {
+ text: alertify.defaults.glossary.cancel,
+ key: keys.ESC,
+ invokeOnClose: true,
+ className: alertify.defaults.theme.cancel,
+ }
+ ],
+ focus: {
+ element: input,
+ select: true
+ },
+ options: {
+ maximizable: false,
+ resizable: false
+ }
+ };
+ },
+ build: function () {
+ input.className = alertify.defaults.theme.input;
+ input.setAttribute('type', 'text');
+ input.value = this.get('value');
+ this.elements.content.appendChild(p);
+ this.elements.content.appendChild(input);
+ },
+ prepare: function () {
+ //nothing
+ },
+ setMessage: function (message) {
+ if (typeof message === 'string') {
+ clearContents(p);
+ p.innerHTML = message;
+ } else if (message instanceof window.HTMLElement && p.firstChild !== message) {
+ clearContents(p);
+ p.appendChild(message);
+ }
+ },
+ settings: {
+ message: undefined,
+ labels: undefined,
+ onok: undefined,
+ oncancel: undefined,
+ value: '',
+ type:'text',
+ reverseButtons: undefined,
+ },
+ settingUpdated: function (key, oldValue, newValue) {
+ switch (key) {
+ case 'message':
+ this.setMessage(newValue);
+ break;
+ case 'value':
+ input.value = newValue;
+ break;
+ case 'type':
+ switch (newValue) {
+ case 'text':
+ case 'color':
+ case 'date':
+ case 'datetime-local':
+ case 'email':
+ case 'month':
+ case 'number':
+ case 'password':
+ case 'search':
+ case 'tel':
+ case 'time':
+ case 'week':
+ input.type = newValue;
+ break;
+ default:
+ input.type = 'text';
+ break;
+ }
+ break;
+ case 'labels':
+ if (newValue.ok && this.__internal.buttons[0].element) {
+ this.__internal.buttons[0].element.innerHTML = newValue.ok;
+ }
+ if (newValue.cancel && this.__internal.buttons[1].element) {
+ this.__internal.buttons[1].element.innerHTML = newValue.cancel;
+ }
+ break;
+ case 'reverseButtons':
+ if (newValue === true) {
+ this.elements.buttons.primary.appendChild(this.__internal.buttons[0].element);
+ } else {
+ this.elements.buttons.primary.appendChild(this.__internal.buttons[1].element);
+ }
+ break;
+ }
+ },
+ callback: function (closeEvent) {
+ var returnValue;
+ switch (closeEvent.index) {
+ case 0:
+ this.settings.value = input.value;
+ if (typeof this.get('onok') === 'function') {
+ returnValue = this.get('onok').call(this, closeEvent, this.settings.value);
+ if (typeof returnValue !== 'undefined') {
+ closeEvent.cancel = !returnValue;
+ }
+ }
+ break;
+ case 1:
+ if (typeof this.get('oncancel') === 'function') {
+ returnValue = this.get('oncancel').call(this, closeEvent);
+ if (typeof returnValue !== 'undefined') {
+ closeEvent.cancel = !returnValue;
+ }
+ }
+ if(!closeEvent.cancel){
+ input.value = this.settings.value;
+ }
+ break;
+ }
+ }
+ };
+ });
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/less/alertify.less b/chatto/src/main/javascript/node_modules/alertifyjs/src/less/alertify.less
new file mode 100644
index 0000000..6461942
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/less/alertify.less
@@ -0,0 +1,921 @@
+.alertify {
+ .dimmer {
+ position: fixed;
+ z-index: 1981;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ padding: 0;
+ margin: 0;
+ background-color: #252525;
+ opacity: .5;
+ }
+
+ .modal {
+ position: fixed;
+ top: 0;
+ right: 0;
+ left: 0;
+ bottom: 0;
+ padding: 0;
+ overflow-y: auto;
+ z-index: 1981;
+ }
+
+ .dialog {
+ position: relative;
+ margin: 5% auto;
+ min-height: 110px;
+ max-width: 500px;
+ padding: 24px 24px 0 24px;
+ outline: 0;
+ background-color: #fff;
+
+ &.capture:before {
+ content: '';
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ display: block;
+ z-index: 1;
+ }
+ }
+
+ .reset {
+ position: absolute !important;
+ display: inline !important;
+ width: 0 !important;
+ height: 0 !important;
+ opacity: 0 !important;
+ }
+
+ .commands {
+ position: absolute;
+ right: 4px;
+ margin: -14px 24px 0 0;
+ z-index: 2;
+
+ button {
+ display: none;
+ width: 10px;
+ height: 10px;
+ margin-left: 10px;
+ padding: 10px;
+ border: 0;
+ background-color: transparent;
+ background-repeat: no-repeat;
+ background-position: center;
+ cursor: pointer;
+
+ &.close {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNy8xMy8xNOrZqugAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAh0lEQVQYlY2QsQ0EIQwEB9cBAR1CJUaI/gigDnwR6NBL/7/xWLNrZ2b8EwGotVpr7eOitWa1VjugiNB7R1UPrKrWe0dEAHBbXUqxMQbeewDmnHjvyTm7C3zDwAUd9c63YQdUVdu6EAJzzquz7HXvTiklt+H9DQFYaxFjvDqllFyMkbXWvfpXHjJrWFgdBq/hAAAAAElFTkSuQmCC);
+ }
+
+ &.maximize {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNy8xMy8xNOrZqugAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAOUlEQVQYlWP8//8/AzGAhYGBgaG4uBiv6t7eXkYmooxjYGAgWiELsvHYFMCcRX2rSXcjoSBiJDbAAeD+EGu+8BZcAAAAAElFTkSuQmCC);
+ }
+ }
+ //button
+ }
+ //commands
+ .header {
+ margin: -24px;
+ margin-bottom: 0;
+ padding: 16px 24px;
+ background-color: #fff;
+ }
+
+ .body {
+ min-height: 56px;
+
+ .content {
+ padding: 16px 24px 16px 16px;
+ }
+ }
+
+ .footer {
+ padding: 4px;
+ margin-left: -24px;
+ margin-right: -24px;
+ min-height: 43px;
+ background-color: #fff;
+
+ .buttons {
+ &.primary {
+ text-align: right;
+
+ .button {
+ margin: 4px;
+ }
+ }
+
+ &.auxiliary {
+ float: left;
+ clear: none;
+ text-align: left;
+
+ .button {
+ margin: 4px;
+ }
+ }
+
+ .button {
+ min-width: 88px;
+ min-height: 35px;
+ }
+ }
+ }
+
+ .handle {
+ position: absolute;
+ display: none;
+ width: 10px;
+ height: 10px;
+ right: 0;
+ bottom: 0;
+ z-index: 1;
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNy8xMS8xNEDQYmMAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAQ0lEQVQYlaXNMQoAIAxD0dT7H657l0KX3iJuUlBUNOsPPCGJm7VDp6ryeMxMuDsAQH7owW3pyn3RS26iKxERMLN3ugOaAkaL3sWVigAAAABJRU5ErkJggg==);
+ transform: scaleX(1) /*rtl:scaleX(-1)*/;
+ cursor: se-resize;
+ }
+
+ // pass overflow control to the content
+ &.no-overflow {
+ .body {
+ .content {
+ overflow: hidden !important;
+ }
+ }
+ }
+ // pass padding control to the content
+ &.no-padding {
+ &.maximized {
+ .body {
+ .content {
+ left: 0;
+ right: 0;
+ padding: 0;
+ }
+ }
+ }
+
+ &:not(.maximized) {
+ .body {
+ margin-left: -24px;
+ margin-right: -24px;
+
+ .content {
+ padding: 0;
+ }
+ }
+ }
+
+ &.resizable {
+ .body {
+ .content {
+ left: 0;
+ right: 0;
+ }
+ }
+ }
+ }
+ // has maximize box
+ &.maximizable {
+ .commands {
+ button {
+ &.maximize, &.restore {
+ display: inline-block;
+ }
+ }
+ }
+ }
+ // has close box
+ &.closable {
+ .commands {
+ button {
+ &.close {
+ display: inline-block;
+ }
+ }
+ }
+ }
+ // maximizes the dialog
+ &.maximized {
+ .dialog {
+ width: 100% !important;
+ height: 100% !important;
+ max-width: none !important;
+ margin: 0 auto !important;
+ top: 0 !important;
+ left: 0 !important;
+ }
+
+ &.modeless .modal {
+ position: fixed !important;
+ min-height: 100% !important;
+ max-height: none !important;
+ margin: 0 !important;
+ }
+
+ .commands {
+ button {
+ &.maximize {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNy8xMy8xNOrZqugAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAASklEQVQYlZWQ0QkAMQhDtXRincOZX78KVtrDCwgqJNEoIB3MPLj7lRUROlpyVXGzby6zWuY+kz6tj5sBMTMAyVV3/595RbOh3cAXsww1raeiOcoAAAAASUVORK5CYII=);
+ }
+ }
+ }
+ }
+ // resizable dialog
+ &.resizable, &.maximized {
+
+ .dialog {
+ padding: 0;
+ }
+
+ .commands {
+ margin: 14px 24px 0 0;
+ }
+
+ .header {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ margin: 0;
+ padding: 16px 24px;
+ }
+
+ .body {
+ min-height: 224px;
+ display: inline-block;
+
+ .content {
+ position: absolute;
+ top: 50px;
+ right: 24px;
+ bottom: 50px;
+ left: 24px;
+ overflow: auto;
+ }
+ }
+
+ .footer {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ margin: 0;
+ }
+ }
+
+ &.resizable:not(.maximized) {
+ .dialog {
+ //max-width + none resizable padding.
+ min-width: 548px;
+ }
+
+ .handle {
+ display: block;
+ }
+ }
+ // makes the dialog movable
+ &.movable:not(.maximized) {
+
+ .header {
+ cursor: move;
+ }
+ }
+ // makes the dilog modeless (non-modal)
+ &.modeless {
+
+ .dimmer, .reset {
+ display: none;
+ }
+
+ .modal {
+ overflow: visible;
+ max-width: none;
+ max-height: 0;
+ }
+ // has pin box
+ &.pinnable {
+ .commands {
+ button {
+ &.pin {
+ display: inline-block;
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNy8xMy8xNOrZqugAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAQklEQVQYlcWPMQ4AIAwCqU9u38GbcbHRWN1MvKQDhQFMEpKImGJA0gCgnYw0V0rwxseg5erT4oSkQVI5d9f+e9+xA0NbLpWfitPXAAAAAElFTkSuQmCC);
+ }
+ }
+ }
+ }
+
+ &.unpinned {
+ .modal {
+ position: absolute;
+ }
+
+ .commands {
+ button {
+ &.pin {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABZ0RVh0Q3JlYXRpb24gVGltZQAwNy8xMy8xNOrZqugAAAAcdEVYdFNvZnR3YXJlAEFkb2JlIEZpcmV3b3JrcyBDUzbovLKMAAAAO0lEQVQYlWP8//8/AzGAiShV6AqLi4txGs+CLoBLMYbC3t5eRmyaWfBZhwwYkX2NTxPRvibKjRhW4wMAhxkYGbLu3pEAAAAASUVORK5CYII=);
+ }
+ }
+ }
+ }
+
+ &:not(.unpinned) {
+ .body {
+ max-height: 500px;
+ overflow: auto;
+ }
+ }
+ }
+ //basic
+ &.basic {
+ .header{
+ opacity: 0;
+ }
+ .footer {
+ visibility:hidden;
+ }
+ }
+ //frameless
+ &.frameless {
+ .header{
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ min-height:60px;
+ margin: 0;
+ padding:0;
+ opacity:0;
+ z-index: 1;
+ }
+ .footer{
+ display:none;
+ }
+ .body {
+ .content {
+ position:absolute;
+ top:0;
+ right:0;
+ bottom:0;
+ left:0;
+ }
+ }
+ &:not(.resizable){
+ .dialog{
+ padding-top:0;
+ .commands{
+ margin-top:0;
+ }
+ }
+ }
+ }
+}
+
+//helpers
+//hides body overflow
+.no-overflow {
+ overflow: hidden !important;
+ outline: none;
+
+ &.fixed {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ overflow-y: scroll!important;
+ }
+}
+//disables selection
+.no-selection, .no-selection * {
+ user-select: none;
+}
+
+@media screen and (max-width: 568px) {
+ .alertify {
+ .dialog {
+ min-width: 150px;
+ }
+
+ &:not(.maximized) {
+ .modal {
+ padding: 0 5%;
+ }
+
+ &.resizable {
+ .dialog {
+ min-width: initial;
+ min-width: auto /*IE fallback*/;
+ }
+ }
+ }
+ }
+}
+
+//fix FF missing outline
+@-moz-document url-prefix() {
+ .alertify {
+ button:focus{
+ outline: 1px dotted #3593D2;
+ }
+ }
+}
+
+// transition
+.alertify {
+ // setup
+ .dimmer, .modal {
+ transform: translate3d(0, 0, 0);
+ transition-property: opacity, visibility;
+ transition-timing-function: linear;
+ transition-duration: 250ms;
+ }
+
+ &.hidden {
+ .dimmer, .modal {
+ visibility: hidden;
+ opacity: 0;
+ }
+ }
+ //in
+ &.in:not(.hidden) {
+ .dialog {
+ animation-duration: 500ms;
+ }
+ }
+ //out
+ &.out.hidden {
+ .dialog {
+ animation-duration: 250ms;
+ }
+ }
+ //helper animation
+ .dialog.shake {
+ animation-name: ajs-shake;
+ animation-duration: .1s;
+ animation-fill-mode: both;
+ }
+
+ @keyframes ajs-shake {
+ 0%, 100% {
+ transform: translate3d(0, 0, 0);
+ }
+
+ 10%, 30%, 50%, 70%, 90% {
+ transform: translate3d(-10px, 0, 0);
+ }
+
+ 20%, 40%, 60%, 80% {
+ transform: translate3d(10px, 0, 0);
+ }
+ }
+ // slide
+ &.slide {
+ //in
+ &.in:not(.hidden) {
+ .dialog {
+ animation-name: ajs-slideIn;
+ animation-timing-function: cubic-bezier( 0.175, 0.885, 0.320, 1.275 );
+ }
+ }
+ //out
+ &.out.hidden {
+ .dialog {
+ animation-name: ajs-slideOut;
+ animation-timing-function: cubic-bezier( 0.600, -0.280, 0.735, 0.045 );
+ }
+ }
+ }
+ // zoom
+ &.zoom {
+ //in
+ &.in:not(.hidden) {
+ .dialog {
+ animation-name: ajs-zoomIn;
+ }
+ }
+ //out
+ &.out.hidden {
+ .dialog {
+ animation-name: ajs-zoomOut;
+ }
+ }
+ }
+ // fade
+ &.fade {
+ //in
+ &.in:not(.hidden) {
+ .dialog {
+ animation-name: ajs-fadeIn;
+ }
+ }
+ //out
+ &.out.hidden {
+ .dialog {
+ animation-name: ajs-fadeOut;
+ }
+ }
+ }
+ // pulse
+ &.pulse {
+ //in
+ &.in:not(.hidden) {
+ .dialog {
+ animation-name: ajs-pulseIn;
+ }
+ }
+ //out
+ &.out.hidden {
+ .dialog {
+ animation-name: ajs-pulseOut;
+ }
+ }
+ }
+ // flip vertical
+ &.flipx {
+ //in
+ &.in:not(.hidden) {
+ .dialog {
+ animation-name: ajs-flipInX;
+ }
+ }
+ //out
+ &.out.hidden {
+ .dialog {
+ animation-name: ajs-flipOutX;
+ }
+ }
+ }
+ // flip vertical
+ &.flipy {
+ //in
+ &.in:not(.hidden) {
+ .dialog {
+ animation-name: ajs-flipInY;
+ }
+ }
+ //out
+ &.out.hidden {
+ .dialog {
+ animation-name: ajs-flipOutY;
+ }
+ }
+ }
+}
+
+
+/////////
+//pulse//
+@keyframes ajs-pulseIn {
+ 0%, 20%, 40%, 60%, 80%, 100% {
+ transition-timing-function: cubic-bezier(0.215, 0.610, 0.355, 1.000);
+ }
+
+ 0% {
+ opacity: 0;
+ transform: scale3d(.3, .3, .3);
+ }
+
+ 20% {
+ transform: scale3d(1.1, 1.1, 1.1);
+ }
+
+ 40% {
+ transform: scale3d(.9, .9, .9);
+ }
+
+ 60% {
+ opacity: 1;
+ transform: scale3d(1.03, 1.03, 1.03);
+ }
+
+ 80% {
+ transform: scale3d(.97, .97, .97);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+@keyframes ajs-pulseOut {
+ 20% {
+ transform: scale3d(.9, .9, .9);
+ }
+
+ 50%, 55% {
+ opacity: 1;
+ transform: scale3d(1.1, 1.1, 1.1);
+ }
+
+ 100% {
+ opacity: 0;
+ transform: scale3d(.3, .3, .3);
+ }
+}
+//pulse//
+/////////
+
+////////
+//zoom//
+@keyframes ajs-zoomIn {
+ 0% {
+ opacity: 0;
+ transform: scale3d(.25, .25, .25);
+ }
+
+ 100% {
+ opacity: 1;
+ transform: scale3d(1, 1, 1);
+ }
+}
+
+@keyframes ajs-zoomOut {
+ 0% {
+ opacity: 1;
+ transform: scale3d(1, 1, 1);
+ }
+
+ 100% {
+ opacity: 0;
+ transform: scale3d(.25, .25, .25);
+ }
+}
+//zoom//
+////////
+
+
+////////
+//fade//
+@keyframes ajs-fadeIn {
+ 0% {
+ opacity: 0;
+ }
+
+ 100% {
+ opacity: 1;
+ }
+}
+
+@keyframes ajs-fadeOut {
+ 0% {
+ opacity: 1;
+ }
+
+ 100% {
+ opacity: 0;
+ }
+}
+//fade//
+////////
+
+/////////
+//flipx//
+@keyframes ajs-flipInX {
+ 0% {
+ transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+ transition-timing-function: ease-in;
+ opacity: 0;
+ }
+
+ 40% {
+ transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+ transition-timing-function: ease-in;
+ }
+
+ 60% {
+ transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
+ opacity: 1;
+ }
+
+ 80% {
+ transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
+ }
+
+ 100% {
+ transform: perspective(400px);
+ }
+}
+
+
+@keyframes ajs-flipOutX {
+ 0% {
+ transform: perspective(400px);
+ }
+
+ 30% {
+ transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
+ opacity: 1;
+ }
+
+ 100% {
+ transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
+ opacity: 0;
+ }
+}
+//flipx//
+/////////
+
+
+
+/////////
+//flipy//
+@keyframes ajs-flipInY {
+ 0% {
+ transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
+ transition-timing-function: ease-in;
+ opacity: 0;
+ }
+
+ 40% {
+ transform: perspective(400px) rotate3d(0, 1, 0, -20deg);
+ transition-timing-function: ease-in;
+ }
+
+ 60% {
+ transform: perspective(400px) rotate3d(0, 1, 0, 10deg);
+ opacity: 1;
+ }
+
+ 80% {
+ transform: perspective(400px) rotate3d(0, 1, 0, -5deg);
+ }
+
+ 100% {
+ transform: perspective(400px);
+ }
+}
+
+@keyframes ajs-flipOutY {
+ 0% {
+ transform: perspective(400px);
+ }
+
+ 30% {
+ transform: perspective(400px) rotate3d(0, 1, 0, -15deg);
+ opacity: 1;
+ }
+
+ 100% {
+ transform: perspective(400px) rotate3d(0, 1, 0, 90deg);
+ opacity: 0;
+ }
+}
+//flipy//
+/////////
+
+////////
+//slide//
+@keyframes ajs-slideIn {
+ 0% {
+ margin-top: -100%;
+ }
+
+ 100% {
+ margin-top: 5%;
+ }
+}
+
+@keyframes ajs-slideOut {
+ 0% {
+ margin-top: 5%;
+ }
+
+ 100% {
+ margin-top: -100%;
+ }
+}
+//slide//
+////////
+
+/***************
+ Notifier
+***************/
+
+.alertify-notifier {
+ position: fixed;
+ width: 0;
+ overflow: visible;
+ z-index: 1982;
+ transform: translate3d(0,0,0);
+
+ .message {
+ position: relative;
+ width: 260px;
+ max-height: 0;
+ padding: 0;
+ opacity: 0;
+ margin: 0;
+ transform: translate3d(0,0,0);
+ transition-duration: 250ms;
+ transition-timing-function: linear;
+
+ &.visible {
+ transition-duration: 500ms;
+ transition-timing-function: cubic-bezier( 0.175, 0.885, 0.320, 1.275 );
+ opacity: 1;
+ max-height: 100%;
+ padding: 15px;
+ margin-top: 10px;
+ }
+
+ &.success {
+ background: rgba(91, 189, 114,.95);
+ }
+
+ &.error {
+ background: rgba(217, 92, 92,.95);
+ }
+
+ &.warning {
+ background: rgba(252, 248, 215, 0.95);
+ }
+
+ .close {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width:16px;
+ height:16px;
+ cursor: pointer;
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAABGdBTUEAALGPC/xhBQAAAFBJREFUGBl1j0EKADEIA+ve/P9f9bh1hEihNBfjVCO1v7RKVqJK4h8gM5cAPR42AkQEpSXPwMTyoi13n5N9YqJehm3Fnr7nL1D0ZEbD5OubGyC7a9gx+9eNAAAAAElFTkSuQmCC);
+ background-repeat: no-repeat;
+ background-position: center center;
+ background-color:rgba(0, 0, 0, 0.5);
+ border-top-right-radius: 2px;
+ }
+ }
+
+ &.top {
+ top: 10px;
+ }
+
+ &.bottom {
+ bottom: 10px;
+ }
+
+ &.right {
+ right: 10px;
+
+ .message {
+ right: -320px;
+
+ &.visible {
+ right: 290px;
+ }
+ }
+ }
+
+ &.left {
+ left: 10px;
+
+ .message {
+ left: -300px;
+
+ &.visible {
+ left: 0;
+ }
+ }
+ }
+
+ &.center {
+ left: 50%;
+
+ .message {
+ transform: translateX(-50%);
+
+ &.visible {
+ left: 50%;
+ transition-timing-function: cubic-bezier(0.57, 0.43, 0.1, 0.65);
+ }
+ }
+
+ &.top {
+ .message {
+ top: -300px;
+
+ &.visible {
+ top: 0;
+ }
+ }
+ }
+
+ &.bottom {
+ .message {
+ bottom: -300px;
+
+ &.visible {
+ bottom: 0;
+ }
+ }
+ }
+ }
+}
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/less/themes/bootstrap.less b/chatto/src/main/javascript/node_modules/alertifyjs/src/less/themes/bootstrap.less
new file mode 100644
index 0000000..479e3f8
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/less/themes/bootstrap.less
@@ -0,0 +1,73 @@
+.alertify {
+ .dimmer {
+ background-color: #000;
+ opacity: .5;
+ }
+
+ .dialog {
+ max-width: 600px;
+ min-height: 122px;
+ background-color: #fff;
+ border: 1px solid rgba(0,0,0,.2);
+ box-shadow: 0 5px 15px rgba(0,0,0,.5);
+ border-radius: 6px;
+ }
+
+ .header {
+ color: #333;
+ border-bottom: 1px solid #e5e5e5;
+ border-radius: 6px 6px 0 0;
+ font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
+ font-size: 18px;
+ }
+
+ .body {
+ font-family: 'Roboto', sans-serif;
+ color: black;
+ }
+
+ &.resizable, &.maximized:not(.resizable) {
+ .content {
+ top: 58px;
+ bottom: 68px;
+ }
+ }
+
+ .footer {
+ background-color: #fff;
+ padding: 15px;
+ border-top: 1px solid #e5e5e5;
+ border-radius: 0 0 6px 6px;
+ }
+}
+
+/***************
+ notifier
+***************/
+
+.alertify-notifier {
+ .message {
+ background: rgba( 255, 255, 255, .95);
+ color: #000;
+ text-align: center;
+ border: solid 1px #ddd;
+ border-radius: 2px;
+
+ &.success {
+ color: #fff;
+ background: rgba(91, 189, 114,.95);
+ text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.5);
+ }
+
+ &.error {
+ color: #fff;
+ background: rgba(217, 92, 92,.95);
+ text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.5);
+ }
+
+ &.warning {
+ background: rgba(252, 248, 215, 0.95);
+ border-color: #999;
+ }
+ }
+}
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/less/themes/default.less b/chatto/src/main/javascript/node_modules/alertifyjs/src/less/themes/default.less
new file mode 100644
index 0000000..37cdd8c
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/less/themes/default.less
@@ -0,0 +1,91 @@
+.alertify {
+
+ .dialog {
+ background-color: white;
+ box-shadow: 0px 15px 20px 0px rgba(0, 0, 0, 0.25);
+ border-radius: 2px;
+ }
+
+ .header {
+ color: black;
+ font-weight: bold;
+ background: #fafafa;
+ border-bottom: #eee 1px solid;
+ border-radius: 2px 2px 0 0;
+ }
+
+ .body {
+ color: black;
+
+ .content {
+
+ .input {
+ display: block;
+ width: 100%;
+ padding: 8px;
+ margin: 4px;
+ border-radius: 2px;
+ border: 1px solid #CCC;
+ }
+
+ p {
+ margin: 0;
+ }
+ }
+ }
+
+ .footer {
+ background: #fbfbfb;
+ border-top: #eee 1px solid;
+ border-radius: 0 0 2px 2px;
+
+ .buttons {
+ .button {
+ background-color: transparent;
+ color: #000;
+ border: 0;
+ font-size: 14px;
+ font-weight: bold;
+ text-transform: uppercase;
+ //&:focus{
+ // outline:auto 5px #498EFF;
+ // box-shadow: 0px 0px 4px 0px #498EFF;
+ //}
+ &.ok {
+ color: #3593D2;
+ }
+ }
+ }
+ }
+}
+
+/***************
+ notifier
+***************/
+
+.alertify-notifier {
+ .message {
+ background: rgba( 255, 255, 255, .95);
+ color: #000;
+ text-align: center;
+ border: solid 1px #ddd;
+ border-radius: 2px;
+
+ &.success {
+ color: #fff;
+ background: rgba(91, 189, 114,.95);
+ text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.5);
+ }
+
+ &.error {
+ color: #fff;
+ background: rgba(217, 92, 92,.95);
+ text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.5);
+ }
+
+ &.warning {
+ background: rgba(252, 248, 215, 0.95);
+ border-color: #999;
+ }
+ }
+}
diff --git a/chatto/src/main/javascript/node_modules/alertifyjs/src/less/themes/semantic.less b/chatto/src/main/javascript/node_modules/alertifyjs/src/less/themes/semantic.less
new file mode 100644
index 0000000..0acdfa6
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/alertifyjs/src/less/themes/semantic.less
@@ -0,0 +1,102 @@
+.alertify {
+ .dimmer {
+ background-color: rgba(0,0,0,.85);
+ opacity: 1;
+ }
+
+ .dialog {
+ max-width: 50%;
+ min-height: 137px;
+ background-color: #F4F4F4;
+ border: 1px solid #DDD;
+ box-shadow: none;
+ border-radius: 5px;
+ }
+
+ .header {
+ padding: 1.5rem 2rem;
+ border-bottom: none;
+ border-radius: 5px 5px 0 0;
+ color: #555;
+ background-color: #fff;
+ font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
+ font-size: 1.6em;
+ font-weight: 700;
+ }
+
+ .body {
+ font-family: 'Roboto', sans-serif;
+ color: #555;
+
+ .content {
+ .input {
+ width: 100%;
+ margin: 0;
+ padding: .65em 1em;
+ font-size: 1em;
+ background-color: #FFF;
+ border: 1px solid rgba(0,0,0,.15);
+ outline: 0;
+ color: rgba(0,0,0,.7);
+ border-radius: .3125em;
+ transition: background-color .3s ease-out,box-shadow .2s ease,border-color .2s ease;
+ box-sizing: border-box;
+
+ &:active {
+ border-color: rgba(0, 0, 0, 0.3);
+ background-color: #FAFAFA;
+ }
+
+ &:focus {
+ border-color: rgba(0, 0, 0, 0.2);
+ color: rgba(0, 0, 0, 0.85);
+ }
+ }
+ }
+ }
+
+ &.resizable, &.maximized:not(.resizable) {
+ .content {
+ top: 64px;
+ bottom: 74px;
+ }
+ }
+
+ .footer {
+ background-color: #fff;
+ padding: 1rem 2rem;
+ border-top: none;
+ border-radius: 0 0 5px 5px;
+ }
+}
+
+/***************
+ notifier
+***************/
+
+.alertify-notifier {
+ .message {
+ background: rgba( 255, 255, 255, .95);
+ color: #000;
+ text-align: center;
+ border: solid 1px #ddd;
+ border-radius: 2px;
+
+ &.success {
+ color: #fff;
+ background: rgba(91, 189, 114,.95);
+ text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.5);
+ }
+
+ &.error {
+ color: #fff;
+ background: rgba(217, 92, 92,.95);
+ text-shadow: -1px -1px 0 rgba(0, 0, 0, 0.5);
+ }
+
+ &.warning {
+ background: rgba(252, 248, 215, 0.95);
+ border-color: #999;
+ }
+ }
+}
diff --git a/chatto/src/main/javascript/node_modules/argparse/CHANGELOG.md b/chatto/src/main/javascript/node_modules/argparse/CHANGELOG.md
new file mode 100644
index 0000000..a43c628
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/CHANGELOG.md
@@ -0,0 +1,185 @@
+1.0.10 / 2018-02-15
+------------------
+
+- Use .concat instead of + for arrays, #122.
+
+
+1.0.9 / 2016-09-29
+------------------
+
+- Rerelease after 1.0.8 - deps cleanup.
+
+
+1.0.8 / 2016-09-29
+------------------
+
+- Maintenance (deps bump, fix node 6.5+ tests, coverage report).
+
+
+1.0.7 / 2016-03-17
+------------------
+
+- Teach `addArgument` to accept string arg names. #97, @tomxtobin.
+
+
+1.0.6 / 2016-02-06
+------------------
+
+- Maintenance: moved to eslint & updated CS.
+
+
+1.0.5 / 2016-02-05
+------------------
+
+- Removed lodash dependency to significantly reduce install size.
+ Thanks to @mourner.
+
+
+1.0.4 / 2016-01-17
+------------------
+
+- Maintenance: lodash update to 4.0.0.
+
+
+1.0.3 / 2015-10-27
+------------------
+
+- Fix parse `=` in args: `--examplepath="C:\myfolder\env=x64"`. #84, @CatWithApple.
+
+
+1.0.2 / 2015-03-22
+------------------
+
+- Relaxed lodash version dependency.
+
+
+1.0.1 / 2015-02-20
+------------------
+
+- Changed dependencies to be compatible with ancient nodejs.
+
+
+1.0.0 / 2015-02-19
+------------------
+
+- Maintenance release.
+- Replaced `underscore` with `lodash`.
+- Bumped version to 1.0.0 to better reflect semver meaning.
+- HISTORY.md -> CHANGELOG.md
+
+
+0.1.16 / 2013-12-01
+-------------------
+
+- Maintenance release. Updated dependencies and docs.
+
+
+0.1.15 / 2013-05-13
+-------------------
+
+- Fixed #55, @trebor89
+
+
+0.1.14 / 2013-05-12
+-------------------
+
+- Fixed #62, @maxtaco
+
+
+0.1.13 / 2013-04-08
+-------------------
+
+- Added `.npmignore` to reduce package size
+
+
+0.1.12 / 2013-02-10
+-------------------
+
+- Fixed conflictHandler (#46), @hpaulj
+
+
+0.1.11 / 2013-02-07
+-------------------
+
+- Multiple bugfixes, @hpaulj
+- Added 70+ tests (ported from python), @hpaulj
+- Added conflictHandler, @applepicke
+- Added fromfilePrefixChar, @hpaulj
+
+
+0.1.10 / 2012-12-30
+-------------------
+
+- Added [mutual exclusion](http://docs.python.org/dev/library/argparse.html#mutual-exclusion)
+ support, thanks to @hpaulj
+- Fixed options check for `storeConst` & `appendConst` actions, thanks to @hpaulj
+
+
+0.1.9 / 2012-12-27
+------------------
+
+- Fixed option dest interferens with other options (issue #23), thanks to @hpaulj
+- Fixed default value behavior with `*` positionals, thanks to @hpaulj
+- Improve `getDefault()` behavior, thanks to @hpaulj
+- Imrove negative argument parsing, thanks to @hpaulj
+
+
+0.1.8 / 2012-12-01
+------------------
+
+- Fixed parser parents (issue #19), thanks to @hpaulj
+- Fixed negative argument parse (issue #20), thanks to @hpaulj
+
+
+0.1.7 / 2012-10-14
+------------------
+
+- Fixed 'choices' argument parse (issue #16)
+- Fixed stderr output (issue #15)
+
+
+0.1.6 / 2012-09-09
+------------------
+
+- Fixed check for conflict of options (thanks to @tomxtobin)
+
+
+0.1.5 / 2012-09-03
+------------------
+
+- Fix parser #setDefaults method (thanks to @tomxtobin)
+
+
+0.1.4 / 2012-07-30
+------------------
+
+- Fixed pseudo-argument support (thanks to @CGamesPlay)
+- Fixed addHelp default (should be true), if not set (thanks to @benblank)
+
+
+0.1.3 / 2012-06-27
+------------------
+
+- Fixed formatter api name: Formatter -> HelpFormatter
+
+
+0.1.2 / 2012-05-29
+------------------
+
+- Added basic tests
+- Removed excess whitespace in help
+- Fixed error reporting, when parcer with subcommands
+ called with empty arguments
+
+
+0.1.1 / 2012-05-23
+------------------
+
+- Fixed line wrapping in help formatter
+- Added better error reporting on invalid arguments
+
+
+0.1.0 / 2012-05-16
+------------------
+
+- First release.
diff --git a/chatto/src/main/javascript/node_modules/argparse/LICENSE b/chatto/src/main/javascript/node_modules/argparse/LICENSE
new file mode 100644
index 0000000..1afdae5
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/LICENSE
@@ -0,0 +1,21 @@
+(The MIT License)
+
+Copyright (C) 2012 by Vitaly Puzrin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chatto/src/main/javascript/node_modules/argparse/README.md b/chatto/src/main/javascript/node_modules/argparse/README.md
new file mode 100644
index 0000000..7fa6c40
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/README.md
@@ -0,0 +1,257 @@
+argparse
+========
+
+[![Build Status](https://secure.travis-ci.org/nodeca/argparse.svg?branch=master)](http://travis-ci.org/nodeca/argparse)
+[![NPM version](https://img.shields.io/npm/v/argparse.svg)](https://www.npmjs.org/package/argparse)
+
+CLI arguments parser for node.js. Javascript port of python's
+[argparse](http://docs.python.org/dev/library/argparse.html) module
+(original version 3.2). That's a full port, except some very rare options,
+recorded in issue tracker.
+
+**NB. Difference with original.**
+
+- Method names changed to camelCase. See [generated docs](http://nodeca.github.com/argparse/).
+- Use `defaultValue` instead of `default`.
+- Use `argparse.Const.REMAINDER` instead of `argparse.REMAINDER`, and
+ similarly for constant values `OPTIONAL`, `ZERO_OR_MORE`, and `ONE_OR_MORE`
+ (aliases for `nargs` values `'?'`, `'*'`, `'+'`, respectively), and
+ `SUPPRESS`.
+
+
+Example
+=======
+
+test.js file:
+
+```javascript
+#!/usr/bin/env node
+'use strict';
+
+var ArgumentParser = require('../lib/argparse').ArgumentParser;
+var parser = new ArgumentParser({
+ version: '0.0.1',
+ addHelp:true,
+ description: 'Argparse example'
+});
+parser.addArgument(
+ [ '-f', '--foo' ],
+ {
+ help: 'foo bar'
+ }
+);
+parser.addArgument(
+ [ '-b', '--bar' ],
+ {
+ help: 'bar foo'
+ }
+);
+parser.addArgument(
+ '--baz',
+ {
+ help: 'baz bar'
+ }
+);
+var args = parser.parseArgs();
+console.dir(args);
+```
+
+Display help:
+
+```
+$ ./test.js -h
+usage: example.js [-h] [-v] [-f FOO] [-b BAR] [--baz BAZ]
+
+Argparse example
+
+Optional arguments:
+ -h, --help Show this help message and exit.
+ -v, --version Show program's version number and exit.
+ -f FOO, --foo FOO foo bar
+ -b BAR, --bar BAR bar foo
+ --baz BAZ baz bar
+```
+
+Parse arguments:
+
+```
+$ ./test.js -f=3 --bar=4 --baz 5
+{ foo: '3', bar: '4', baz: '5' }
+```
+
+More [examples](https://github.com/nodeca/argparse/tree/master/examples).
+
+
+ArgumentParser objects
+======================
+
+```
+new ArgumentParser({parameters hash});
+```
+
+Creates a new ArgumentParser object.
+
+**Supported params:**
+
+- ```description``` - Text to display before the argument help.
+- ```epilog``` - Text to display after the argument help.
+- ```addHelp``` - Add a -h/–help option to the parser. (default: true)
+- ```argumentDefault``` - Set the global default value for arguments. (default: null)
+- ```parents``` - A list of ArgumentParser objects whose arguments should also be included.
+- ```prefixChars``` - The set of characters that prefix optional arguments. (default: ‘-‘)
+- ```formatterClass``` - A class for customizing the help output.
+- ```prog``` - The name of the program (default: `path.basename(process.argv[1])`)
+- ```usage``` - The string describing the program usage (default: generated)
+- ```conflictHandler``` - Usually unnecessary, defines strategy for resolving conflicting optionals.
+
+**Not supported yet**
+
+- ```fromfilePrefixChars``` - The set of characters that prefix files from which additional arguments should be read.
+
+
+Details in [original ArgumentParser guide](http://docs.python.org/dev/library/argparse.html#argumentparser-objects)
+
+
+addArgument() method
+====================
+
+```
+ArgumentParser.addArgument(name or flag or [name] or [flags...], {options})
+```
+
+Defines how a single command-line argument should be parsed.
+
+- ```name or flag or [name] or [flags...]``` - Either a positional name
+ (e.g., `'foo'`), a single option (e.g., `'-f'` or `'--foo'`), an array
+ of a single positional name (e.g., `['foo']`), or an array of options
+ (e.g., `['-f', '--foo']`).
+
+Options:
+
+- ```action``` - The basic type of action to be taken when this argument is encountered at the command line.
+- ```nargs```- The number of command-line arguments that should be consumed.
+- ```constant``` - A constant value required by some action and nargs selections.
+- ```defaultValue``` - The value produced if the argument is absent from the command line.
+- ```type``` - The type to which the command-line argument should be converted.
+- ```choices``` - A container of the allowable values for the argument.
+- ```required``` - Whether or not the command-line option may be omitted (optionals only).
+- ```help``` - A brief description of what the argument does.
+- ```metavar``` - A name for the argument in usage messages.
+- ```dest``` - The name of the attribute to be added to the object returned by parseArgs().
+
+Details in [original add_argument guide](http://docs.python.org/dev/library/argparse.html#the-add-argument-method)
+
+
+Action (some details)
+================
+
+ArgumentParser objects associate command-line arguments with actions.
+These actions can do just about anything with the command-line arguments associated
+with them, though most actions simply add an attribute to the object returned by
+parseArgs(). The action keyword argument specifies how the command-line arguments
+should be handled. The supported actions are:
+
+- ```store``` - Just stores the argument’s value. This is the default action.
+- ```storeConst``` - Stores value, specified by the const keyword argument.
+ (Note that the const keyword argument defaults to the rather unhelpful None.)
+ The 'storeConst' action is most commonly used with optional arguments, that
+ specify some sort of flag.
+- ```storeTrue``` and ```storeFalse``` - Stores values True and False
+ respectively. These are special cases of 'storeConst'.
+- ```append``` - Stores a list, and appends each argument value to the list.
+ This is useful to allow an option to be specified multiple times.
+- ```appendConst``` - Stores a list, and appends value, specified by the
+ const keyword argument to the list. (Note, that the const keyword argument defaults
+ is None.) The 'appendConst' action is typically used when multiple arguments need
+ to store constants to the same list.
+- ```count``` - Counts the number of times a keyword argument occurs. For example,
+ used for increasing verbosity levels.
+- ```help``` - Prints a complete help message for all the options in the current
+ parser and then exits. By default a help action is automatically added to the parser.
+ See ArgumentParser for details of how the output is created.
+- ```version``` - Prints version information and exit. Expects a `version=`
+ keyword argument in the addArgument() call.
+
+Details in [original action guide](http://docs.python.org/dev/library/argparse.html#action)
+
+
+Sub-commands
+============
+
+ArgumentParser.addSubparsers()
+
+Many programs split their functionality into a number of sub-commands, for
+example, the svn program can invoke sub-commands like `svn checkout`, `svn update`,
+and `svn commit`. Splitting up functionality this way can be a particularly good
+idea when a program performs several different functions which require different
+kinds of command-line arguments. `ArgumentParser` supports creation of such
+sub-commands with `addSubparsers()` method. The `addSubparsers()` method is
+normally called with no arguments and returns an special action object.
+This object has a single method `addParser()`, which takes a command name and
+any `ArgumentParser` constructor arguments, and returns an `ArgumentParser` object
+that can be modified as usual.
+
+Example:
+
+sub_commands.js
+```javascript
+#!/usr/bin/env node
+'use strict';
+
+var ArgumentParser = require('../lib/argparse').ArgumentParser;
+var parser = new ArgumentParser({
+ version: '0.0.1',
+ addHelp:true,
+ description: 'Argparse examples: sub-commands',
+});
+
+var subparsers = parser.addSubparsers({
+ title:'subcommands',
+ dest:"subcommand_name"
+});
+
+var bar = subparsers.addParser('c1', {addHelp:true});
+bar.addArgument(
+ [ '-f', '--foo' ],
+ {
+ action: 'store',
+ help: 'foo3 bar3'
+ }
+);
+var bar = subparsers.addParser(
+ 'c2',
+ {aliases:['co'], addHelp:true}
+);
+bar.addArgument(
+ [ '-b', '--bar' ],
+ {
+ action: 'store',
+ type: 'int',
+ help: 'foo3 bar3'
+ }
+);
+
+var args = parser.parseArgs();
+console.dir(args);
+
+```
+
+Details in [original sub-commands guide](http://docs.python.org/dev/library/argparse.html#sub-commands)
+
+
+Contributors
+============
+
+- [Eugene Shkuropat](https://github.com/shkuropat)
+- [Paul Jacobson](https://github.com/hpaulj)
+
+[others](https://github.com/nodeca/argparse/graphs/contributors)
+
+License
+=======
+
+Copyright (c) 2012 [Vitaly Puzrin](https://github.com/puzrin).
+Released under the MIT license. See
+[LICENSE](https://github.com/nodeca/argparse/blob/master/LICENSE) for details.
+
+
diff --git a/chatto/src/main/javascript/node_modules/argparse/index.js b/chatto/src/main/javascript/node_modules/argparse/index.js
new file mode 100644
index 0000000..3bbc143
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/index.js
@@ -0,0 +1,3 @@
+'use strict';
+
+module.exports = require('./lib/argparse');
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/action.js b/chatto/src/main/javascript/node_modules/argparse/lib/action.js
new file mode 100644
index 0000000..1483c79
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/action.js
@@ -0,0 +1,146 @@
+/**
+ * class Action
+ *
+ * Base class for all actions
+ * Do not call in your code, use this class only for inherits your own action
+ *
+ * Information about how to convert command line strings to Javascript objects.
+ * Action objects are used by an ArgumentParser to represent the information
+ * needed to parse a single argument from one or more strings from the command
+ * line. The keyword arguments to the Action constructor are also all attributes
+ * of Action instances.
+ *
+ * ##### Allowed keywords:
+ *
+ * - `store`
+ * - `storeConstant`
+ * - `storeTrue`
+ * - `storeFalse`
+ * - `append`
+ * - `appendConstant`
+ * - `count`
+ * - `help`
+ * - `version`
+ *
+ * Information about action options see [[Action.new]]
+ *
+ * See also [original guide](http://docs.python.org/dev/library/argparse.html#action)
+ *
+ **/
+
+'use strict';
+
+
+// Constants
+var c = require('./const');
+
+
+/**
+ * new Action(options)
+ *
+ * Base class for all actions. Used only for inherits
+ *
+ *
+ * ##### Options:
+ *
+ * - `optionStrings` A list of command-line option strings for the action.
+ * - `dest` Attribute to hold the created object(s)
+ * - `nargs` The number of command-line arguments that should be consumed.
+ * By default, one argument will be consumed and a single value will be
+ * produced.
+ * - `constant` Default value for an action with no value.
+ * - `defaultValue` The value to be produced if the option is not specified.
+ * - `type` Cast to 'string'|'int'|'float'|'complex'|function (string). If
+ * None, 'string'.
+ * - `choices` The choices available.
+ * - `required` True if the action must always be specified at the command
+ * line.
+ * - `help` The help describing the argument.
+ * - `metavar` The name to be used for the option's argument with the help
+ * string. If None, the 'dest' value will be used as the name.
+ *
+ * ##### nargs supported values:
+ *
+ * - `N` (an integer) consumes N arguments (and produces a list)
+ * - `?` consumes zero or one arguments
+ * - `*` consumes zero or more arguments (and produces a list)
+ * - `+` consumes one or more arguments (and produces a list)
+ *
+ * Note: that the difference between the default and nargs=1 is that with the
+ * default, a single value will be produced, while with nargs=1, a list
+ * containing a single value will be produced.
+ **/
+var Action = module.exports = function Action(options) {
+ options = options || {};
+ this.optionStrings = options.optionStrings || [];
+ this.dest = options.dest;
+ this.nargs = typeof options.nargs !== 'undefined' ? options.nargs : null;
+ this.constant = typeof options.constant !== 'undefined' ? options.constant : null;
+ this.defaultValue = options.defaultValue;
+ this.type = typeof options.type !== 'undefined' ? options.type : null;
+ this.choices = typeof options.choices !== 'undefined' ? options.choices : null;
+ this.required = typeof options.required !== 'undefined' ? options.required : false;
+ this.help = typeof options.help !== 'undefined' ? options.help : null;
+ this.metavar = typeof options.metavar !== 'undefined' ? options.metavar : null;
+
+ if (!(this.optionStrings instanceof Array)) {
+ throw new Error('optionStrings should be an array');
+ }
+ if (typeof this.required !== 'undefined' && typeof this.required !== 'boolean') {
+ throw new Error('required should be a boolean');
+ }
+};
+
+/**
+ * Action#getName -> String
+ *
+ * Tells action name
+ **/
+Action.prototype.getName = function () {
+ if (this.optionStrings.length > 0) {
+ return this.optionStrings.join('/');
+ } else if (this.metavar !== null && this.metavar !== c.SUPPRESS) {
+ return this.metavar;
+ } else if (typeof this.dest !== 'undefined' && this.dest !== c.SUPPRESS) {
+ return this.dest;
+ }
+ return null;
+};
+
+/**
+ * Action#isOptional -> Boolean
+ *
+ * Return true if optional
+ **/
+Action.prototype.isOptional = function () {
+ return !this.isPositional();
+};
+
+/**
+ * Action#isPositional -> Boolean
+ *
+ * Return true if positional
+ **/
+Action.prototype.isPositional = function () {
+ return (this.optionStrings.length === 0);
+};
+
+/**
+ * Action#call(parser, namespace, values, optionString) -> Void
+ * - parser (ArgumentParser): current parser
+ * - namespace (Namespace): namespace for output data
+ * - values (Array): parsed values
+ * - optionString (Array): input option string(not parsed)
+ *
+ * Call the action. Should be implemented in inherited classes
+ *
+ * ##### Example
+ *
+ * ActionCount.prototype.call = function (parser, namespace, values, optionString) {
+ * namespace.set(this.dest, (namespace[this.dest] || 0) + 1);
+ * };
+ *
+ **/
+Action.prototype.call = function () {
+ throw new Error('.call() not defined');// Not Implemented error
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/action/append.js b/chatto/src/main/javascript/node_modules/argparse/lib/action/append.js
new file mode 100644
index 0000000..b5da0de
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/action/append.js
@@ -0,0 +1,53 @@
+/*:nodoc:*
+ * class ActionAppend
+ *
+ * This action stores a list, and appends each argument value to the list.
+ * This is useful to allow an option to be specified multiple times.
+ * This class inherided from [[Action]]
+ *
+ **/
+
+'use strict';
+
+var util = require('util');
+
+var Action = require('../action');
+
+// Constants
+var c = require('../const');
+
+/*:nodoc:*
+ * new ActionAppend(options)
+ * - options (object): options hash see [[Action.new]]
+ *
+ * Note: options.nargs should be optional for constants
+ * and more then zero for other
+ **/
+var ActionAppend = module.exports = function ActionAppend(options) {
+ options = options || {};
+ if (this.nargs <= 0) {
+ throw new Error('nargs for append actions must be > 0; if arg ' +
+ 'strings are not supplying the value to append, ' +
+ 'the append const action may be more appropriate');
+ }
+ if (!!this.constant && this.nargs !== c.OPTIONAL) {
+ throw new Error('nargs must be OPTIONAL to supply const');
+ }
+ Action.call(this, options);
+};
+util.inherits(ActionAppend, Action);
+
+/*:nodoc:*
+ * ActionAppend#call(parser, namespace, values, optionString) -> Void
+ * - parser (ArgumentParser): current parser
+ * - namespace (Namespace): namespace for output data
+ * - values (Array): parsed values
+ * - optionString (Array): input option string(not parsed)
+ *
+ * Call the action. Save result in namespace object
+ **/
+ActionAppend.prototype.call = function (parser, namespace, values) {
+ var items = (namespace[this.dest] || []).slice();
+ items.push(values);
+ namespace.set(this.dest, items);
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/action/append/constant.js b/chatto/src/main/javascript/node_modules/argparse/lib/action/append/constant.js
new file mode 100644
index 0000000..313f5d2
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/action/append/constant.js
@@ -0,0 +1,47 @@
+/*:nodoc:*
+ * class ActionAppendConstant
+ *
+ * This stores a list, and appends the value specified by
+ * the const keyword argument to the list.
+ * (Note that the const keyword argument defaults to null.)
+ * The 'appendConst' action is typically useful when multiple
+ * arguments need to store constants to the same list.
+ *
+ * This class inherited from [[Action]]
+ **/
+
+'use strict';
+
+var util = require('util');
+
+var Action = require('../../action');
+
+/*:nodoc:*
+ * new ActionAppendConstant(options)
+ * - options (object): options hash see [[Action.new]]
+ *
+ **/
+var ActionAppendConstant = module.exports = function ActionAppendConstant(options) {
+ options = options || {};
+ options.nargs = 0;
+ if (typeof options.constant === 'undefined') {
+ throw new Error('constant option is required for appendAction');
+ }
+ Action.call(this, options);
+};
+util.inherits(ActionAppendConstant, Action);
+
+/*:nodoc:*
+ * ActionAppendConstant#call(parser, namespace, values, optionString) -> Void
+ * - parser (ArgumentParser): current parser
+ * - namespace (Namespace): namespace for output data
+ * - values (Array): parsed values
+ * - optionString (Array): input option string(not parsed)
+ *
+ * Call the action. Save result in namespace object
+ **/
+ActionAppendConstant.prototype.call = function (parser, namespace) {
+ var items = [].concat(namespace[this.dest] || []);
+ items.push(this.constant);
+ namespace.set(this.dest, items);
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/action/count.js b/chatto/src/main/javascript/node_modules/argparse/lib/action/count.js
new file mode 100644
index 0000000..d6a5899
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/action/count.js
@@ -0,0 +1,40 @@
+/*:nodoc:*
+ * class ActionCount
+ *
+ * This counts the number of times a keyword argument occurs.
+ * For example, this is useful for increasing verbosity levels
+ *
+ * This class inherided from [[Action]]
+ *
+ **/
+'use strict';
+
+var util = require('util');
+
+var Action = require('../action');
+
+/*:nodoc:*
+ * new ActionCount(options)
+ * - options (object): options hash see [[Action.new]]
+ *
+ **/
+var ActionCount = module.exports = function ActionCount(options) {
+ options = options || {};
+ options.nargs = 0;
+
+ Action.call(this, options);
+};
+util.inherits(ActionCount, Action);
+
+/*:nodoc:*
+ * ActionCount#call(parser, namespace, values, optionString) -> Void
+ * - parser (ArgumentParser): current parser
+ * - namespace (Namespace): namespace for output data
+ * - values (Array): parsed values
+ * - optionString (Array): input option string(not parsed)
+ *
+ * Call the action. Save result in namespace object
+ **/
+ActionCount.prototype.call = function (parser, namespace) {
+ namespace.set(this.dest, (namespace[this.dest] || 0) + 1);
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/action/help.js b/chatto/src/main/javascript/node_modules/argparse/lib/action/help.js
new file mode 100644
index 0000000..b40e05a
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/action/help.js
@@ -0,0 +1,47 @@
+/*:nodoc:*
+ * class ActionHelp
+ *
+ * Support action for printing help
+ * This class inherided from [[Action]]
+ **/
+'use strict';
+
+var util = require('util');
+
+var Action = require('../action');
+
+// Constants
+var c = require('../const');
+
+/*:nodoc:*
+ * new ActionHelp(options)
+ * - options (object): options hash see [[Action.new]]
+ *
+ **/
+var ActionHelp = module.exports = function ActionHelp(options) {
+ options = options || {};
+ if (options.defaultValue !== null) {
+ options.defaultValue = options.defaultValue;
+ } else {
+ options.defaultValue = c.SUPPRESS;
+ }
+ options.dest = (options.dest !== null ? options.dest : c.SUPPRESS);
+ options.nargs = 0;
+ Action.call(this, options);
+
+};
+util.inherits(ActionHelp, Action);
+
+/*:nodoc:*
+ * ActionHelp#call(parser, namespace, values, optionString)
+ * - parser (ArgumentParser): current parser
+ * - namespace (Namespace): namespace for output data
+ * - values (Array): parsed values
+ * - optionString (Array): input option string(not parsed)
+ *
+ * Print help and exit
+ **/
+ActionHelp.prototype.call = function (parser) {
+ parser.printHelp();
+ parser.exit();
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/action/store.js b/chatto/src/main/javascript/node_modules/argparse/lib/action/store.js
new file mode 100644
index 0000000..283b860
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/action/store.js
@@ -0,0 +1,50 @@
+/*:nodoc:*
+ * class ActionStore
+ *
+ * This action just stores the argument’s value. This is the default action.
+ *
+ * This class inherited from [[Action]]
+ *
+ **/
+'use strict';
+
+var util = require('util');
+
+var Action = require('../action');
+
+// Constants
+var c = require('../const');
+
+
+/*:nodoc:*
+ * new ActionStore(options)
+ * - options (object): options hash see [[Action.new]]
+ *
+ **/
+var ActionStore = module.exports = function ActionStore(options) {
+ options = options || {};
+ if (this.nargs <= 0) {
+ throw new Error('nargs for store actions must be > 0; if you ' +
+ 'have nothing to store, actions such as store ' +
+ 'true or store const may be more appropriate');
+
+ }
+ if (typeof this.constant !== 'undefined' && this.nargs !== c.OPTIONAL) {
+ throw new Error('nargs must be OPTIONAL to supply const');
+ }
+ Action.call(this, options);
+};
+util.inherits(ActionStore, Action);
+
+/*:nodoc:*
+ * ActionStore#call(parser, namespace, values, optionString) -> Void
+ * - parser (ArgumentParser): current parser
+ * - namespace (Namespace): namespace for output data
+ * - values (Array): parsed values
+ * - optionString (Array): input option string(not parsed)
+ *
+ * Call the action. Save result in namespace object
+ **/
+ActionStore.prototype.call = function (parser, namespace, values) {
+ namespace.set(this.dest, values);
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/action/store/constant.js b/chatto/src/main/javascript/node_modules/argparse/lib/action/store/constant.js
new file mode 100644
index 0000000..23caa89
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/action/store/constant.js
@@ -0,0 +1,43 @@
+/*:nodoc:*
+ * class ActionStoreConstant
+ *
+ * This action stores the value specified by the const keyword argument.
+ * (Note that the const keyword argument defaults to the rather unhelpful null.)
+ * The 'store_const' action is most commonly used with optional
+ * arguments that specify some sort of flag.
+ *
+ * This class inherited from [[Action]]
+ **/
+'use strict';
+
+var util = require('util');
+
+var Action = require('../../action');
+
+/*:nodoc:*
+ * new ActionStoreConstant(options)
+ * - options (object): options hash see [[Action.new]]
+ *
+ **/
+var ActionStoreConstant = module.exports = function ActionStoreConstant(options) {
+ options = options || {};
+ options.nargs = 0;
+ if (typeof options.constant === 'undefined') {
+ throw new Error('constant option is required for storeAction');
+ }
+ Action.call(this, options);
+};
+util.inherits(ActionStoreConstant, Action);
+
+/*:nodoc:*
+ * ActionStoreConstant#call(parser, namespace, values, optionString) -> Void
+ * - parser (ArgumentParser): current parser
+ * - namespace (Namespace): namespace for output data
+ * - values (Array): parsed values
+ * - optionString (Array): input option string(not parsed)
+ *
+ * Call the action. Save result in namespace object
+ **/
+ActionStoreConstant.prototype.call = function (parser, namespace) {
+ namespace.set(this.dest, this.constant);
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/action/store/false.js b/chatto/src/main/javascript/node_modules/argparse/lib/action/store/false.js
new file mode 100644
index 0000000..9924f46
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/action/store/false.js
@@ -0,0 +1,27 @@
+/*:nodoc:*
+ * class ActionStoreFalse
+ *
+ * This action store the values False respectively.
+ * This is special cases of 'storeConst'
+ *
+ * This class inherited from [[Action]]
+ **/
+
+'use strict';
+
+var util = require('util');
+
+var ActionStoreConstant = require('./constant');
+
+/*:nodoc:*
+ * new ActionStoreFalse(options)
+ * - options (object): hash of options see [[Action.new]]
+ *
+ **/
+var ActionStoreFalse = module.exports = function ActionStoreFalse(options) {
+ options = options || {};
+ options.constant = false;
+ options.defaultValue = options.defaultValue !== null ? options.defaultValue : true;
+ ActionStoreConstant.call(this, options);
+};
+util.inherits(ActionStoreFalse, ActionStoreConstant);
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/action/store/true.js b/chatto/src/main/javascript/node_modules/argparse/lib/action/store/true.js
new file mode 100644
index 0000000..9e22f7d
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/action/store/true.js
@@ -0,0 +1,26 @@
+/*:nodoc:*
+ * class ActionStoreTrue
+ *
+ * This action store the values True respectively.
+ * This isspecial cases of 'storeConst'
+ *
+ * This class inherited from [[Action]]
+ **/
+'use strict';
+
+var util = require('util');
+
+var ActionStoreConstant = require('./constant');
+
+/*:nodoc:*
+ * new ActionStoreTrue(options)
+ * - options (object): options hash see [[Action.new]]
+ *
+ **/
+var ActionStoreTrue = module.exports = function ActionStoreTrue(options) {
+ options = options || {};
+ options.constant = true;
+ options.defaultValue = options.defaultValue !== null ? options.defaultValue : false;
+ ActionStoreConstant.call(this, options);
+};
+util.inherits(ActionStoreTrue, ActionStoreConstant);
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/action/subparsers.js b/chatto/src/main/javascript/node_modules/argparse/lib/action/subparsers.js
new file mode 100644
index 0000000..99dfedd
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/action/subparsers.js
@@ -0,0 +1,149 @@
+/** internal
+ * class ActionSubparsers
+ *
+ * Support the creation of such sub-commands with the addSubparsers()
+ *
+ * This class inherited from [[Action]]
+ **/
+'use strict';
+
+var util = require('util');
+var format = require('util').format;
+
+
+var Action = require('../action');
+
+// Constants
+var c = require('../const');
+
+// Errors
+var argumentErrorHelper = require('../argument/error');
+
+
+/*:nodoc:*
+ * new ChoicesPseudoAction(name, help)
+ *
+ * Create pseudo action for correct help text
+ *
+ **/
+function ChoicesPseudoAction(name, help) {
+ var options = {
+ optionStrings: [],
+ dest: name,
+ help: help
+ };
+
+ Action.call(this, options);
+}
+
+util.inherits(ChoicesPseudoAction, Action);
+
+/**
+ * new ActionSubparsers(options)
+ * - options (object): options hash see [[Action.new]]
+ *
+ **/
+function ActionSubparsers(options) {
+ options = options || {};
+ options.dest = options.dest || c.SUPPRESS;
+ options.nargs = c.PARSER;
+
+ this.debug = (options.debug === true);
+
+ this._progPrefix = options.prog;
+ this._parserClass = options.parserClass;
+ this._nameParserMap = {};
+ this._choicesActions = [];
+
+ options.choices = this._nameParserMap;
+ Action.call(this, options);
+}
+
+util.inherits(ActionSubparsers, Action);
+
+/*:nodoc:*
+ * ActionSubparsers#addParser(name, options) -> ArgumentParser
+ * - name (string): sub-command name
+ * - options (object): see [[ArgumentParser.new]]
+ *
+ * Note:
+ * addParser supports an additional aliases option,
+ * which allows multiple strings to refer to the same subparser.
+ * This example, like svn, aliases co as a shorthand for checkout
+ *
+ **/
+ActionSubparsers.prototype.addParser = function (name, options) {
+ var parser;
+
+ var self = this;
+
+ options = options || {};
+
+ options.debug = (this.debug === true);
+
+ // set program from the existing prefix
+ if (!options.prog) {
+ options.prog = this._progPrefix + ' ' + name;
+ }
+
+ var aliases = options.aliases || [];
+
+ // create a pseudo-action to hold the choice help
+ if (!!options.help || typeof options.help === 'string') {
+ var help = options.help;
+ delete options.help;
+
+ var choiceAction = new ChoicesPseudoAction(name, help);
+ this._choicesActions.push(choiceAction);
+ }
+
+ // create the parser and add it to the map
+ parser = new this._parserClass(options);
+ this._nameParserMap[name] = parser;
+
+ // make parser available under aliases also
+ aliases.forEach(function (alias) {
+ self._nameParserMap[alias] = parser;
+ });
+
+ return parser;
+};
+
+ActionSubparsers.prototype._getSubactions = function () {
+ return this._choicesActions;
+};
+
+/*:nodoc:*
+ * ActionSubparsers#call(parser, namespace, values, optionString) -> Void
+ * - parser (ArgumentParser): current parser
+ * - namespace (Namespace): namespace for output data
+ * - values (Array): parsed values
+ * - optionString (Array): input option string(not parsed)
+ *
+ * Call the action. Parse input aguments
+ **/
+ActionSubparsers.prototype.call = function (parser, namespace, values) {
+ var parserName = values[0];
+ var argStrings = values.slice(1);
+
+ // set the parser name if requested
+ if (this.dest !== c.SUPPRESS) {
+ namespace[this.dest] = parserName;
+ }
+
+ // select the parser
+ if (this._nameParserMap[parserName]) {
+ parser = this._nameParserMap[parserName];
+ } else {
+ throw argumentErrorHelper(format(
+ 'Unknown parser "%s" (choices: [%s]).',
+ parserName,
+ Object.keys(this._nameParserMap).join(', ')
+ ));
+ }
+
+ // parse all the remaining options into the namespace
+ parser.parseArgs(argStrings, namespace);
+};
+
+module.exports = ActionSubparsers;
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/action/version.js b/chatto/src/main/javascript/node_modules/argparse/lib/action/version.js
new file mode 100644
index 0000000..8053328
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/action/version.js
@@ -0,0 +1,47 @@
+/*:nodoc:*
+ * class ActionVersion
+ *
+ * Support action for printing program version
+ * This class inherited from [[Action]]
+ **/
+'use strict';
+
+var util = require('util');
+
+var Action = require('../action');
+
+//
+// Constants
+//
+var c = require('../const');
+
+/*:nodoc:*
+ * new ActionVersion(options)
+ * - options (object): options hash see [[Action.new]]
+ *
+ **/
+var ActionVersion = module.exports = function ActionVersion(options) {
+ options = options || {};
+ options.defaultValue = (options.defaultValue ? options.defaultValue : c.SUPPRESS);
+ options.dest = (options.dest || c.SUPPRESS);
+ options.nargs = 0;
+ this.version = options.version;
+ Action.call(this, options);
+};
+util.inherits(ActionVersion, Action);
+
+/*:nodoc:*
+ * ActionVersion#call(parser, namespace, values, optionString) -> Void
+ * - parser (ArgumentParser): current parser
+ * - namespace (Namespace): namespace for output data
+ * - values (Array): parsed values
+ * - optionString (Array): input option string(not parsed)
+ *
+ * Print version and exit
+ **/
+ActionVersion.prototype.call = function (parser) {
+ var version = this.version || parser.version;
+ var formatter = parser._getFormatter();
+ formatter.addText(version);
+ parser.exit(0, formatter.formatHelp());
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/action_container.js b/chatto/src/main/javascript/node_modules/argparse/lib/action_container.js
new file mode 100644
index 0000000..6f1237b
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/action_container.js
@@ -0,0 +1,482 @@
+/** internal
+ * class ActionContainer
+ *
+ * Action container. Parent for [[ArgumentParser]] and [[ArgumentGroup]]
+ **/
+
+'use strict';
+
+var format = require('util').format;
+
+// Constants
+var c = require('./const');
+
+var $$ = require('./utils');
+
+//Actions
+var ActionHelp = require('./action/help');
+var ActionAppend = require('./action/append');
+var ActionAppendConstant = require('./action/append/constant');
+var ActionCount = require('./action/count');
+var ActionStore = require('./action/store');
+var ActionStoreConstant = require('./action/store/constant');
+var ActionStoreTrue = require('./action/store/true');
+var ActionStoreFalse = require('./action/store/false');
+var ActionVersion = require('./action/version');
+var ActionSubparsers = require('./action/subparsers');
+
+// Errors
+var argumentErrorHelper = require('./argument/error');
+
+/**
+ * new ActionContainer(options)
+ *
+ * Action container. Parent for [[ArgumentParser]] and [[ArgumentGroup]]
+ *
+ * ##### Options:
+ *
+ * - `description` -- A description of what the program does
+ * - `prefixChars` -- Characters that prefix optional arguments
+ * - `argumentDefault` -- The default value for all arguments
+ * - `conflictHandler` -- The conflict handler to use for duplicate arguments
+ **/
+var ActionContainer = module.exports = function ActionContainer(options) {
+ options = options || {};
+
+ this.description = options.description;
+ this.argumentDefault = options.argumentDefault;
+ this.prefixChars = options.prefixChars || '';
+ this.conflictHandler = options.conflictHandler;
+
+ // set up registries
+ this._registries = {};
+
+ // register actions
+ this.register('action', null, ActionStore);
+ this.register('action', 'store', ActionStore);
+ this.register('action', 'storeConst', ActionStoreConstant);
+ this.register('action', 'storeTrue', ActionStoreTrue);
+ this.register('action', 'storeFalse', ActionStoreFalse);
+ this.register('action', 'append', ActionAppend);
+ this.register('action', 'appendConst', ActionAppendConstant);
+ this.register('action', 'count', ActionCount);
+ this.register('action', 'help', ActionHelp);
+ this.register('action', 'version', ActionVersion);
+ this.register('action', 'parsers', ActionSubparsers);
+
+ // raise an exception if the conflict handler is invalid
+ this._getHandler();
+
+ // action storage
+ this._actions = [];
+ this._optionStringActions = {};
+
+ // groups
+ this._actionGroups = [];
+ this._mutuallyExclusiveGroups = [];
+
+ // defaults storage
+ this._defaults = {};
+
+ // determines whether an "option" looks like a negative number
+ // -1, -1.5 -5e+4
+ this._regexpNegativeNumber = new RegExp('^[-]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$');
+
+ // whether or not there are any optionals that look like negative
+ // numbers -- uses a list so it can be shared and edited
+ this._hasNegativeNumberOptionals = [];
+};
+
+// Groups must be required, then ActionContainer already defined
+var ArgumentGroup = require('./argument/group');
+var MutuallyExclusiveGroup = require('./argument/exclusive');
+
+//
+// Registration methods
+//
+
+/**
+ * ActionContainer#register(registryName, value, object) -> Void
+ * - registryName (String) : object type action|type
+ * - value (string) : keyword
+ * - object (Object|Function) : handler
+ *
+ * Register handlers
+ **/
+ActionContainer.prototype.register = function (registryName, value, object) {
+ this._registries[registryName] = this._registries[registryName] || {};
+ this._registries[registryName][value] = object;
+};
+
+ActionContainer.prototype._registryGet = function (registryName, value, defaultValue) {
+ if (arguments.length < 3) {
+ defaultValue = null;
+ }
+ return this._registries[registryName][value] || defaultValue;
+};
+
+//
+// Namespace default accessor methods
+//
+
+/**
+ * ActionContainer#setDefaults(options) -> Void
+ * - options (object):hash of options see [[Action.new]]
+ *
+ * Set defaults
+ **/
+ActionContainer.prototype.setDefaults = function (options) {
+ options = options || {};
+ for (var property in options) {
+ if ($$.has(options, property)) {
+ this._defaults[property] = options[property];
+ }
+ }
+
+ // if these defaults match any existing arguments, replace the previous
+ // default on the object with the new one
+ this._actions.forEach(function (action) {
+ if ($$.has(options, action.dest)) {
+ action.defaultValue = options[action.dest];
+ }
+ });
+};
+
+/**
+ * ActionContainer#getDefault(dest) -> Mixed
+ * - dest (string): action destination
+ *
+ * Return action default value
+ **/
+ActionContainer.prototype.getDefault = function (dest) {
+ var result = $$.has(this._defaults, dest) ? this._defaults[dest] : null;
+
+ this._actions.forEach(function (action) {
+ if (action.dest === dest && $$.has(action, 'defaultValue')) {
+ result = action.defaultValue;
+ }
+ });
+
+ return result;
+};
+//
+// Adding argument actions
+//
+
+/**
+ * ActionContainer#addArgument(args, options) -> Object
+ * - args (String|Array): argument key, or array of argument keys
+ * - options (Object): action objects see [[Action.new]]
+ *
+ * #### Examples
+ * - addArgument([ '-f', '--foo' ], { action: 'store', defaultValue: 1, ... })
+ * - addArgument([ 'bar' ], { action: 'store', nargs: 1, ... })
+ * - addArgument('--baz', { action: 'store', nargs: 1, ... })
+ **/
+ActionContainer.prototype.addArgument = function (args, options) {
+ args = args;
+ options = options || {};
+
+ if (typeof args === 'string') {
+ args = [ args ];
+ }
+ if (!Array.isArray(args)) {
+ throw new TypeError('addArgument first argument should be a string or an array');
+ }
+ if (typeof options !== 'object' || Array.isArray(options)) {
+ throw new TypeError('addArgument second argument should be a hash');
+ }
+
+ // if no positional args are supplied or only one is supplied and
+ // it doesn't look like an option string, parse a positional argument
+ if (!args || args.length === 1 && this.prefixChars.indexOf(args[0][0]) < 0) {
+ if (args && !!options.dest) {
+ throw new Error('dest supplied twice for positional argument');
+ }
+ options = this._getPositional(args, options);
+
+ // otherwise, we're adding an optional argument
+ } else {
+ options = this._getOptional(args, options);
+ }
+
+ // if no default was supplied, use the parser-level default
+ if (typeof options.defaultValue === 'undefined') {
+ var dest = options.dest;
+ if ($$.has(this._defaults, dest)) {
+ options.defaultValue = this._defaults[dest];
+ } else if (typeof this.argumentDefault !== 'undefined') {
+ options.defaultValue = this.argumentDefault;
+ }
+ }
+
+ // create the action object, and add it to the parser
+ var ActionClass = this._popActionClass(options);
+ if (typeof ActionClass !== 'function') {
+ throw new Error(format('Unknown action "%s".', ActionClass));
+ }
+ var action = new ActionClass(options);
+
+ // throw an error if the action type is not callable
+ var typeFunction = this._registryGet('type', action.type, action.type);
+ if (typeof typeFunction !== 'function') {
+ throw new Error(format('"%s" is not callable', typeFunction));
+ }
+
+ return this._addAction(action);
+};
+
+/**
+ * ActionContainer#addArgumentGroup(options) -> ArgumentGroup
+ * - options (Object): hash of options see [[ArgumentGroup.new]]
+ *
+ * Create new arguments groups
+ **/
+ActionContainer.prototype.addArgumentGroup = function (options) {
+ var group = new ArgumentGroup(this, options);
+ this._actionGroups.push(group);
+ return group;
+};
+
+/**
+ * ActionContainer#addMutuallyExclusiveGroup(options) -> ArgumentGroup
+ * - options (Object): {required: false}
+ *
+ * Create new mutual exclusive groups
+ **/
+ActionContainer.prototype.addMutuallyExclusiveGroup = function (options) {
+ var group = new MutuallyExclusiveGroup(this, options);
+ this._mutuallyExclusiveGroups.push(group);
+ return group;
+};
+
+ActionContainer.prototype._addAction = function (action) {
+ var self = this;
+
+ // resolve any conflicts
+ this._checkConflict(action);
+
+ // add to actions list
+ this._actions.push(action);
+ action.container = this;
+
+ // index the action by any option strings it has
+ action.optionStrings.forEach(function (optionString) {
+ self._optionStringActions[optionString] = action;
+ });
+
+ // set the flag if any option strings look like negative numbers
+ action.optionStrings.forEach(function (optionString) {
+ if (optionString.match(self._regexpNegativeNumber)) {
+ if (!self._hasNegativeNumberOptionals.some(Boolean)) {
+ self._hasNegativeNumberOptionals.push(true);
+ }
+ }
+ });
+
+ // return the created action
+ return action;
+};
+
+ActionContainer.prototype._removeAction = function (action) {
+ var actionIndex = this._actions.indexOf(action);
+ if (actionIndex >= 0) {
+ this._actions.splice(actionIndex, 1);
+ }
+};
+
+ActionContainer.prototype._addContainerActions = function (container) {
+ // collect groups by titles
+ var titleGroupMap = {};
+ this._actionGroups.forEach(function (group) {
+ if (titleGroupMap[group.title]) {
+ throw new Error(format('Cannot merge actions - two groups are named "%s".', group.title));
+ }
+ titleGroupMap[group.title] = group;
+ });
+
+ // map each action to its group
+ var groupMap = {};
+ function actionHash(action) {
+ // unique (hopefully?) string suitable as dictionary key
+ return action.getName();
+ }
+ container._actionGroups.forEach(function (group) {
+ // if a group with the title exists, use that, otherwise
+ // create a new group matching the container's group
+ if (!titleGroupMap[group.title]) {
+ titleGroupMap[group.title] = this.addArgumentGroup({
+ title: group.title,
+ description: group.description
+ });
+ }
+
+ // map the actions to their new group
+ group._groupActions.forEach(function (action) {
+ groupMap[actionHash(action)] = titleGroupMap[group.title];
+ });
+ }, this);
+
+ // add container's mutually exclusive groups
+ // NOTE: if add_mutually_exclusive_group ever gains title= and
+ // description= then this code will need to be expanded as above
+ var mutexGroup;
+ container._mutuallyExclusiveGroups.forEach(function (group) {
+ mutexGroup = this.addMutuallyExclusiveGroup({
+ required: group.required
+ });
+ // map the actions to their new mutex group
+ group._groupActions.forEach(function (action) {
+ groupMap[actionHash(action)] = mutexGroup;
+ });
+ }, this); // forEach takes a 'this' argument
+
+ // add all actions to this container or their group
+ container._actions.forEach(function (action) {
+ var key = actionHash(action);
+ if (groupMap[key]) {
+ groupMap[key]._addAction(action);
+ } else {
+ this._addAction(action);
+ }
+ });
+};
+
+ActionContainer.prototype._getPositional = function (dest, options) {
+ if (Array.isArray(dest)) {
+ dest = dest[0];
+ }
+ // make sure required is not specified
+ if (options.required) {
+ throw new Error('"required" is an invalid argument for positionals.');
+ }
+
+ // mark positional arguments as required if at least one is
+ // always required
+ if (options.nargs !== c.OPTIONAL && options.nargs !== c.ZERO_OR_MORE) {
+ options.required = true;
+ }
+ if (options.nargs === c.ZERO_OR_MORE && typeof options.defaultValue === 'undefined') {
+ options.required = true;
+ }
+
+ // return the keyword arguments with no option strings
+ options.dest = dest;
+ options.optionStrings = [];
+ return options;
+};
+
+ActionContainer.prototype._getOptional = function (args, options) {
+ var prefixChars = this.prefixChars;
+ var optionStrings = [];
+ var optionStringsLong = [];
+
+ // determine short and long option strings
+ args.forEach(function (optionString) {
+ // error on strings that don't start with an appropriate prefix
+ if (prefixChars.indexOf(optionString[0]) < 0) {
+ throw new Error(format('Invalid option string "%s": must start with a "%s".',
+ optionString,
+ prefixChars
+ ));
+ }
+
+ // strings starting with two prefix characters are long options
+ optionStrings.push(optionString);
+ if (optionString.length > 1 && prefixChars.indexOf(optionString[1]) >= 0) {
+ optionStringsLong.push(optionString);
+ }
+ });
+
+ // infer dest, '--foo-bar' -> 'foo_bar' and '-x' -> 'x'
+ var dest = options.dest || null;
+ delete options.dest;
+
+ if (!dest) {
+ var optionStringDest = optionStringsLong.length ? optionStringsLong[0] : optionStrings[0];
+ dest = $$.trimChars(optionStringDest, this.prefixChars);
+
+ if (dest.length === 0) {
+ throw new Error(
+ format('dest= is required for options like "%s"', optionStrings.join(', '))
+ );
+ }
+ dest = dest.replace(/-/g, '_');
+ }
+
+ // return the updated keyword arguments
+ options.dest = dest;
+ options.optionStrings = optionStrings;
+
+ return options;
+};
+
+ActionContainer.prototype._popActionClass = function (options, defaultValue) {
+ defaultValue = defaultValue || null;
+
+ var action = (options.action || defaultValue);
+ delete options.action;
+
+ var actionClass = this._registryGet('action', action, action);
+ return actionClass;
+};
+
+ActionContainer.prototype._getHandler = function () {
+ var handlerString = this.conflictHandler;
+ var handlerFuncName = '_handleConflict' + $$.capitalize(handlerString);
+ var func = this[handlerFuncName];
+ if (typeof func === 'undefined') {
+ var msg = 'invalid conflict resolution value: ' + handlerString;
+ throw new Error(msg);
+ } else {
+ return func;
+ }
+};
+
+ActionContainer.prototype._checkConflict = function (action) {
+ var optionStringActions = this._optionStringActions;
+ var conflictOptionals = [];
+
+ // find all options that conflict with this option
+ // collect pairs, the string, and an existing action that it conflicts with
+ action.optionStrings.forEach(function (optionString) {
+ var conflOptional = optionStringActions[optionString];
+ if (typeof conflOptional !== 'undefined') {
+ conflictOptionals.push([ optionString, conflOptional ]);
+ }
+ });
+
+ if (conflictOptionals.length > 0) {
+ var conflictHandler = this._getHandler();
+ conflictHandler.call(this, action, conflictOptionals);
+ }
+};
+
+ActionContainer.prototype._handleConflictError = function (action, conflOptionals) {
+ var conflicts = conflOptionals.map(function (pair) { return pair[0]; });
+ conflicts = conflicts.join(', ');
+ throw argumentErrorHelper(
+ action,
+ format('Conflicting option string(s): %s', conflicts)
+ );
+};
+
+ActionContainer.prototype._handleConflictResolve = function (action, conflOptionals) {
+ // remove all conflicting options
+ var self = this;
+ conflOptionals.forEach(function (pair) {
+ var optionString = pair[0];
+ var conflictingAction = pair[1];
+ // remove the conflicting option string
+ var i = conflictingAction.optionStrings.indexOf(optionString);
+ if (i >= 0) {
+ conflictingAction.optionStrings.splice(i, 1);
+ }
+ delete self._optionStringActions[optionString];
+ // if the option now has no option string, remove it from the
+ // container holding it
+ if (conflictingAction.optionStrings.length === 0) {
+ conflictingAction.container._removeAction(conflictingAction);
+ }
+ });
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/argparse.js b/chatto/src/main/javascript/node_modules/argparse/lib/argparse.js
new file mode 100644
index 0000000..f2a2c51
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/argparse.js
@@ -0,0 +1,14 @@
+'use strict';
+
+module.exports.ArgumentParser = require('./argument_parser.js');
+module.exports.Namespace = require('./namespace');
+module.exports.Action = require('./action');
+module.exports.HelpFormatter = require('./help/formatter.js');
+module.exports.Const = require('./const.js');
+
+module.exports.ArgumentDefaultsHelpFormatter =
+ require('./help/added_formatters.js').ArgumentDefaultsHelpFormatter;
+module.exports.RawDescriptionHelpFormatter =
+ require('./help/added_formatters.js').RawDescriptionHelpFormatter;
+module.exports.RawTextHelpFormatter =
+ require('./help/added_formatters.js').RawTextHelpFormatter;
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/argument/error.js b/chatto/src/main/javascript/node_modules/argparse/lib/argument/error.js
new file mode 100644
index 0000000..c8a02a0
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/argument/error.js
@@ -0,0 +1,50 @@
+'use strict';
+
+
+var format = require('util').format;
+
+
+var ERR_CODE = 'ARGError';
+
+/*:nodoc:*
+ * argumentError(argument, message) -> TypeError
+ * - argument (Object): action with broken argument
+ * - message (String): error message
+ *
+ * Error format helper. An error from creating or using an argument
+ * (optional or positional). The string value of this exception
+ * is the message, augmented with information
+ * about the argument that caused it.
+ *
+ * #####Example
+ *
+ * var argumentErrorHelper = require('./argument/error');
+ * if (conflictOptionals.length > 0) {
+ * throw argumentErrorHelper(
+ * action,
+ * format('Conflicting option string(s): %s', conflictOptionals.join(', '))
+ * );
+ * }
+ *
+ **/
+module.exports = function (argument, message) {
+ var argumentName = null;
+ var errMessage;
+ var err;
+
+ if (argument.getName) {
+ argumentName = argument.getName();
+ } else {
+ argumentName = '' + argument;
+ }
+
+ if (!argumentName) {
+ errMessage = message;
+ } else {
+ errMessage = format('argument "%s": %s', argumentName, message);
+ }
+
+ err = new TypeError(errMessage);
+ err.code = ERR_CODE;
+ return err;
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/argument/exclusive.js b/chatto/src/main/javascript/node_modules/argparse/lib/argument/exclusive.js
new file mode 100644
index 0000000..8287e00
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/argument/exclusive.js
@@ -0,0 +1,54 @@
+/** internal
+ * class MutuallyExclusiveGroup
+ *
+ * Group arguments.
+ * By default, ArgumentParser groups command-line arguments
+ * into “positional arguments” and “optional arguments”
+ * when displaying help messages. When there is a better
+ * conceptual grouping of arguments than this default one,
+ * appropriate groups can be created using the addArgumentGroup() method
+ *
+ * This class inherited from [[ArgumentContainer]]
+ **/
+'use strict';
+
+var util = require('util');
+
+var ArgumentGroup = require('./group');
+
+/**
+ * new MutuallyExclusiveGroup(container, options)
+ * - container (object): main container
+ * - options (object): options.required -> true/false
+ *
+ * `required` could be an argument itself, but making it a property of
+ * the options argument is more consistent with the JS adaptation of the Python)
+ **/
+var MutuallyExclusiveGroup = module.exports = function MutuallyExclusiveGroup(container, options) {
+ var required;
+ options = options || {};
+ required = options.required || false;
+ ArgumentGroup.call(this, container);
+ this.required = required;
+
+};
+util.inherits(MutuallyExclusiveGroup, ArgumentGroup);
+
+
+MutuallyExclusiveGroup.prototype._addAction = function (action) {
+ var msg;
+ if (action.required) {
+ msg = 'mutually exclusive arguments must be optional';
+ throw new Error(msg);
+ }
+ action = this._container._addAction(action);
+ this._groupActions.push(action);
+ return action;
+};
+
+
+MutuallyExclusiveGroup.prototype._removeAction = function (action) {
+ this._container._removeAction(action);
+ this._groupActions.remove(action);
+};
+
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/argument/group.js b/chatto/src/main/javascript/node_modules/argparse/lib/argument/group.js
new file mode 100644
index 0000000..58b271f
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/argument/group.js
@@ -0,0 +1,75 @@
+/** internal
+ * class ArgumentGroup
+ *
+ * Group arguments.
+ * By default, ArgumentParser groups command-line arguments
+ * into “positional arguments” and “optional arguments”
+ * when displaying help messages. When there is a better
+ * conceptual grouping of arguments than this default one,
+ * appropriate groups can be created using the addArgumentGroup() method
+ *
+ * This class inherited from [[ArgumentContainer]]
+ **/
+'use strict';
+
+var util = require('util');
+
+var ActionContainer = require('../action_container');
+
+
+/**
+ * new ArgumentGroup(container, options)
+ * - container (object): main container
+ * - options (object): hash of group options
+ *
+ * #### options
+ * - **prefixChars** group name prefix
+ * - **argumentDefault** default argument value
+ * - **title** group title
+ * - **description** group description
+ *
+ **/
+var ArgumentGroup = module.exports = function ArgumentGroup(container, options) {
+
+ options = options || {};
+
+ // add any missing keyword arguments by checking the container
+ options.conflictHandler = (options.conflictHandler || container.conflictHandler);
+ options.prefixChars = (options.prefixChars || container.prefixChars);
+ options.argumentDefault = (options.argumentDefault || container.argumentDefault);
+
+ ActionContainer.call(this, options);
+
+ // group attributes
+ this.title = options.title;
+ this._groupActions = [];
+
+ // share most attributes with the container
+ this._container = container;
+ this._registries = container._registries;
+ this._actions = container._actions;
+ this._optionStringActions = container._optionStringActions;
+ this._defaults = container._defaults;
+ this._hasNegativeNumberOptionals = container._hasNegativeNumberOptionals;
+ this._mutuallyExclusiveGroups = container._mutuallyExclusiveGroups;
+};
+util.inherits(ArgumentGroup, ActionContainer);
+
+
+ArgumentGroup.prototype._addAction = function (action) {
+ // Parent add action
+ action = ActionContainer.prototype._addAction.call(this, action);
+ this._groupActions.push(action);
+ return action;
+};
+
+
+ArgumentGroup.prototype._removeAction = function (action) {
+ // Parent remove action
+ ActionContainer.prototype._removeAction.call(this, action);
+ var actionIndex = this._groupActions.indexOf(action);
+ if (actionIndex >= 0) {
+ this._groupActions.splice(actionIndex, 1);
+ }
+};
+
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/argument_parser.js b/chatto/src/main/javascript/node_modules/argparse/lib/argument_parser.js
new file mode 100644
index 0000000..bd9a59a
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/argument_parser.js
@@ -0,0 +1,1161 @@
+/**
+ * class ArgumentParser
+ *
+ * Object for parsing command line strings into js objects.
+ *
+ * Inherited from [[ActionContainer]]
+ **/
+'use strict';
+
+var util = require('util');
+var format = require('util').format;
+var Path = require('path');
+var sprintf = require('sprintf-js').sprintf;
+
+// Constants
+var c = require('./const');
+
+var $$ = require('./utils');
+
+var ActionContainer = require('./action_container');
+
+// Errors
+var argumentErrorHelper = require('./argument/error');
+
+var HelpFormatter = require('./help/formatter');
+
+var Namespace = require('./namespace');
+
+
+/**
+ * new ArgumentParser(options)
+ *
+ * Create a new ArgumentParser object.
+ *
+ * ##### Options:
+ * - `prog` The name of the program (default: Path.basename(process.argv[1]))
+ * - `usage` A usage message (default: auto-generated from arguments)
+ * - `description` A description of what the program does
+ * - `epilog` Text following the argument descriptions
+ * - `parents` Parsers whose arguments should be copied into this one
+ * - `formatterClass` HelpFormatter class for printing help messages
+ * - `prefixChars` Characters that prefix optional arguments
+ * - `fromfilePrefixChars` Characters that prefix files containing additional arguments
+ * - `argumentDefault` The default value for all arguments
+ * - `addHelp` Add a -h/-help option
+ * - `conflictHandler` Specifies how to handle conflicting argument names
+ * - `debug` Enable debug mode. Argument errors throw exception in
+ * debug mode and process.exit in normal. Used for development and
+ * testing (default: false)
+ *
+ * See also [original guide][1]
+ *
+ * [1]:http://docs.python.org/dev/library/argparse.html#argumentparser-objects
+ **/
+function ArgumentParser(options) {
+ if (!(this instanceof ArgumentParser)) {
+ return new ArgumentParser(options);
+ }
+ var self = this;
+ options = options || {};
+
+ options.description = (options.description || null);
+ options.argumentDefault = (options.argumentDefault || null);
+ options.prefixChars = (options.prefixChars || '-');
+ options.conflictHandler = (options.conflictHandler || 'error');
+ ActionContainer.call(this, options);
+
+ options.addHelp = typeof options.addHelp === 'undefined' || !!options.addHelp;
+ options.parents = options.parents || [];
+ // default program name
+ options.prog = (options.prog || Path.basename(process.argv[1]));
+ this.prog = options.prog;
+ this.usage = options.usage;
+ this.epilog = options.epilog;
+ this.version = options.version;
+
+ this.debug = (options.debug === true);
+
+ this.formatterClass = (options.formatterClass || HelpFormatter);
+ this.fromfilePrefixChars = options.fromfilePrefixChars || null;
+ this._positionals = this.addArgumentGroup({ title: 'Positional arguments' });
+ this._optionals = this.addArgumentGroup({ title: 'Optional arguments' });
+ this._subparsers = null;
+
+ // register types
+ function FUNCTION_IDENTITY(o) {
+ return o;
+ }
+ this.register('type', 'auto', FUNCTION_IDENTITY);
+ this.register('type', null, FUNCTION_IDENTITY);
+ this.register('type', 'int', function (x) {
+ var result = parseInt(x, 10);
+ if (isNaN(result)) {
+ throw new Error(x + ' is not a valid integer.');
+ }
+ return result;
+ });
+ this.register('type', 'float', function (x) {
+ var result = parseFloat(x);
+ if (isNaN(result)) {
+ throw new Error(x + ' is not a valid float.');
+ }
+ return result;
+ });
+ this.register('type', 'string', function (x) {
+ return '' + x;
+ });
+
+ // add help and version arguments if necessary
+ var defaultPrefix = (this.prefixChars.indexOf('-') > -1) ? '-' : this.prefixChars[0];
+ if (options.addHelp) {
+ this.addArgument(
+ [ defaultPrefix + 'h', defaultPrefix + defaultPrefix + 'help' ],
+ {
+ action: 'help',
+ defaultValue: c.SUPPRESS,
+ help: 'Show this help message and exit.'
+ }
+ );
+ }
+ if (typeof this.version !== 'undefined') {
+ this.addArgument(
+ [ defaultPrefix + 'v', defaultPrefix + defaultPrefix + 'version' ],
+ {
+ action: 'version',
+ version: this.version,
+ defaultValue: c.SUPPRESS,
+ help: "Show program's version number and exit."
+ }
+ );
+ }
+
+ // add parent arguments and defaults
+ options.parents.forEach(function (parent) {
+ self._addContainerActions(parent);
+ if (typeof parent._defaults !== 'undefined') {
+ for (var defaultKey in parent._defaults) {
+ if (parent._defaults.hasOwnProperty(defaultKey)) {
+ self._defaults[defaultKey] = parent._defaults[defaultKey];
+ }
+ }
+ }
+ });
+}
+
+util.inherits(ArgumentParser, ActionContainer);
+
+/**
+ * ArgumentParser#addSubparsers(options) -> [[ActionSubparsers]]
+ * - options (object): hash of options see [[ActionSubparsers.new]]
+ *
+ * See also [subcommands][1]
+ *
+ * [1]:http://docs.python.org/dev/library/argparse.html#sub-commands
+ **/
+ArgumentParser.prototype.addSubparsers = function (options) {
+ if (this._subparsers) {
+ this.error('Cannot have multiple subparser arguments.');
+ }
+
+ options = options || {};
+ options.debug = (this.debug === true);
+ options.optionStrings = [];
+ options.parserClass = (options.parserClass || ArgumentParser);
+
+
+ if (!!options.title || !!options.description) {
+
+ this._subparsers = this.addArgumentGroup({
+ title: (options.title || 'subcommands'),
+ description: options.description
+ });
+ delete options.title;
+ delete options.description;
+
+ } else {
+ this._subparsers = this._positionals;
+ }
+
+ // prog defaults to the usage message of this parser, skipping
+ // optional arguments and with no "usage:" prefix
+ if (!options.prog) {
+ var formatter = this._getFormatter();
+ var positionals = this._getPositionalActions();
+ var groups = this._mutuallyExclusiveGroups;
+ formatter.addUsage(this.usage, positionals, groups, '');
+ options.prog = formatter.formatHelp().trim();
+ }
+
+ // create the parsers action and add it to the positionals list
+ var ParsersClass = this._popActionClass(options, 'parsers');
+ var action = new ParsersClass(options);
+ this._subparsers._addAction(action);
+
+ // return the created parsers action
+ return action;
+};
+
+ArgumentParser.prototype._addAction = function (action) {
+ if (action.isOptional()) {
+ this._optionals._addAction(action);
+ } else {
+ this._positionals._addAction(action);
+ }
+ return action;
+};
+
+ArgumentParser.prototype._getOptionalActions = function () {
+ return this._actions.filter(function (action) {
+ return action.isOptional();
+ });
+};
+
+ArgumentParser.prototype._getPositionalActions = function () {
+ return this._actions.filter(function (action) {
+ return action.isPositional();
+ });
+};
+
+
+/**
+ * ArgumentParser#parseArgs(args, namespace) -> Namespace|Object
+ * - args (array): input elements
+ * - namespace (Namespace|Object): result object
+ *
+ * Parsed args and throws error if some arguments are not recognized
+ *
+ * See also [original guide][1]
+ *
+ * [1]:http://docs.python.org/dev/library/argparse.html#the-parse-args-method
+ **/
+ArgumentParser.prototype.parseArgs = function (args, namespace) {
+ var argv;
+ var result = this.parseKnownArgs(args, namespace);
+
+ args = result[0];
+ argv = result[1];
+ if (argv && argv.length > 0) {
+ this.error(
+ format('Unrecognized arguments: %s.', argv.join(' '))
+ );
+ }
+ return args;
+};
+
+/**
+ * ArgumentParser#parseKnownArgs(args, namespace) -> array
+ * - args (array): input options
+ * - namespace (Namespace|Object): result object
+ *
+ * Parse known arguments and return tuple of result object
+ * and unknown args
+ *
+ * See also [original guide][1]
+ *
+ * [1]:http://docs.python.org/dev/library/argparse.html#partial-parsing
+ **/
+ArgumentParser.prototype.parseKnownArgs = function (args, namespace) {
+ var self = this;
+
+ // args default to the system args
+ args = args || process.argv.slice(2);
+
+ // default Namespace built from parser defaults
+ namespace = namespace || new Namespace();
+
+ self._actions.forEach(function (action) {
+ if (action.dest !== c.SUPPRESS) {
+ if (!$$.has(namespace, action.dest)) {
+ if (action.defaultValue !== c.SUPPRESS) {
+ var defaultValue = action.defaultValue;
+ if (typeof action.defaultValue === 'string') {
+ defaultValue = self._getValue(action, defaultValue);
+ }
+ namespace[action.dest] = defaultValue;
+ }
+ }
+ }
+ });
+
+ Object.keys(self._defaults).forEach(function (dest) {
+ namespace[dest] = self._defaults[dest];
+ });
+
+ // parse the arguments and exit if there are any errors
+ try {
+ var res = this._parseKnownArgs(args, namespace);
+
+ namespace = res[0];
+ args = res[1];
+ if ($$.has(namespace, c._UNRECOGNIZED_ARGS_ATTR)) {
+ args = $$.arrayUnion(args, namespace[c._UNRECOGNIZED_ARGS_ATTR]);
+ delete namespace[c._UNRECOGNIZED_ARGS_ATTR];
+ }
+ return [ namespace, args ];
+ } catch (e) {
+ this.error(e);
+ }
+};
+
+ArgumentParser.prototype._parseKnownArgs = function (argStrings, namespace) {
+ var self = this;
+
+ var extras = [];
+
+ // replace arg strings that are file references
+ if (this.fromfilePrefixChars !== null) {
+ argStrings = this._readArgsFromFiles(argStrings);
+ }
+ // map all mutually exclusive arguments to the other arguments
+ // they can't occur with
+ // Python has 'conflicts = action_conflicts.setdefault(mutex_action, [])'
+ // though I can't conceive of a way in which an action could be a member
+ // of two different mutually exclusive groups.
+
+ function actionHash(action) {
+ // some sort of hashable key for this action
+ // action itself cannot be a key in actionConflicts
+ // I think getName() (join of optionStrings) is unique enough
+ return action.getName();
+ }
+
+ var conflicts, key;
+ var actionConflicts = {};
+
+ this._mutuallyExclusiveGroups.forEach(function (mutexGroup) {
+ mutexGroup._groupActions.forEach(function (mutexAction, i, groupActions) {
+ key = actionHash(mutexAction);
+ if (!$$.has(actionConflicts, key)) {
+ actionConflicts[key] = [];
+ }
+ conflicts = actionConflicts[key];
+ conflicts.push.apply(conflicts, groupActions.slice(0, i));
+ conflicts.push.apply(conflicts, groupActions.slice(i + 1));
+ });
+ });
+
+ // find all option indices, and determine the arg_string_pattern
+ // which has an 'O' if there is an option at an index,
+ // an 'A' if there is an argument, or a '-' if there is a '--'
+ var optionStringIndices = {};
+
+ var argStringPatternParts = [];
+
+ argStrings.forEach(function (argString, argStringIndex) {
+ if (argString === '--') {
+ argStringPatternParts.push('-');
+ while (argStringIndex < argStrings.length) {
+ argStringPatternParts.push('A');
+ argStringIndex++;
+ }
+ } else {
+ // otherwise, add the arg to the arg strings
+ // and note the index if it was an option
+ var pattern;
+ var optionTuple = self._parseOptional(argString);
+ if (!optionTuple) {
+ pattern = 'A';
+ } else {
+ optionStringIndices[argStringIndex] = optionTuple;
+ pattern = 'O';
+ }
+ argStringPatternParts.push(pattern);
+ }
+ });
+ var argStringsPattern = argStringPatternParts.join('');
+
+ var seenActions = [];
+ var seenNonDefaultActions = [];
+
+
+ function takeAction(action, argumentStrings, optionString) {
+ seenActions.push(action);
+ var argumentValues = self._getValues(action, argumentStrings);
+
+ // error if this argument is not allowed with other previously
+ // seen arguments, assuming that actions that use the default
+ // value don't really count as "present"
+ if (argumentValues !== action.defaultValue) {
+ seenNonDefaultActions.push(action);
+ if (actionConflicts[actionHash(action)]) {
+ actionConflicts[actionHash(action)].forEach(function (actionConflict) {
+ if (seenNonDefaultActions.indexOf(actionConflict) >= 0) {
+ throw argumentErrorHelper(
+ action,
+ format('Not allowed with argument "%s".', actionConflict.getName())
+ );
+ }
+ });
+ }
+ }
+
+ if (argumentValues !== c.SUPPRESS) {
+ action.call(self, namespace, argumentValues, optionString);
+ }
+ }
+
+ function consumeOptional(startIndex) {
+ // get the optional identified at this index
+ var optionTuple = optionStringIndices[startIndex];
+ var action = optionTuple[0];
+ var optionString = optionTuple[1];
+ var explicitArg = optionTuple[2];
+
+ // identify additional optionals in the same arg string
+ // (e.g. -xyz is the same as -x -y -z if no args are required)
+ var actionTuples = [];
+
+ var args, argCount, start, stop;
+
+ for (;;) {
+ if (!action) {
+ extras.push(argStrings[startIndex]);
+ return startIndex + 1;
+ }
+ if (explicitArg) {
+ argCount = self._matchArgument(action, 'A');
+
+ // if the action is a single-dash option and takes no
+ // arguments, try to parse more single-dash options out
+ // of the tail of the option string
+ var chars = self.prefixChars;
+ if (argCount === 0 && chars.indexOf(optionString[1]) < 0) {
+ actionTuples.push([ action, [], optionString ]);
+ optionString = optionString[0] + explicitArg[0];
+ var newExplicitArg = explicitArg.slice(1) || null;
+ var optionalsMap = self._optionStringActions;
+
+ if (Object.keys(optionalsMap).indexOf(optionString) >= 0) {
+ action = optionalsMap[optionString];
+ explicitArg = newExplicitArg;
+ } else {
+ throw argumentErrorHelper(action, sprintf('ignored explicit argument %r', explicitArg));
+ }
+ } else if (argCount === 1) {
+ // if the action expect exactly one argument, we've
+ // successfully matched the option; exit the loop
+ stop = startIndex + 1;
+ args = [ explicitArg ];
+ actionTuples.push([ action, args, optionString ]);
+ break;
+ } else {
+ // error if a double-dash option did not use the
+ // explicit argument
+ throw argumentErrorHelper(action, sprintf('ignored explicit argument %r', explicitArg));
+ }
+ } else {
+ // if there is no explicit argument, try to match the
+ // optional's string arguments with the following strings
+ // if successful, exit the loop
+
+ start = startIndex + 1;
+ var selectedPatterns = argStringsPattern.substr(start);
+
+ argCount = self._matchArgument(action, selectedPatterns);
+ stop = start + argCount;
+
+
+ args = argStrings.slice(start, stop);
+
+ actionTuples.push([ action, args, optionString ]);
+ break;
+ }
+
+ }
+
+ // add the Optional to the list and return the index at which
+ // the Optional's string args stopped
+ if (actionTuples.length < 1) {
+ throw new Error('length should be > 0');
+ }
+ for (var i = 0; i < actionTuples.length; i++) {
+ takeAction.apply(self, actionTuples[i]);
+ }
+ return stop;
+ }
+
+ // the list of Positionals left to be parsed; this is modified
+ // by consume_positionals()
+ var positionals = self._getPositionalActions();
+
+ function consumePositionals(startIndex) {
+ // match as many Positionals as possible
+ var selectedPattern = argStringsPattern.substr(startIndex);
+ var argCounts = self._matchArgumentsPartial(positionals, selectedPattern);
+
+ // slice off the appropriate arg strings for each Positional
+ // and add the Positional and its args to the list
+ for (var i = 0; i < positionals.length; i++) {
+ var action = positionals[i];
+ var argCount = argCounts[i];
+ if (typeof argCount === 'undefined') {
+ continue;
+ }
+ var args = argStrings.slice(startIndex, startIndex + argCount);
+
+ startIndex += argCount;
+ takeAction(action, args);
+ }
+
+ // slice off the Positionals that we just parsed and return the
+ // index at which the Positionals' string args stopped
+ positionals = positionals.slice(argCounts.length);
+ return startIndex;
+ }
+
+ // consume Positionals and Optionals alternately, until we have
+ // passed the last option string
+ var startIndex = 0;
+ var position;
+
+ var maxOptionStringIndex = -1;
+
+ Object.keys(optionStringIndices).forEach(function (position) {
+ maxOptionStringIndex = Math.max(maxOptionStringIndex, parseInt(position, 10));
+ });
+
+ var positionalsEndIndex, nextOptionStringIndex;
+
+ while (startIndex <= maxOptionStringIndex) {
+ // consume any Positionals preceding the next option
+ nextOptionStringIndex = null;
+ for (position in optionStringIndices) {
+ if (!optionStringIndices.hasOwnProperty(position)) { continue; }
+
+ position = parseInt(position, 10);
+ if (position >= startIndex) {
+ if (nextOptionStringIndex !== null) {
+ nextOptionStringIndex = Math.min(nextOptionStringIndex, position);
+ } else {
+ nextOptionStringIndex = position;
+ }
+ }
+ }
+
+ if (startIndex !== nextOptionStringIndex) {
+ positionalsEndIndex = consumePositionals(startIndex);
+ // only try to parse the next optional if we didn't consume
+ // the option string during the positionals parsing
+ if (positionalsEndIndex > startIndex) {
+ startIndex = positionalsEndIndex;
+ continue;
+ } else {
+ startIndex = positionalsEndIndex;
+ }
+ }
+
+ // if we consumed all the positionals we could and we're not
+ // at the index of an option string, there were extra arguments
+ if (!optionStringIndices[startIndex]) {
+ var strings = argStrings.slice(startIndex, nextOptionStringIndex);
+ extras = extras.concat(strings);
+ startIndex = nextOptionStringIndex;
+ }
+ // consume the next optional and any arguments for it
+ startIndex = consumeOptional(startIndex);
+ }
+
+ // consume any positionals following the last Optional
+ var stopIndex = consumePositionals(startIndex);
+
+ // if we didn't consume all the argument strings, there were extras
+ extras = extras.concat(argStrings.slice(stopIndex));
+
+ // if we didn't use all the Positional objects, there were too few
+ // arg strings supplied.
+ if (positionals.length > 0) {
+ self.error('too few arguments');
+ }
+
+ // make sure all required actions were present
+ self._actions.forEach(function (action) {
+ if (action.required) {
+ if (seenActions.indexOf(action) < 0) {
+ self.error(format('Argument "%s" is required', action.getName()));
+ }
+ }
+ });
+
+ // make sure all required groups have one option present
+ var actionUsed = false;
+ self._mutuallyExclusiveGroups.forEach(function (group) {
+ if (group.required) {
+ actionUsed = group._groupActions.some(function (action) {
+ return seenNonDefaultActions.indexOf(action) !== -1;
+ });
+
+ // if no actions were used, report the error
+ if (!actionUsed) {
+ var names = [];
+ group._groupActions.forEach(function (action) {
+ if (action.help !== c.SUPPRESS) {
+ names.push(action.getName());
+ }
+ });
+ names = names.join(' ');
+ var msg = 'one of the arguments ' + names + ' is required';
+ self.error(msg);
+ }
+ }
+ });
+
+ // return the updated namespace and the extra arguments
+ return [ namespace, extras ];
+};
+
+ArgumentParser.prototype._readArgsFromFiles = function (argStrings) {
+ // expand arguments referencing files
+ var self = this;
+ var fs = require('fs');
+ var newArgStrings = [];
+ argStrings.forEach(function (argString) {
+ if (self.fromfilePrefixChars.indexOf(argString[0]) < 0) {
+ // for regular arguments, just add them back into the list
+ newArgStrings.push(argString);
+ } else {
+ // replace arguments referencing files with the file content
+ try {
+ var argstrs = [];
+ var filename = argString.slice(1);
+ var content = fs.readFileSync(filename, 'utf8');
+ content = content.trim().split('\n');
+ content.forEach(function (argLine) {
+ self.convertArgLineToArgs(argLine).forEach(function (arg) {
+ argstrs.push(arg);
+ });
+ argstrs = self._readArgsFromFiles(argstrs);
+ });
+ newArgStrings.push.apply(newArgStrings, argstrs);
+ } catch (error) {
+ return self.error(error.message);
+ }
+ }
+ });
+ return newArgStrings;
+};
+
+ArgumentParser.prototype.convertArgLineToArgs = function (argLine) {
+ return [ argLine ];
+};
+
+ArgumentParser.prototype._matchArgument = function (action, regexpArgStrings) {
+
+ // match the pattern for this action to the arg strings
+ var regexpNargs = new RegExp('^' + this._getNargsPattern(action));
+ var matches = regexpArgStrings.match(regexpNargs);
+ var message;
+
+ // throw an exception if we weren't able to find a match
+ if (!matches) {
+ switch (action.nargs) {
+ /*eslint-disable no-undefined*/
+ case undefined:
+ case null:
+ message = 'Expected one argument.';
+ break;
+ case c.OPTIONAL:
+ message = 'Expected at most one argument.';
+ break;
+ case c.ONE_OR_MORE:
+ message = 'Expected at least one argument.';
+ break;
+ default:
+ message = 'Expected %s argument(s)';
+ }
+
+ throw argumentErrorHelper(
+ action,
+ format(message, action.nargs)
+ );
+ }
+ // return the number of arguments matched
+ return matches[1].length;
+};
+
+ArgumentParser.prototype._matchArgumentsPartial = function (actions, regexpArgStrings) {
+ // progressively shorten the actions list by slicing off the
+ // final actions until we find a match
+ var self = this;
+ var result = [];
+ var actionSlice, pattern, matches;
+ var i, j;
+
+ function getLength(string) {
+ return string.length;
+ }
+
+ for (i = actions.length; i > 0; i--) {
+ pattern = '';
+ actionSlice = actions.slice(0, i);
+ for (j = 0; j < actionSlice.length; j++) {
+ pattern += self._getNargsPattern(actionSlice[j]);
+ }
+
+ pattern = new RegExp('^' + pattern);
+ matches = regexpArgStrings.match(pattern);
+
+ if (matches && matches.length > 0) {
+ // need only groups
+ matches = matches.splice(1);
+ result = result.concat(matches.map(getLength));
+ break;
+ }
+ }
+
+ // return the list of arg string counts
+ return result;
+};
+
+ArgumentParser.prototype._parseOptional = function (argString) {
+ var action, optionString, argExplicit, optionTuples;
+
+ // if it's an empty string, it was meant to be a positional
+ if (!argString) {
+ return null;
+ }
+
+ // if it doesn't start with a prefix, it was meant to be positional
+ if (this.prefixChars.indexOf(argString[0]) < 0) {
+ return null;
+ }
+
+ // if the option string is present in the parser, return the action
+ if (this._optionStringActions[argString]) {
+ return [ this._optionStringActions[argString], argString, null ];
+ }
+
+ // if it's just a single character, it was meant to be positional
+ if (argString.length === 1) {
+ return null;
+ }
+
+ // if the option string before the "=" is present, return the action
+ if (argString.indexOf('=') >= 0) {
+ optionString = argString.split('=', 1)[0];
+ argExplicit = argString.slice(optionString.length + 1);
+
+ if (this._optionStringActions[optionString]) {
+ action = this._optionStringActions[optionString];
+ return [ action, optionString, argExplicit ];
+ }
+ }
+
+ // search through all possible prefixes of the option string
+ // and all actions in the parser for possible interpretations
+ optionTuples = this._getOptionTuples(argString);
+
+ // if multiple actions match, the option string was ambiguous
+ if (optionTuples.length > 1) {
+ var optionStrings = optionTuples.map(function (optionTuple) {
+ return optionTuple[1];
+ });
+ this.error(format(
+ 'Ambiguous option: "%s" could match %s.',
+ argString, optionStrings.join(', ')
+ ));
+ // if exactly one action matched, this segmentation is good,
+ // so return the parsed action
+ } else if (optionTuples.length === 1) {
+ return optionTuples[0];
+ }
+
+ // if it was not found as an option, but it looks like a negative
+ // number, it was meant to be positional
+ // unless there are negative-number-like options
+ if (argString.match(this._regexpNegativeNumber)) {
+ if (!this._hasNegativeNumberOptionals.some(Boolean)) {
+ return null;
+ }
+ }
+ // if it contains a space, it was meant to be a positional
+ if (argString.search(' ') >= 0) {
+ return null;
+ }
+
+ // it was meant to be an optional but there is no such option
+ // in this parser (though it might be a valid option in a subparser)
+ return [ null, argString, null ];
+};
+
+ArgumentParser.prototype._getOptionTuples = function (optionString) {
+ var result = [];
+ var chars = this.prefixChars;
+ var optionPrefix;
+ var argExplicit;
+ var action;
+ var actionOptionString;
+
+ // option strings starting with two prefix characters are only split at
+ // the '='
+ if (chars.indexOf(optionString[0]) >= 0 && chars.indexOf(optionString[1]) >= 0) {
+ if (optionString.indexOf('=') >= 0) {
+ var optionStringSplit = optionString.split('=', 1);
+
+ optionPrefix = optionStringSplit[0];
+ argExplicit = optionStringSplit[1];
+ } else {
+ optionPrefix = optionString;
+ argExplicit = null;
+ }
+
+ for (actionOptionString in this._optionStringActions) {
+ if (actionOptionString.substr(0, optionPrefix.length) === optionPrefix) {
+ action = this._optionStringActions[actionOptionString];
+ result.push([ action, actionOptionString, argExplicit ]);
+ }
+ }
+
+ // single character options can be concatenated with their arguments
+ // but multiple character options always have to have their argument
+ // separate
+ } else if (chars.indexOf(optionString[0]) >= 0 && chars.indexOf(optionString[1]) < 0) {
+ optionPrefix = optionString;
+ argExplicit = null;
+ var optionPrefixShort = optionString.substr(0, 2);
+ var argExplicitShort = optionString.substr(2);
+
+ for (actionOptionString in this._optionStringActions) {
+ if (!$$.has(this._optionStringActions, actionOptionString)) continue;
+
+ action = this._optionStringActions[actionOptionString];
+ if (actionOptionString === optionPrefixShort) {
+ result.push([ action, actionOptionString, argExplicitShort ]);
+ } else if (actionOptionString.substr(0, optionPrefix.length) === optionPrefix) {
+ result.push([ action, actionOptionString, argExplicit ]);
+ }
+ }
+
+ // shouldn't ever get here
+ } else {
+ throw new Error(format('Unexpected option string: %s.', optionString));
+ }
+ // return the collected option tuples
+ return result;
+};
+
+ArgumentParser.prototype._getNargsPattern = function (action) {
+ // in all examples below, we have to allow for '--' args
+ // which are represented as '-' in the pattern
+ var regexpNargs;
+
+ switch (action.nargs) {
+ // the default (null) is assumed to be a single argument
+ case undefined:
+ case null:
+ regexpNargs = '(-*A-*)';
+ break;
+ // allow zero or more arguments
+ case c.OPTIONAL:
+ regexpNargs = '(-*A?-*)';
+ break;
+ // allow zero or more arguments
+ case c.ZERO_OR_MORE:
+ regexpNargs = '(-*[A-]*)';
+ break;
+ // allow one or more arguments
+ case c.ONE_OR_MORE:
+ regexpNargs = '(-*A[A-]*)';
+ break;
+ // allow any number of options or arguments
+ case c.REMAINDER:
+ regexpNargs = '([-AO]*)';
+ break;
+ // allow one argument followed by any number of options or arguments
+ case c.PARSER:
+ regexpNargs = '(-*A[-AO]*)';
+ break;
+ // all others should be integers
+ default:
+ regexpNargs = '(-*' + $$.repeat('-*A', action.nargs) + '-*)';
+ }
+
+ // if this is an optional action, -- is not allowed
+ if (action.isOptional()) {
+ regexpNargs = regexpNargs.replace(/-\*/g, '');
+ regexpNargs = regexpNargs.replace(/-/g, '');
+ }
+
+ // return the pattern
+ return regexpNargs;
+};
+
+//
+// Value conversion methods
+//
+
+ArgumentParser.prototype._getValues = function (action, argStrings) {
+ var self = this;
+
+ // for everything but PARSER args, strip out '--'
+ if (action.nargs !== c.PARSER && action.nargs !== c.REMAINDER) {
+ argStrings = argStrings.filter(function (arrayElement) {
+ return arrayElement !== '--';
+ });
+ }
+
+ var value, argString;
+
+ // optional argument produces a default when not present
+ if (argStrings.length === 0 && action.nargs === c.OPTIONAL) {
+
+ value = (action.isOptional()) ? action.constant : action.defaultValue;
+
+ if (typeof (value) === 'string') {
+ value = this._getValue(action, value);
+ this._checkValue(action, value);
+ }
+
+ // when nargs='*' on a positional, if there were no command-line
+ // args, use the default if it is anything other than None
+ } else if (argStrings.length === 0 && action.nargs === c.ZERO_OR_MORE &&
+ action.optionStrings.length === 0) {
+
+ value = (action.defaultValue || argStrings);
+ this._checkValue(action, value);
+
+ // single argument or optional argument produces a single value
+ } else if (argStrings.length === 1 &&
+ (!action.nargs || action.nargs === c.OPTIONAL)) {
+
+ argString = argStrings[0];
+ value = this._getValue(action, argString);
+ this._checkValue(action, value);
+
+ // REMAINDER arguments convert all values, checking none
+ } else if (action.nargs === c.REMAINDER) {
+ value = argStrings.map(function (v) {
+ return self._getValue(action, v);
+ });
+
+ // PARSER arguments convert all values, but check only the first
+ } else if (action.nargs === c.PARSER) {
+ value = argStrings.map(function (v) {
+ return self._getValue(action, v);
+ });
+ this._checkValue(action, value[0]);
+
+ // all other types of nargs produce a list
+ } else {
+ value = argStrings.map(function (v) {
+ return self._getValue(action, v);
+ });
+ value.forEach(function (v) {
+ self._checkValue(action, v);
+ });
+ }
+
+ // return the converted value
+ return value;
+};
+
+ArgumentParser.prototype._getValue = function (action, argString) {
+ var result;
+
+ var typeFunction = this._registryGet('type', action.type, action.type);
+ if (typeof typeFunction !== 'function') {
+ var message = format('%s is not callable', typeFunction);
+ throw argumentErrorHelper(action, message);
+ }
+
+ // convert the value to the appropriate type
+ try {
+ result = typeFunction(argString);
+
+ // ArgumentTypeErrors indicate errors
+ // If action.type is not a registered string, it is a function
+ // Try to deduce its name for inclusion in the error message
+ // Failing that, include the error message it raised.
+ } catch (e) {
+ var name = null;
+ if (typeof action.type === 'string') {
+ name = action.type;
+ } else {
+ name = action.type.name || action.type.displayName || '';
+ }
+ var msg = format('Invalid %s value: %s', name, argString);
+ if (name === '') { msg += '\n' + e.message; }
+ throw argumentErrorHelper(action, msg);
+ }
+ // return the converted value
+ return result;
+};
+
+ArgumentParser.prototype._checkValue = function (action, value) {
+ // converted value must be one of the choices (if specified)
+ var choices = action.choices;
+ if (choices) {
+ // choise for argument can by array or string
+ if ((typeof choices === 'string' || Array.isArray(choices)) &&
+ choices.indexOf(value) !== -1) {
+ return;
+ }
+ // choise for subparsers can by only hash
+ if (typeof choices === 'object' && !Array.isArray(choices) && choices[value]) {
+ return;
+ }
+
+ if (typeof choices === 'string') {
+ choices = choices.split('').join(', ');
+ } else if (Array.isArray(choices)) {
+ choices = choices.join(', ');
+ } else {
+ choices = Object.keys(choices).join(', ');
+ }
+ var message = format('Invalid choice: %s (choose from [%s])', value, choices);
+ throw argumentErrorHelper(action, message);
+ }
+};
+
+//
+// Help formatting methods
+//
+
+/**
+ * ArgumentParser#formatUsage -> string
+ *
+ * Return usage string
+ *
+ * See also [original guide][1]
+ *
+ * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
+ **/
+ArgumentParser.prototype.formatUsage = function () {
+ var formatter = this._getFormatter();
+ formatter.addUsage(this.usage, this._actions, this._mutuallyExclusiveGroups);
+ return formatter.formatHelp();
+};
+
+/**
+ * ArgumentParser#formatHelp -> string
+ *
+ * Return help
+ *
+ * See also [original guide][1]
+ *
+ * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
+ **/
+ArgumentParser.prototype.formatHelp = function () {
+ var formatter = this._getFormatter();
+
+ // usage
+ formatter.addUsage(this.usage, this._actions, this._mutuallyExclusiveGroups);
+
+ // description
+ formatter.addText(this.description);
+
+ // positionals, optionals and user-defined groups
+ this._actionGroups.forEach(function (actionGroup) {
+ formatter.startSection(actionGroup.title);
+ formatter.addText(actionGroup.description);
+ formatter.addArguments(actionGroup._groupActions);
+ formatter.endSection();
+ });
+
+ // epilog
+ formatter.addText(this.epilog);
+
+ // determine help from format above
+ return formatter.formatHelp();
+};
+
+ArgumentParser.prototype._getFormatter = function () {
+ var FormatterClass = this.formatterClass;
+ var formatter = new FormatterClass({ prog: this.prog });
+ return formatter;
+};
+
+//
+// Print functions
+//
+
+/**
+ * ArgumentParser#printUsage() -> Void
+ *
+ * Print usage
+ *
+ * See also [original guide][1]
+ *
+ * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
+ **/
+ArgumentParser.prototype.printUsage = function () {
+ this._printMessage(this.formatUsage());
+};
+
+/**
+ * ArgumentParser#printHelp() -> Void
+ *
+ * Print help
+ *
+ * See also [original guide][1]
+ *
+ * [1]:http://docs.python.org/dev/library/argparse.html#printing-help
+ **/
+ArgumentParser.prototype.printHelp = function () {
+ this._printMessage(this.formatHelp());
+};
+
+ArgumentParser.prototype._printMessage = function (message, stream) {
+ if (!stream) {
+ stream = process.stdout;
+ }
+ if (message) {
+ stream.write('' + message);
+ }
+};
+
+//
+// Exit functions
+//
+
+/**
+ * ArgumentParser#exit(status=0, message) -> Void
+ * - status (int): exit status
+ * - message (string): message
+ *
+ * Print message in stderr/stdout and exit program
+ **/
+ArgumentParser.prototype.exit = function (status, message) {
+ if (message) {
+ if (status === 0) {
+ this._printMessage(message);
+ } else {
+ this._printMessage(message, process.stderr);
+ }
+ }
+
+ process.exit(status);
+};
+
+/**
+ * ArgumentParser#error(message) -> Void
+ * - err (Error|string): message
+ *
+ * Error method Prints a usage message incorporating the message to stderr and
+ * exits. If you override this in a subclass,
+ * it should not return -- it should
+ * either exit or throw an exception.
+ *
+ **/
+ArgumentParser.prototype.error = function (err) {
+ var message;
+ if (err instanceof Error) {
+ if (this.debug === true) {
+ throw err;
+ }
+ message = err.message;
+ } else {
+ message = err;
+ }
+ var msg = format('%s: error: %s', this.prog, message) + c.EOL;
+
+ if (this.debug === true) {
+ throw new Error(msg);
+ }
+
+ this.printUsage(process.stderr);
+
+ return this.exit(2, msg);
+};
+
+module.exports = ArgumentParser;
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/const.js b/chatto/src/main/javascript/node_modules/argparse/lib/const.js
new file mode 100644
index 0000000..b1fd4ce
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/const.js
@@ -0,0 +1,21 @@
+//
+// Constants
+//
+
+'use strict';
+
+module.exports.EOL = '\n';
+
+module.exports.SUPPRESS = '==SUPPRESS==';
+
+module.exports.OPTIONAL = '?';
+
+module.exports.ZERO_OR_MORE = '*';
+
+module.exports.ONE_OR_MORE = '+';
+
+module.exports.PARSER = 'A...';
+
+module.exports.REMAINDER = '...';
+
+module.exports._UNRECOGNIZED_ARGS_ATTR = '_unrecognized_args';
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/help/added_formatters.js b/chatto/src/main/javascript/node_modules/argparse/lib/help/added_formatters.js
new file mode 100644
index 0000000..f8e4299
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/help/added_formatters.js
@@ -0,0 +1,87 @@
+'use strict';
+
+var util = require('util');
+
+// Constants
+var c = require('../const');
+
+var $$ = require('../utils');
+var HelpFormatter = require('./formatter.js');
+
+/**
+ * new RawDescriptionHelpFormatter(options)
+ * new ArgumentParser({formatterClass: argparse.RawDescriptionHelpFormatter, ...})
+ *
+ * Help message formatter which adds default values to argument help.
+ *
+ * Only the name of this class is considered a public API. All the methods
+ * provided by the class are considered an implementation detail.
+ **/
+
+function ArgumentDefaultsHelpFormatter(options) {
+ HelpFormatter.call(this, options);
+}
+
+util.inherits(ArgumentDefaultsHelpFormatter, HelpFormatter);
+
+ArgumentDefaultsHelpFormatter.prototype._getHelpString = function (action) {
+ var help = action.help;
+ if (action.help.indexOf('%(defaultValue)s') === -1) {
+ if (action.defaultValue !== c.SUPPRESS) {
+ var defaulting_nargs = [ c.OPTIONAL, c.ZERO_OR_MORE ];
+ if (action.isOptional() || (defaulting_nargs.indexOf(action.nargs) >= 0)) {
+ help += ' (default: %(defaultValue)s)';
+ }
+ }
+ }
+ return help;
+};
+
+module.exports.ArgumentDefaultsHelpFormatter = ArgumentDefaultsHelpFormatter;
+
+/**
+ * new RawDescriptionHelpFormatter(options)
+ * new ArgumentParser({formatterClass: argparse.RawDescriptionHelpFormatter, ...})
+ *
+ * Help message formatter which retains any formatting in descriptions.
+ *
+ * Only the name of this class is considered a public API. All the methods
+ * provided by the class are considered an implementation detail.
+ **/
+
+function RawDescriptionHelpFormatter(options) {
+ HelpFormatter.call(this, options);
+}
+
+util.inherits(RawDescriptionHelpFormatter, HelpFormatter);
+
+RawDescriptionHelpFormatter.prototype._fillText = function (text, width, indent) {
+ var lines = text.split('\n');
+ lines = lines.map(function (line) {
+ return $$.trimEnd(indent + line);
+ });
+ return lines.join('\n');
+};
+module.exports.RawDescriptionHelpFormatter = RawDescriptionHelpFormatter;
+
+/**
+ * new RawTextHelpFormatter(options)
+ * new ArgumentParser({formatterClass: argparse.RawTextHelpFormatter, ...})
+ *
+ * Help message formatter which retains formatting of all help text.
+ *
+ * Only the name of this class is considered a public API. All the methods
+ * provided by the class are considered an implementation detail.
+ **/
+
+function RawTextHelpFormatter(options) {
+ RawDescriptionHelpFormatter.call(this, options);
+}
+
+util.inherits(RawTextHelpFormatter, RawDescriptionHelpFormatter);
+
+RawTextHelpFormatter.prototype._splitLines = function (text) {
+ return text.split('\n');
+};
+
+module.exports.RawTextHelpFormatter = RawTextHelpFormatter;
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/help/formatter.js b/chatto/src/main/javascript/node_modules/argparse/lib/help/formatter.js
new file mode 100644
index 0000000..29036c1
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/help/formatter.js
@@ -0,0 +1,795 @@
+/**
+ * class HelpFormatter
+ *
+ * Formatter for generating usage messages and argument help strings. Only the
+ * name of this class is considered a public API. All the methods provided by
+ * the class are considered an implementation detail.
+ *
+ * Do not call in your code, use this class only for inherits your own forvatter
+ *
+ * ToDo add [additonal formatters][1]
+ *
+ * [1]:http://docs.python.org/dev/library/argparse.html#formatter-class
+ **/
+'use strict';
+
+var sprintf = require('sprintf-js').sprintf;
+
+// Constants
+var c = require('../const');
+
+var $$ = require('../utils');
+
+
+/*:nodoc:* internal
+ * new Support(parent, heding)
+ * - parent (object): parent section
+ * - heading (string): header string
+ *
+ **/
+function Section(parent, heading) {
+ this._parent = parent;
+ this._heading = heading;
+ this._items = [];
+}
+
+/*:nodoc:* internal
+ * Section#addItem(callback) -> Void
+ * - callback (array): tuple with function and args
+ *
+ * Add function for single element
+ **/
+Section.prototype.addItem = function (callback) {
+ this._items.push(callback);
+};
+
+/*:nodoc:* internal
+ * Section#formatHelp(formatter) -> string
+ * - formatter (HelpFormatter): current formatter
+ *
+ * Form help section string
+ *
+ **/
+Section.prototype.formatHelp = function (formatter) {
+ var itemHelp, heading;
+
+ // format the indented section
+ if (this._parent) {
+ formatter._indent();
+ }
+
+ itemHelp = this._items.map(function (item) {
+ var obj, func, args;
+
+ obj = formatter;
+ func = item[0];
+ args = item[1];
+ return func.apply(obj, args);
+ });
+ itemHelp = formatter._joinParts(itemHelp);
+
+ if (this._parent) {
+ formatter._dedent();
+ }
+
+ // return nothing if the section was empty
+ if (!itemHelp) {
+ return '';
+ }
+
+ // add the heading if the section was non-empty
+ heading = '';
+ if (this._heading && this._heading !== c.SUPPRESS) {
+ var currentIndent = formatter.currentIndent;
+ heading = $$.repeat(' ', currentIndent) + this._heading + ':' + c.EOL;
+ }
+
+ // join the section-initialize newline, the heading and the help
+ return formatter._joinParts([ c.EOL, heading, itemHelp, c.EOL ]);
+};
+
+/**
+ * new HelpFormatter(options)
+ *
+ * #### Options:
+ * - `prog`: program name
+ * - `indentIncriment`: indent step, default value 2
+ * - `maxHelpPosition`: max help position, default value = 24
+ * - `width`: line width
+ *
+ **/
+var HelpFormatter = module.exports = function HelpFormatter(options) {
+ options = options || {};
+
+ this._prog = options.prog;
+
+ this._maxHelpPosition = options.maxHelpPosition || 24;
+ this._width = (options.width || ((process.env.COLUMNS || 80) - 2));
+
+ this._currentIndent = 0;
+ this._indentIncriment = options.indentIncriment || 2;
+ this._level = 0;
+ this._actionMaxLength = 0;
+
+ this._rootSection = new Section(null);
+ this._currentSection = this._rootSection;
+
+ this._whitespaceMatcher = new RegExp('\\s+', 'g');
+ this._longBreakMatcher = new RegExp(c.EOL + c.EOL + c.EOL + '+', 'g');
+};
+
+HelpFormatter.prototype._indent = function () {
+ this._currentIndent += this._indentIncriment;
+ this._level += 1;
+};
+
+HelpFormatter.prototype._dedent = function () {
+ this._currentIndent -= this._indentIncriment;
+ this._level -= 1;
+ if (this._currentIndent < 0) {
+ throw new Error('Indent decreased below 0.');
+ }
+};
+
+HelpFormatter.prototype._addItem = function (func, args) {
+ this._currentSection.addItem([ func, args ]);
+};
+
+//
+// Message building methods
+//
+
+/**
+ * HelpFormatter#startSection(heading) -> Void
+ * - heading (string): header string
+ *
+ * Start new help section
+ *
+ * See alse [code example][1]
+ *
+ * ##### Example
+ *
+ * formatter.startSection(actionGroup.title);
+ * formatter.addText(actionGroup.description);
+ * formatter.addArguments(actionGroup._groupActions);
+ * formatter.endSection();
+ *
+ **/
+HelpFormatter.prototype.startSection = function (heading) {
+ this._indent();
+ var section = new Section(this._currentSection, heading);
+ var func = section.formatHelp.bind(section);
+ this._addItem(func, [ this ]);
+ this._currentSection = section;
+};
+
+/**
+ * HelpFormatter#endSection -> Void
+ *
+ * End help section
+ *
+ * ##### Example
+ *
+ * formatter.startSection(actionGroup.title);
+ * formatter.addText(actionGroup.description);
+ * formatter.addArguments(actionGroup._groupActions);
+ * formatter.endSection();
+ **/
+HelpFormatter.prototype.endSection = function () {
+ this._currentSection = this._currentSection._parent;
+ this._dedent();
+};
+
+/**
+ * HelpFormatter#addText(text) -> Void
+ * - text (string): plain text
+ *
+ * Add plain text into current section
+ *
+ * ##### Example
+ *
+ * formatter.startSection(actionGroup.title);
+ * formatter.addText(actionGroup.description);
+ * formatter.addArguments(actionGroup._groupActions);
+ * formatter.endSection();
+ *
+ **/
+HelpFormatter.prototype.addText = function (text) {
+ if (text && text !== c.SUPPRESS) {
+ this._addItem(this._formatText, [ text ]);
+ }
+};
+
+/**
+ * HelpFormatter#addUsage(usage, actions, groups, prefix) -> Void
+ * - usage (string): usage text
+ * - actions (array): actions list
+ * - groups (array): groups list
+ * - prefix (string): usage prefix
+ *
+ * Add usage data into current section
+ *
+ * ##### Example
+ *
+ * formatter.addUsage(this.usage, this._actions, []);
+ * return formatter.formatHelp();
+ *
+ **/
+HelpFormatter.prototype.addUsage = function (usage, actions, groups, prefix) {
+ if (usage !== c.SUPPRESS) {
+ this._addItem(this._formatUsage, [ usage, actions, groups, prefix ]);
+ }
+};
+
+/**
+ * HelpFormatter#addArgument(action) -> Void
+ * - action (object): action
+ *
+ * Add argument into current section
+ *
+ * Single variant of [[HelpFormatter#addArguments]]
+ **/
+HelpFormatter.prototype.addArgument = function (action) {
+ if (action.help !== c.SUPPRESS) {
+ var self = this;
+
+ // find all invocations
+ var invocations = [ this._formatActionInvocation(action) ];
+ var invocationLength = invocations[0].length;
+
+ var actionLength;
+
+ if (action._getSubactions) {
+ this._indent();
+ action._getSubactions().forEach(function (subaction) {
+
+ var invocationNew = self._formatActionInvocation(subaction);
+ invocations.push(invocationNew);
+ invocationLength = Math.max(invocationLength, invocationNew.length);
+
+ });
+ this._dedent();
+ }
+
+ // update the maximum item length
+ actionLength = invocationLength + this._currentIndent;
+ this._actionMaxLength = Math.max(this._actionMaxLength, actionLength);
+
+ // add the item to the list
+ this._addItem(this._formatAction, [ action ]);
+ }
+};
+
+/**
+ * HelpFormatter#addArguments(actions) -> Void
+ * - actions (array): actions list
+ *
+ * Mass add arguments into current section
+ *
+ * ##### Example
+ *
+ * formatter.startSection(actionGroup.title);
+ * formatter.addText(actionGroup.description);
+ * formatter.addArguments(actionGroup._groupActions);
+ * formatter.endSection();
+ *
+ **/
+HelpFormatter.prototype.addArguments = function (actions) {
+ var self = this;
+ actions.forEach(function (action) {
+ self.addArgument(action);
+ });
+};
+
+//
+// Help-formatting methods
+//
+
+/**
+ * HelpFormatter#formatHelp -> string
+ *
+ * Format help
+ *
+ * ##### Example
+ *
+ * formatter.addText(this.epilog);
+ * return formatter.formatHelp();
+ *
+ **/
+HelpFormatter.prototype.formatHelp = function () {
+ var help = this._rootSection.formatHelp(this);
+ if (help) {
+ help = help.replace(this._longBreakMatcher, c.EOL + c.EOL);
+ help = $$.trimChars(help, c.EOL) + c.EOL;
+ }
+ return help;
+};
+
+HelpFormatter.prototype._joinParts = function (partStrings) {
+ return partStrings.filter(function (part) {
+ return (part && part !== c.SUPPRESS);
+ }).join('');
+};
+
+HelpFormatter.prototype._formatUsage = function (usage, actions, groups, prefix) {
+ if (!prefix && typeof prefix !== 'string') {
+ prefix = 'usage: ';
+ }
+
+ actions = actions || [];
+ groups = groups || [];
+
+
+ // if usage is specified, use that
+ if (usage) {
+ usage = sprintf(usage, { prog: this._prog });
+
+ // if no optionals or positionals are available, usage is just prog
+ } else if (!usage && actions.length === 0) {
+ usage = this._prog;
+
+ // if optionals and positionals are available, calculate usage
+ } else if (!usage) {
+ var prog = this._prog;
+ var optionals = [];
+ var positionals = [];
+ var actionUsage;
+ var textWidth;
+
+ // split optionals from positionals
+ actions.forEach(function (action) {
+ if (action.isOptional()) {
+ optionals.push(action);
+ } else {
+ positionals.push(action);
+ }
+ });
+
+ // build full usage string
+ actionUsage = this._formatActionsUsage([].concat(optionals, positionals), groups);
+ usage = [ prog, actionUsage ].join(' ');
+
+ // wrap the usage parts if it's too long
+ textWidth = this._width - this._currentIndent;
+ if ((prefix.length + usage.length) > textWidth) {
+
+ // break usage into wrappable parts
+ var regexpPart = new RegExp('\\(.*?\\)+|\\[.*?\\]+|\\S+', 'g');
+ var optionalUsage = this._formatActionsUsage(optionals, groups);
+ var positionalUsage = this._formatActionsUsage(positionals, groups);
+
+
+ var optionalParts = optionalUsage.match(regexpPart);
+ var positionalParts = positionalUsage.match(regexpPart) || [];
+
+ if (optionalParts.join(' ') !== optionalUsage) {
+ throw new Error('assert "optionalParts.join(\' \') === optionalUsage"');
+ }
+ if (positionalParts.join(' ') !== positionalUsage) {
+ throw new Error('assert "positionalParts.join(\' \') === positionalUsage"');
+ }
+
+ // helper for wrapping lines
+ /*eslint-disable func-style*/ // node 0.10 compat
+ var _getLines = function (parts, indent, prefix) {
+ var lines = [];
+ var line = [];
+
+ var lineLength = prefix ? prefix.length - 1 : indent.length - 1;
+
+ parts.forEach(function (part) {
+ if (lineLength + 1 + part.length > textWidth) {
+ lines.push(indent + line.join(' '));
+ line = [];
+ lineLength = indent.length - 1;
+ }
+ line.push(part);
+ lineLength += part.length + 1;
+ });
+
+ if (line) {
+ lines.push(indent + line.join(' '));
+ }
+ if (prefix) {
+ lines[0] = lines[0].substr(indent.length);
+ }
+ return lines;
+ };
+
+ var lines, indent, parts;
+ // if prog is short, follow it with optionals or positionals
+ if (prefix.length + prog.length <= 0.75 * textWidth) {
+ indent = $$.repeat(' ', (prefix.length + prog.length + 1));
+ if (optionalParts) {
+ lines = [].concat(
+ _getLines([ prog ].concat(optionalParts), indent, prefix),
+ _getLines(positionalParts, indent)
+ );
+ } else if (positionalParts) {
+ lines = _getLines([ prog ].concat(positionalParts), indent, prefix);
+ } else {
+ lines = [ prog ];
+ }
+
+ // if prog is long, put it on its own line
+ } else {
+ indent = $$.repeat(' ', prefix.length);
+ parts = optionalParts.concat(positionalParts);
+ lines = _getLines(parts, indent);
+ if (lines.length > 1) {
+ lines = [].concat(
+ _getLines(optionalParts, indent),
+ _getLines(positionalParts, indent)
+ );
+ }
+ lines = [ prog ].concat(lines);
+ }
+ // join lines into usage
+ usage = lines.join(c.EOL);
+ }
+ }
+
+ // prefix with 'usage:'
+ return prefix + usage + c.EOL + c.EOL;
+};
+
+HelpFormatter.prototype._formatActionsUsage = function (actions, groups) {
+ // find group indices and identify actions in groups
+ var groupActions = [];
+ var inserts = [];
+ var self = this;
+
+ groups.forEach(function (group) {
+ var end;
+ var i;
+
+ var start = actions.indexOf(group._groupActions[0]);
+ if (start >= 0) {
+ end = start + group._groupActions.length;
+
+ //if (actions.slice(start, end) === group._groupActions) {
+ if ($$.arrayEqual(actions.slice(start, end), group._groupActions)) {
+ group._groupActions.forEach(function (action) {
+ groupActions.push(action);
+ });
+
+ if (!group.required) {
+ if (inserts[start]) {
+ inserts[start] += ' [';
+ } else {
+ inserts[start] = '[';
+ }
+ inserts[end] = ']';
+ } else {
+ if (inserts[start]) {
+ inserts[start] += ' (';
+ } else {
+ inserts[start] = '(';
+ }
+ inserts[end] = ')';
+ }
+ for (i = start + 1; i < end; i += 1) {
+ inserts[i] = '|';
+ }
+ }
+ }
+ });
+
+ // collect all actions format strings
+ var parts = [];
+
+ actions.forEach(function (action, actionIndex) {
+ var part;
+ var optionString;
+ var argsDefault;
+ var argsString;
+
+ // suppressed arguments are marked with None
+ // remove | separators for suppressed arguments
+ if (action.help === c.SUPPRESS) {
+ parts.push(null);
+ if (inserts[actionIndex] === '|') {
+ inserts.splice(actionIndex, actionIndex);
+ } else if (inserts[actionIndex + 1] === '|') {
+ inserts.splice(actionIndex + 1, actionIndex + 1);
+ }
+
+ // produce all arg strings
+ } else if (!action.isOptional()) {
+ part = self._formatArgs(action, action.dest);
+
+ // if it's in a group, strip the outer []
+ if (groupActions.indexOf(action) >= 0) {
+ if (part[0] === '[' && part[part.length - 1] === ']') {
+ part = part.slice(1, -1);
+ }
+ }
+ // add the action string to the list
+ parts.push(part);
+
+ // produce the first way to invoke the option in brackets
+ } else {
+ optionString = action.optionStrings[0];
+
+ // if the Optional doesn't take a value, format is: -s or --long
+ if (action.nargs === 0) {
+ part = '' + optionString;
+
+ // if the Optional takes a value, format is: -s ARGS or --long ARGS
+ } else {
+ argsDefault = action.dest.toUpperCase();
+ argsString = self._formatArgs(action, argsDefault);
+ part = optionString + ' ' + argsString;
+ }
+ // make it look optional if it's not required or in a group
+ if (!action.required && groupActions.indexOf(action) < 0) {
+ part = '[' + part + ']';
+ }
+ // add the action string to the list
+ parts.push(part);
+ }
+ });
+
+ // insert things at the necessary indices
+ for (var i = inserts.length - 1; i >= 0; --i) {
+ if (inserts[i] !== null) {
+ parts.splice(i, 0, inserts[i]);
+ }
+ }
+
+ // join all the action items with spaces
+ var text = parts.filter(function (part) {
+ return !!part;
+ }).join(' ');
+
+ // clean up separators for mutually exclusive groups
+ text = text.replace(/([\[(]) /g, '$1'); // remove spaces
+ text = text.replace(/ ([\])])/g, '$1');
+ text = text.replace(/\[ *\]/g, ''); // remove empty groups
+ text = text.replace(/\( *\)/g, '');
+ text = text.replace(/\(([^|]*)\)/g, '$1'); // remove () from single action groups
+
+ text = text.trim();
+
+ // return the text
+ return text;
+};
+
+HelpFormatter.prototype._formatText = function (text) {
+ text = sprintf(text, { prog: this._prog });
+ var textWidth = this._width - this._currentIndent;
+ var indentIncriment = $$.repeat(' ', this._currentIndent);
+ return this._fillText(text, textWidth, indentIncriment) + c.EOL + c.EOL;
+};
+
+HelpFormatter.prototype._formatAction = function (action) {
+ var self = this;
+
+ var helpText;
+ var helpLines;
+ var parts;
+ var indentFirst;
+
+ // determine the required width and the entry label
+ var helpPosition = Math.min(this._actionMaxLength + 2, this._maxHelpPosition);
+ var helpWidth = this._width - helpPosition;
+ var actionWidth = helpPosition - this._currentIndent - 2;
+ var actionHeader = this._formatActionInvocation(action);
+
+ // no help; start on same line and add a final newline
+ if (!action.help) {
+ actionHeader = $$.repeat(' ', this._currentIndent) + actionHeader + c.EOL;
+
+ // short action name; start on the same line and pad two spaces
+ } else if (actionHeader.length <= actionWidth) {
+ actionHeader = $$.repeat(' ', this._currentIndent) +
+ actionHeader +
+ ' ' +
+ $$.repeat(' ', actionWidth - actionHeader.length);
+ indentFirst = 0;
+
+ // long action name; start on the next line
+ } else {
+ actionHeader = $$.repeat(' ', this._currentIndent) + actionHeader + c.EOL;
+ indentFirst = helpPosition;
+ }
+
+ // collect the pieces of the action help
+ parts = [ actionHeader ];
+
+ // if there was help for the action, add lines of help text
+ if (action.help) {
+ helpText = this._expandHelp(action);
+ helpLines = this._splitLines(helpText, helpWidth);
+ parts.push($$.repeat(' ', indentFirst) + helpLines[0] + c.EOL);
+ helpLines.slice(1).forEach(function (line) {
+ parts.push($$.repeat(' ', helpPosition) + line + c.EOL);
+ });
+
+ // or add a newline if the description doesn't end with one
+ } else if (actionHeader.charAt(actionHeader.length - 1) !== c.EOL) {
+ parts.push(c.EOL);
+ }
+ // if there are any sub-actions, add their help as well
+ if (action._getSubactions) {
+ this._indent();
+ action._getSubactions().forEach(function (subaction) {
+ parts.push(self._formatAction(subaction));
+ });
+ this._dedent();
+ }
+ // return a single string
+ return this._joinParts(parts);
+};
+
+HelpFormatter.prototype._formatActionInvocation = function (action) {
+ if (!action.isOptional()) {
+ var format_func = this._metavarFormatter(action, action.dest);
+ var metavars = format_func(1);
+ return metavars[0];
+ }
+
+ var parts = [];
+ var argsDefault;
+ var argsString;
+
+ // if the Optional doesn't take a value, format is: -s, --long
+ if (action.nargs === 0) {
+ parts = parts.concat(action.optionStrings);
+
+ // if the Optional takes a value, format is: -s ARGS, --long ARGS
+ } else {
+ argsDefault = action.dest.toUpperCase();
+ argsString = this._formatArgs(action, argsDefault);
+ action.optionStrings.forEach(function (optionString) {
+ parts.push(optionString + ' ' + argsString);
+ });
+ }
+ return parts.join(', ');
+};
+
+HelpFormatter.prototype._metavarFormatter = function (action, metavarDefault) {
+ var result;
+
+ if (action.metavar || action.metavar === '') {
+ result = action.metavar;
+ } else if (action.choices) {
+ var choices = action.choices;
+
+ if (typeof choices === 'string') {
+ choices = choices.split('').join(', ');
+ } else if (Array.isArray(choices)) {
+ choices = choices.join(',');
+ } else {
+ choices = Object.keys(choices).join(',');
+ }
+ result = '{' + choices + '}';
+ } else {
+ result = metavarDefault;
+ }
+
+ return function (size) {
+ if (Array.isArray(result)) {
+ return result;
+ }
+
+ var metavars = [];
+ for (var i = 0; i < size; i += 1) {
+ metavars.push(result);
+ }
+ return metavars;
+ };
+};
+
+HelpFormatter.prototype._formatArgs = function (action, metavarDefault) {
+ var result;
+ var metavars;
+
+ var buildMetavar = this._metavarFormatter(action, metavarDefault);
+
+ switch (action.nargs) {
+ /*eslint-disable no-undefined*/
+ case undefined:
+ case null:
+ metavars = buildMetavar(1);
+ result = '' + metavars[0];
+ break;
+ case c.OPTIONAL:
+ metavars = buildMetavar(1);
+ result = '[' + metavars[0] + ']';
+ break;
+ case c.ZERO_OR_MORE:
+ metavars = buildMetavar(2);
+ result = '[' + metavars[0] + ' [' + metavars[1] + ' ...]]';
+ break;
+ case c.ONE_OR_MORE:
+ metavars = buildMetavar(2);
+ result = '' + metavars[0] + ' [' + metavars[1] + ' ...]';
+ break;
+ case c.REMAINDER:
+ result = '...';
+ break;
+ case c.PARSER:
+ metavars = buildMetavar(1);
+ result = metavars[0] + ' ...';
+ break;
+ default:
+ metavars = buildMetavar(action.nargs);
+ result = metavars.join(' ');
+ }
+ return result;
+};
+
+HelpFormatter.prototype._expandHelp = function (action) {
+ var params = { prog: this._prog };
+
+ Object.keys(action).forEach(function (actionProperty) {
+ var actionValue = action[actionProperty];
+
+ if (actionValue !== c.SUPPRESS) {
+ params[actionProperty] = actionValue;
+ }
+ });
+
+ if (params.choices) {
+ if (typeof params.choices === 'string') {
+ params.choices = params.choices.split('').join(', ');
+ } else if (Array.isArray(params.choices)) {
+ params.choices = params.choices.join(', ');
+ } else {
+ params.choices = Object.keys(params.choices).join(', ');
+ }
+ }
+
+ return sprintf(this._getHelpString(action), params);
+};
+
+HelpFormatter.prototype._splitLines = function (text, width) {
+ var lines = [];
+ var delimiters = [ ' ', '.', ',', '!', '?' ];
+ var re = new RegExp('[' + delimiters.join('') + '][^' + delimiters.join('') + ']*$');
+
+ text = text.replace(/[\n\|\t]/g, ' ');
+
+ text = text.trim();
+ text = text.replace(this._whitespaceMatcher, ' ');
+
+ // Wraps the single paragraph in text (a string) so every line
+ // is at most width characters long.
+ text.split(c.EOL).forEach(function (line) {
+ if (width >= line.length) {
+ lines.push(line);
+ return;
+ }
+
+ var wrapStart = 0;
+ var wrapEnd = width;
+ var delimiterIndex = 0;
+ while (wrapEnd <= line.length) {
+ if (wrapEnd !== line.length && delimiters.indexOf(line[wrapEnd] < -1)) {
+ delimiterIndex = (re.exec(line.substring(wrapStart, wrapEnd)) || {}).index;
+ wrapEnd = wrapStart + delimiterIndex + 1;
+ }
+ lines.push(line.substring(wrapStart, wrapEnd));
+ wrapStart = wrapEnd;
+ wrapEnd += width;
+ }
+ if (wrapStart < line.length) {
+ lines.push(line.substring(wrapStart, wrapEnd));
+ }
+ });
+
+ return lines;
+};
+
+HelpFormatter.prototype._fillText = function (text, width, indent) {
+ var lines = this._splitLines(text, width);
+ lines = lines.map(function (line) {
+ return indent + line;
+ });
+ return lines.join(c.EOL);
+};
+
+HelpFormatter.prototype._getHelpString = function (action) {
+ return action.help;
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/namespace.js b/chatto/src/main/javascript/node_modules/argparse/lib/namespace.js
new file mode 100644
index 0000000..a860de9
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/namespace.js
@@ -0,0 +1,76 @@
+/**
+ * class Namespace
+ *
+ * Simple object for storing attributes. Implements equality by attribute names
+ * and values, and provides a simple string representation.
+ *
+ * See also [original guide][1]
+ *
+ * [1]:http://docs.python.org/dev/library/argparse.html#the-namespace-object
+ **/
+'use strict';
+
+var $$ = require('./utils');
+
+/**
+ * new Namespace(options)
+ * - options(object): predefined propertis for result object
+ *
+ **/
+var Namespace = module.exports = function Namespace(options) {
+ $$.extend(this, options);
+};
+
+/**
+ * Namespace#isset(key) -> Boolean
+ * - key (string|number): property name
+ *
+ * Tells whenever `namespace` contains given `key` or not.
+ **/
+Namespace.prototype.isset = function (key) {
+ return $$.has(this, key);
+};
+
+/**
+ * Namespace#set(key, value) -> self
+ * -key (string|number|object): propery name
+ * -value (mixed): new property value
+ *
+ * Set the property named key with value.
+ * If key object then set all key properties to namespace object
+ **/
+Namespace.prototype.set = function (key, value) {
+ if (typeof (key) === 'object') {
+ $$.extend(this, key);
+ } else {
+ this[key] = value;
+ }
+ return this;
+};
+
+/**
+ * Namespace#get(key, defaultValue) -> mixed
+ * - key (string|number): property name
+ * - defaultValue (mixed): default value
+ *
+ * Return the property key or defaulValue if not set
+ **/
+Namespace.prototype.get = function (key, defaultValue) {
+ return !this[key] ? defaultValue : this[key];
+};
+
+/**
+ * Namespace#unset(key, defaultValue) -> mixed
+ * - key (string|number): property name
+ * - defaultValue (mixed): default value
+ *
+ * Return data[key](and delete it) or defaultValue
+ **/
+Namespace.prototype.unset = function (key, defaultValue) {
+ var value = this[key];
+ if (value !== null) {
+ delete this[key];
+ return value;
+ }
+ return defaultValue;
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/lib/utils.js b/chatto/src/main/javascript/node_modules/argparse/lib/utils.js
new file mode 100644
index 0000000..4a9cf3e
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/lib/utils.js
@@ -0,0 +1,57 @@
+'use strict';
+
+exports.repeat = function (str, num) {
+ var result = '';
+ for (var i = 0; i < num; i++) { result += str; }
+ return result;
+};
+
+exports.arrayEqual = function (a, b) {
+ if (a.length !== b.length) { return false; }
+ for (var i = 0; i < a.length; i++) {
+ if (a[i] !== b[i]) { return false; }
+ }
+ return true;
+};
+
+exports.trimChars = function (str, chars) {
+ var start = 0;
+ var end = str.length - 1;
+ while (chars.indexOf(str.charAt(start)) >= 0) { start++; }
+ while (chars.indexOf(str.charAt(end)) >= 0) { end--; }
+ return str.slice(start, end + 1);
+};
+
+exports.capitalize = function (str) {
+ return str.charAt(0).toUpperCase() + str.slice(1);
+};
+
+exports.arrayUnion = function () {
+ var result = [];
+ for (var i = 0, values = {}; i < arguments.length; i++) {
+ var arr = arguments[i];
+ for (var j = 0; j < arr.length; j++) {
+ if (!values[arr[j]]) {
+ values[arr[j]] = true;
+ result.push(arr[j]);
+ }
+ }
+ }
+ return result;
+};
+
+function has(obj, key) {
+ return Object.prototype.hasOwnProperty.call(obj, key);
+}
+
+exports.has = has;
+
+exports.extend = function (dest, src) {
+ for (var i in src) {
+ if (has(src, i)) { dest[i] = src[i]; }
+ }
+};
+
+exports.trimEnd = function (str) {
+ return str.replace(/\s+$/g, '');
+};
diff --git a/chatto/src/main/javascript/node_modules/argparse/package.json b/chatto/src/main/javascript/node_modules/argparse/package.json
new file mode 100644
index 0000000..26c8422
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/argparse/package.json
@@ -0,0 +1,70 @@
+{
+ "_from": "argparse@1.0.10",
+ "_id": "argparse@1.0.10",
+ "_inBundle": false,
+ "_integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "_location": "/argparse",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "version",
+ "registry": true,
+ "raw": "argparse@1.0.10",
+ "name": "argparse",
+ "escapedName": "argparse",
+ "rawSpec": "1.0.10",
+ "saveSpec": null,
+ "fetchSpec": "1.0.10"
+ },
+ "_requiredBy": [
+ "/markdown-it"
+ ],
+ "_resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "_shasum": "bcd6791ea5ae09725e17e5ad988134cd40b3d911",
+ "_spec": "argparse@1.0.10",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/markdown-it",
+ "bugs": {
+ "url": "https://github.com/nodeca/argparse/issues"
+ },
+ "bundleDependencies": false,
+ "contributors": [
+ {
+ "name": "Eugene Shkuropat"
+ },
+ {
+ "name": "Paul Jacobson"
+ }
+ ],
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ },
+ "deprecated": false,
+ "description": "Very powerful CLI arguments parser. Native port of argparse - python's options parsing library",
+ "devDependencies": {
+ "eslint": "^2.13.1",
+ "istanbul": "^0.4.5",
+ "mocha": "^3.1.0",
+ "ndoc": "^5.0.1"
+ },
+ "files": [
+ "index.js",
+ "lib/"
+ ],
+ "homepage": "https://github.com/nodeca/argparse#readme",
+ "keywords": [
+ "cli",
+ "parser",
+ "argparse",
+ "option",
+ "args"
+ ],
+ "license": "MIT",
+ "name": "argparse",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/nodeca/argparse.git"
+ },
+ "scripts": {
+ "test": "make test"
+ },
+ "version": "1.0.10"
+}
diff --git a/chatto/src/main/javascript/node_modules/arr-diff/LICENSE b/chatto/src/main/javascript/node_modules/arr-diff/LICENSE
new file mode 100755
index 0000000..d734237
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/arr-diff/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2017, Jon Schlinkert
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chatto/src/main/javascript/node_modules/arr-diff/README.md b/chatto/src/main/javascript/node_modules/arr-diff/README.md
new file mode 100644
index 0000000..961f5c3
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/arr-diff/README.md
@@ -0,0 +1,130 @@
+# arr-diff [![NPM version](https://img.shields.io/npm/v/arr-diff.svg?style=flat)](https://www.npmjs.com/package/arr-diff) [![NPM monthly downloads](https://img.shields.io/npm/dm/arr-diff.svg?style=flat)](https://npmjs.org/package/arr-diff) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/arr-diff.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/arr-diff)
+
+> Returns an array with only the unique values from the first array, by excluding all values from additional arrays using strict equality for comparisons.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save arr-diff
+```
+
+Install with [yarn](https://yarnpkg.com):
+
+```sh
+$ yarn add arr-diff
+```
+
+Install with [bower](https://bower.io/)
+
+```sh
+$ bower install arr-diff --save
+```
+
+## Usage
+
+Returns the difference between the first array and additional arrays.
+
+```js
+var diff = require('arr-diff');
+
+var a = ['a', 'b', 'c', 'd'];
+var b = ['b', 'c'];
+
+console.log(diff(a, b))
+//=> ['a', 'd']
+```
+
+## Benchmarks
+
+This library versus [array-differ](https://github.com/sindresorhus/array-differ), on April 14, 2017:
+
+```
+Benchmarking: (4 of 4)
+ · long-dupes
+ · long
+ · med
+ · short
+
+# benchmark/fixtures/long-dupes.js (100804 bytes)
+ arr-diff-3.0.0 x 822 ops/sec ±0.67% (86 runs sampled)
+ arr-diff-4.0.0 x 2,141 ops/sec ±0.42% (89 runs sampled)
+ array-differ x 708 ops/sec ±0.70% (89 runs sampled)
+
+ fastest is arr-diff-4.0.0
+
+# benchmark/fixtures/long.js (94529 bytes)
+ arr-diff-3.0.0 x 882 ops/sec ±0.60% (87 runs sampled)
+ arr-diff-4.0.0 x 2,329 ops/sec ±0.97% (83 runs sampled)
+ array-differ x 769 ops/sec ±0.61% (90 runs sampled)
+
+ fastest is arr-diff-4.0.0
+
+# benchmark/fixtures/med.js (708 bytes)
+ arr-diff-3.0.0 x 856,150 ops/sec ±0.42% (89 runs sampled)
+ arr-diff-4.0.0 x 4,665,249 ops/sec ±1.06% (89 runs sampled)
+ array-differ x 653,888 ops/sec ±1.02% (86 runs sampled)
+
+ fastest is arr-diff-4.0.0
+
+# benchmark/fixtures/short.js (60 bytes)
+ arr-diff-3.0.0 x 3,078,467 ops/sec ±0.77% (93 runs sampled)
+ arr-diff-4.0.0 x 9,213,296 ops/sec ±0.65% (89 runs sampled)
+ array-differ x 1,337,051 ops/sec ±0.91% (92 runs sampled)
+
+ fastest is arr-diff-4.0.0
+```
+
+## About
+
+### Related projects
+
+* [arr-flatten](https://www.npmjs.com/package/arr-flatten): Recursively flatten an array or arrays. This is the fastest implementation of array flatten. | [homepage](https://github.com/jonschlinkert/arr-flatten "Recursively flatten an array or arrays. This is the fastest implementation of array flatten.")
+* [array-filter](https://www.npmjs.com/package/array-filter): Array#filter for older browsers. | [homepage](https://github.com/juliangruber/array-filter "Array#filter for older browsers.")
+* [array-intersection](https://www.npmjs.com/package/array-intersection): Return an array with the unique values present in _all_ given arrays using strict equality… [more](https://github.com/jonschlinkert/array-intersection) | [homepage](https://github.com/jonschlinkert/array-intersection "Return an array with the unique values present in _all_ given arrays using strict equality for comparisons.")
+
+### Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+### Contributors
+
+| **Commits** | **Contributor** |
+| --- | --- |
+| 33 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 2 | [paulmillr](https://github.com/paulmillr) |
+
+### Building docs
+
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
+
+To generate the readme, run the following command:
+
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
+
+### Running tests
+
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
+
+```sh
+$ npm install && npm test
+```
+
+### Author
+
+**Jon Schlinkert**
+
+* [github/jonschlinkert](https://github.com/jonschlinkert)
+* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
+
+### License
+
+Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.5.0, on April 14, 2017._
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/arr-diff/index.js b/chatto/src/main/javascript/node_modules/arr-diff/index.js
new file mode 100644
index 0000000..90f2807
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/arr-diff/index.js
@@ -0,0 +1,47 @@
+/*!
+ * arr-diff
+ *
+ * Copyright (c) 2014-2017, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+'use strict';
+
+module.exports = function diff(arr/*, arrays*/) {
+ var len = arguments.length;
+ var idx = 0;
+ while (++idx < len) {
+ arr = diffArray(arr, arguments[idx]);
+ }
+ return arr;
+};
+
+function diffArray(one, two) {
+ if (!Array.isArray(two)) {
+ return one.slice();
+ }
+
+ var tlen = two.length
+ var olen = one.length;
+ var idx = -1;
+ var arr = [];
+
+ while (++idx < olen) {
+ var ele = one[idx];
+
+ var hasEle = false;
+ for (var i = 0; i < tlen; i++) {
+ var val = two[i];
+
+ if (ele === val) {
+ hasEle = true;
+ break;
+ }
+ }
+
+ if (hasEle === false) {
+ arr.push(ele);
+ }
+ }
+ return arr;
+}
diff --git a/chatto/src/main/javascript/node_modules/arr-diff/package.json b/chatto/src/main/javascript/node_modules/arr-diff/package.json
new file mode 100644
index 0000000..c1266b0
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/arr-diff/package.json
@@ -0,0 +1,109 @@
+{
+ "_from": "arr-diff@^4.0.0",
+ "_id": "arr-diff@4.0.0",
+ "_inBundle": false,
+ "_integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=",
+ "_location": "/arr-diff",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "arr-diff@^4.0.0",
+ "name": "arr-diff",
+ "escapedName": "arr-diff",
+ "rawSpec": "^4.0.0",
+ "saveSpec": null,
+ "fetchSpec": "^4.0.0"
+ },
+ "_requiredBy": [
+ "/micromatch",
+ "/nanomatch"
+ ],
+ "_resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
+ "_shasum": "d6461074febfec71e7e15235761a329a5dc7c520",
+ "_spec": "arr-diff@^4.0.0",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/micromatch",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/arr-diff/issues"
+ },
+ "bundleDependencies": false,
+ "contributors": [
+ {
+ "name": "Jon Schlinkert",
+ "email": "jon.schlinkert@sellside.com",
+ "url": "http://twitter.com/jonschlinkert"
+ },
+ {
+ "name": "Paul Miller",
+ "email": "paul+gh@paulmillr.com",
+ "url": "paulmillr.com"
+ }
+ ],
+ "dependencies": {},
+ "deprecated": false,
+ "description": "Returns an array with only the unique values from the first array, by excluding all values from additional arrays using strict equality for comparisons.",
+ "devDependencies": {
+ "ansi-bold": "^0.1.1",
+ "arr-flatten": "^1.0.1",
+ "array-differ": "^1.0.0",
+ "benchmarked": "^0.2.4",
+ "gulp-format-md": "^0.1.9",
+ "minimist": "^1.2.0",
+ "mocha": "^2.4.5"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/jonschlinkert/arr-diff",
+ "keywords": [
+ "arr",
+ "array",
+ "array differ",
+ "array-differ",
+ "diff",
+ "differ",
+ "difference"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "arr-diff",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/arr-diff.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "related": {
+ "list": [
+ "arr-flatten",
+ "array-filter",
+ "array-intersection"
+ ]
+ },
+ "reflinks": [
+ "array-differ",
+ "verb"
+ ],
+ "lint": {
+ "reflinks": true
+ }
+ },
+ "version": "4.0.0"
+}
diff --git a/chatto/src/main/javascript/node_modules/arr-flatten/LICENSE b/chatto/src/main/javascript/node_modules/arr-flatten/LICENSE
new file mode 100755
index 0000000..3f2eca1
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/arr-flatten/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2017, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chatto/src/main/javascript/node_modules/arr-flatten/README.md b/chatto/src/main/javascript/node_modules/arr-flatten/README.md
new file mode 100755
index 0000000..7dc7a97
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/arr-flatten/README.md
@@ -0,0 +1,86 @@
+# arr-flatten [![NPM version](https://img.shields.io/npm/v/arr-flatten.svg?style=flat)](https://www.npmjs.com/package/arr-flatten) [![NPM monthly downloads](https://img.shields.io/npm/dm/arr-flatten.svg?style=flat)](https://npmjs.org/package/arr-flatten) [![NPM total downloads](https://img.shields.io/npm/dt/arr-flatten.svg?style=flat)](https://npmjs.org/package/arr-flatten) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/arr-flatten.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/arr-flatten) [![Windows Build Status](https://img.shields.io/appveyor/ci/jonschlinkert/arr-flatten.svg?style=flat&label=AppVeyor)](https://ci.appveyor.com/project/jonschlinkert/arr-flatten)
+
+> Recursively flatten an array or arrays.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save arr-flatten
+```
+
+## Install
+
+Install with [bower](https://bower.io/)
+
+```sh
+$ bower install arr-flatten --save
+```
+
+## Usage
+
+```js
+var flatten = require('arr-flatten');
+
+flatten(['a', ['b', ['c']], 'd', ['e']]);
+//=> ['a', 'b', 'c', 'd', 'e']
+```
+
+## Why another flatten utility?
+
+I wanted the fastest implementation I could find, with implementation choices that should work for 95% of use cases, but no cruft to cover the other 5%.
+
+## About
+
+### Related projects
+
+* [arr-filter](https://www.npmjs.com/package/arr-filter): Faster alternative to javascript's native filter method. | [homepage](https://github.com/jonschlinkert/arr-filter "Faster alternative to javascript's native filter method.")
+* [arr-union](https://www.npmjs.com/package/arr-union): Combines a list of arrays, returning a single array with unique values, using strict equality… [more](https://github.com/jonschlinkert/arr-union) | [homepage](https://github.com/jonschlinkert/arr-union "Combines a list of arrays, returning a single array with unique values, using strict equality for comparisons.")
+* [array-each](https://www.npmjs.com/package/array-each): Loop over each item in an array and call the given function on every element. | [homepage](https://github.com/jonschlinkert/array-each "Loop over each item in an array and call the given function on every element.")
+* [array-unique](https://www.npmjs.com/package/array-unique): Remove duplicate values from an array. Fastest ES5 implementation. | [homepage](https://github.com/jonschlinkert/array-unique "Remove duplicate values from an array. Fastest ES5 implementation.")
+
+### Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+### Contributors
+
+| **Commits** | **Contributor** |
+| --- | --- |
+| 20 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 1 | [lukeed](https://github.com/lukeed) |
+
+### Building docs
+
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
+
+To generate the readme, run the following command:
+
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
+
+### Running tests
+
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
+
+```sh
+$ npm install && npm test
+```
+
+### Author
+
+**Jon Schlinkert**
+
+* [github/jonschlinkert](https://github.com/jonschlinkert)
+* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
+
+### License
+
+Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on July 05, 2017._
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/arr-flatten/index.js b/chatto/src/main/javascript/node_modules/arr-flatten/index.js
new file mode 100644
index 0000000..0cb4ea4
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/arr-flatten/index.js
@@ -0,0 +1,22 @@
+/*!
+ * arr-flatten
+ *
+ * Copyright (c) 2014-2017, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+'use strict';
+
+module.exports = function (arr) {
+ return flat(arr, []);
+};
+
+function flat(arr, res) {
+ var i = 0, cur;
+ var len = arr.length;
+ for (; i < len; i++) {
+ cur = arr[i];
+ Array.isArray(cur) ? flat(cur, res) : res.push(cur);
+ }
+ return res;
+}
diff --git a/chatto/src/main/javascript/node_modules/arr-flatten/package.json b/chatto/src/main/javascript/node_modules/arr-flatten/package.json
new file mode 100644
index 0000000..1c64983
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/arr-flatten/package.json
@@ -0,0 +1,113 @@
+{
+ "_from": "arr-flatten@^1.1.0",
+ "_id": "arr-flatten@1.1.0",
+ "_inBundle": false,
+ "_integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==",
+ "_location": "/arr-flatten",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "arr-flatten@^1.1.0",
+ "name": "arr-flatten",
+ "escapedName": "arr-flatten",
+ "rawSpec": "^1.1.0",
+ "saveSpec": null,
+ "fetchSpec": "^1.1.0"
+ },
+ "_requiredBy": [
+ "/braces"
+ ],
+ "_resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz",
+ "_shasum": "36048bbff4e7b47e136644316c99669ea5ae91f1",
+ "_spec": "arr-flatten@^1.1.0",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/braces",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/arr-flatten/issues"
+ },
+ "bundleDependencies": false,
+ "contributors": [
+ {
+ "name": "Jon Schlinkert",
+ "url": "http://twitter.com/jonschlinkert"
+ },
+ {
+ "name": "Luke Edwards",
+ "url": "https://lukeed.com"
+ }
+ ],
+ "deprecated": false,
+ "description": "Recursively flatten an array or arrays.",
+ "devDependencies": {
+ "ansi-bold": "^0.1.1",
+ "array-flatten": "^2.1.1",
+ "array-slice": "^1.0.0",
+ "benchmarked": "^1.0.0",
+ "compute-flatten": "^1.0.0",
+ "flatit": "^1.1.1",
+ "flatten": "^1.0.2",
+ "flatten-array": "^1.0.0",
+ "glob": "^7.1.1",
+ "gulp-format-md": "^0.1.12",
+ "just-flatten-it": "^1.1.23",
+ "lodash.flattendeep": "^4.4.0",
+ "m_flattened": "^1.0.1",
+ "mocha": "^3.2.0",
+ "utils-flatten": "^1.0.0",
+ "write": "^0.3.3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/jonschlinkert/arr-flatten",
+ "keywords": [
+ "arr",
+ "array",
+ "elements",
+ "flat",
+ "flatten",
+ "nested",
+ "recurse",
+ "recursive",
+ "recursively"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "arr-flatten",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/arr-flatten.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "related": {
+ "list": [
+ "arr-filter",
+ "arr-union",
+ "array-each",
+ "array-unique"
+ ]
+ },
+ "lint": {
+ "reflinks": true
+ }
+ },
+ "version": "1.1.0"
+}
diff --git a/chatto/src/main/javascript/node_modules/arr-union/LICENSE b/chatto/src/main/javascript/node_modules/arr-union/LICENSE
new file mode 100644
index 0000000..39245ac
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/arr-union/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2016, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chatto/src/main/javascript/node_modules/arr-union/README.md b/chatto/src/main/javascript/node_modules/arr-union/README.md
new file mode 100644
index 0000000..b3cd4f4
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/arr-union/README.md
@@ -0,0 +1,99 @@
+# arr-union [![NPM version](https://img.shields.io/npm/v/arr-union.svg)](https://www.npmjs.com/package/arr-union) [![Build Status](https://img.shields.io/travis/jonschlinkert/arr-union.svg)](https://travis-ci.org/jonschlinkert/arr-union)
+
+> Combines a list of arrays, returning a single array with unique values, using strict equality for comparisons.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm i arr-union --save
+```
+
+## Benchmarks
+
+This library is **10-20 times faster** and more performant than [array-union](https://github.com/sindresorhus/array-union).
+
+See the [benchmarks](./benchmark).
+
+```sh
+#1: five-arrays
+ array-union x 511,121 ops/sec ±0.80% (96 runs sampled)
+ arr-union x 5,716,039 ops/sec ±0.86% (93 runs sampled)
+
+#2: ten-arrays
+ array-union x 245,196 ops/sec ±0.69% (94 runs sampled)
+ arr-union x 1,850,786 ops/sec ±0.84% (97 runs sampled)
+
+#3: two-arrays
+ array-union x 563,869 ops/sec ±0.97% (94 runs sampled)
+ arr-union x 9,602,852 ops/sec ±0.87% (92 runs sampled)
+```
+
+## Usage
+
+```js
+var union = require('arr-union');
+
+union(['a'], ['b', 'c'], ['d', 'e', 'f']);
+//=> ['a', 'b', 'c', 'd', 'e', 'f']
+```
+
+Returns only unique elements:
+
+```js
+union(['a', 'a'], ['b', 'c']);
+//=> ['a', 'b', 'c']
+```
+
+## Related projects
+
+* [arr-diff](https://www.npmjs.com/package/arr-diff): Returns an array with only the unique values from the first array, by excluding all… [more](https://www.npmjs.com/package/arr-diff) | [homepage](https://github.com/jonschlinkert/arr-diff)
+* [arr-filter](https://www.npmjs.com/package/arr-filter): Faster alternative to javascript's native filter method. | [homepage](https://github.com/jonschlinkert/arr-filter)
+* [arr-flatten](https://www.npmjs.com/package/arr-flatten): Recursively flatten an array or arrays. This is the fastest implementation of array flatten. | [homepage](https://github.com/jonschlinkert/arr-flatten)
+* [arr-map](https://www.npmjs.com/package/arr-map): Faster, node.js focused alternative to JavaScript's native array map. | [homepage](https://github.com/jonschlinkert/arr-map)
+* [arr-pluck](https://www.npmjs.com/package/arr-pluck): Retrieves the value of a specified property from all elements in the collection. | [homepage](https://github.com/jonschlinkert/arr-pluck)
+* [arr-reduce](https://www.npmjs.com/package/arr-reduce): Fast array reduce that also loops over sparse elements. | [homepage](https://github.com/jonschlinkert/arr-reduce)
+* [array-unique](https://www.npmjs.com/package/array-unique): Return an array free of duplicate values. Fastest ES5 implementation. | [homepage](https://github.com/jonschlinkert/array-unique)
+
+## Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/arr-union/issues/new).
+
+## Building docs
+
+Generate readme and API documentation with [verb](https://github.com/verbose/verb):
+
+```sh
+$ npm i verb && npm run docs
+```
+
+Or, if [verb](https://github.com/verbose/verb) is installed globally:
+
+```sh
+$ verb
+```
+
+## Running tests
+
+Install dev dependencies:
+
+```sh
+$ npm i -d && npm test
+```
+
+## Author
+
+**Jon Schlinkert**
+
+* [github/jonschlinkert](https://github.com/jonschlinkert)
+* [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
+
+## License
+
+Copyright © 2016 [Jon Schlinkert](https://github.com/jonschlinkert)
+Released under the [MIT license](https://github.com/jonschlinkert/arr-union/blob/master/LICENSE).
+
+***
+
+_This file was generated by [verb](https://github.com/verbose/verb), v0.9.0, on February 23, 2016._
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/arr-union/index.js b/chatto/src/main/javascript/node_modules/arr-union/index.js
new file mode 100644
index 0000000..5ae6c4a
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/arr-union/index.js
@@ -0,0 +1,29 @@
+'use strict';
+
+module.exports = function union(init) {
+ if (!Array.isArray(init)) {
+ throw new TypeError('arr-union expects the first argument to be an array.');
+ }
+
+ var len = arguments.length;
+ var i = 0;
+
+ while (++i < len) {
+ var arg = arguments[i];
+ if (!arg) continue;
+
+ if (!Array.isArray(arg)) {
+ arg = [arg];
+ }
+
+ for (var j = 0; j < arg.length; j++) {
+ var ele = arg[j];
+
+ if (init.indexOf(ele) >= 0) {
+ continue;
+ }
+ init.push(ele);
+ }
+ }
+ return init;
+};
diff --git a/chatto/src/main/javascript/node_modules/arr-union/package.json b/chatto/src/main/javascript/node_modules/arr-union/package.json
new file mode 100644
index 0000000..bc26911
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/arr-union/package.json
@@ -0,0 +1,108 @@
+{
+ "_from": "arr-union@^3.1.0",
+ "_id": "arr-union@3.1.0",
+ "_inBundle": false,
+ "_integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
+ "_location": "/arr-union",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "arr-union@^3.1.0",
+ "name": "arr-union",
+ "escapedName": "arr-union",
+ "rawSpec": "^3.1.0",
+ "saveSpec": null,
+ "fetchSpec": "^3.1.0"
+ },
+ "_requiredBy": [
+ "/class-utils",
+ "/union-value"
+ ],
+ "_resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz",
+ "_shasum": "e39b09aea9def866a8f206e288af63919bae39c4",
+ "_spec": "arr-union@^3.1.0",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/union-value",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/arr-union/issues"
+ },
+ "bundleDependencies": false,
+ "deprecated": false,
+ "description": "Combines a list of arrays, returning a single array with unique values, using strict equality for comparisons.",
+ "devDependencies": {
+ "ansi-bold": "^0.1.1",
+ "array-union": "^1.0.1",
+ "array-unique": "^0.2.1",
+ "benchmarked": "^0.1.4",
+ "gulp-format-md": "^0.1.7",
+ "minimist": "^1.1.1",
+ "mocha": "*",
+ "should": "*"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/jonschlinkert/arr-union",
+ "keywords": [
+ "add",
+ "append",
+ "array",
+ "arrays",
+ "combine",
+ "concat",
+ "extend",
+ "union",
+ "uniq",
+ "unique",
+ "util",
+ "utility",
+ "utils"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "arr-union",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/arr-union.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "run": true,
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "related": {
+ "list": [
+ "arr-diff",
+ "arr-flatten",
+ "arr-filter",
+ "arr-map",
+ "arr-pluck",
+ "arr-reduce",
+ "array-unique"
+ ]
+ },
+ "reflinks": [
+ "verb",
+ "array-union"
+ ],
+ "lint": {
+ "reflinks": true
+ }
+ },
+ "version": "3.1.0"
+}
diff --git a/chatto/src/main/javascript/node_modules/array-unique/LICENSE b/chatto/src/main/javascript/node_modules/array-unique/LICENSE
new file mode 100755
index 0000000..842218c
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/array-unique/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2016, Jon Schlinkert
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chatto/src/main/javascript/node_modules/array-unique/README.md b/chatto/src/main/javascript/node_modules/array-unique/README.md
new file mode 100755
index 0000000..41c8c90
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/array-unique/README.md
@@ -0,0 +1,77 @@
+# array-unique [![NPM version](https://img.shields.io/npm/v/array-unique.svg?style=flat)](https://www.npmjs.com/package/array-unique) [![NPM downloads](https://img.shields.io/npm/dm/array-unique.svg?style=flat)](https://npmjs.org/package/array-unique) [![Build Status](https://img.shields.io/travis/jonschlinkert/array-unique.svg?style=flat)](https://travis-ci.org/jonschlinkert/array-unique)
+
+Remove duplicate values from an array. Fastest ES5 implementation.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save array-unique
+```
+
+## Usage
+
+```js
+var unique = require('array-unique');
+
+var arr = ['a', 'b', 'c', 'c'];
+console.log(unique(arr)) //=> ['a', 'b', 'c']
+console.log(arr) //=> ['a', 'b', 'c']
+
+/* The above modifies the input array. To prevent that at a slight performance cost: */
+var unique = require("array-unique").immutable;
+
+var arr = ['a', 'b', 'c', 'c'];
+console.log(unique(arr)) //=> ['a', 'b', 'c']
+console.log(arr) //=> ['a', 'b', 'c', 'c']
+```
+
+## About
+
+### Related projects
+
+* [arr-diff](https://www.npmjs.com/package/arr-diff): Returns an array with only the unique values from the first array, by excluding all… [more](https://github.com/jonschlinkert/arr-diff) | [homepage](https://github.com/jonschlinkert/arr-diff "Returns an array with only the unique values from the first array, by excluding all values from additional arrays using strict equality for comparisons.")
+* [arr-flatten](https://www.npmjs.com/package/arr-flatten): Recursively flatten an array or arrays. This is the fastest implementation of array flatten. | [homepage](https://github.com/jonschlinkert/arr-flatten "Recursively flatten an array or arrays. This is the fastest implementation of array flatten.")
+* [arr-map](https://www.npmjs.com/package/arr-map): Faster, node.js focused alternative to JavaScript's native array map. | [homepage](https://github.com/jonschlinkert/arr-map "Faster, node.js focused alternative to JavaScript's native array map.")
+* [arr-pluck](https://www.npmjs.com/package/arr-pluck): Retrieves the value of a specified property from all elements in the collection. | [homepage](https://github.com/jonschlinkert/arr-pluck "Retrieves the value of a specified property from all elements in the collection.")
+* [arr-reduce](https://www.npmjs.com/package/arr-reduce): Fast array reduce that also loops over sparse elements. | [homepage](https://github.com/jonschlinkert/arr-reduce "Fast array reduce that also loops over sparse elements.")
+* [arr-union](https://www.npmjs.com/package/arr-union): Combines a list of arrays, returning a single array with unique values, using strict equality… [more](https://github.com/jonschlinkert/arr-union) | [homepage](https://github.com/jonschlinkert/arr-union "Combines a list of arrays, returning a single array with unique values, using strict equality for comparisons.")
+
+### Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+### Building docs
+
+_(This document was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme) (a [verb](https://github.com/verbose/verb) generator), please don't edit the readme directly. Any changes to the readme must be made in [.verb.md](.verb.md).)_
+
+To generate the readme and API documentation with [verb](https://github.com/verbose/verb):
+
+```sh
+$ npm install -g verb verb-generate-readme && verb
+```
+
+### Running tests
+
+Install dev dependencies:
+
+```sh
+$ npm install -d && npm test
+```
+
+### Author
+
+**Jon Schlinkert**
+
+* [github/jonschlinkert](https://github.com/jonschlinkert)
+* [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
+
+### License
+
+Copyright © 2016, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT license](https://github.com/jonschlinkert/array-unique/blob/master/LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.1.28, on July 31, 2016._
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/array-unique/index.js b/chatto/src/main/javascript/node_modules/array-unique/index.js
new file mode 100644
index 0000000..7e481e0
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/array-unique/index.js
@@ -0,0 +1,43 @@
+/*!
+ * array-unique
+ *
+ * Copyright (c) 2014-2015, Jon Schlinkert.
+ * Licensed under the MIT License.
+ */
+
+'use strict';
+
+module.exports = function unique(arr) {
+ if (!Array.isArray(arr)) {
+ throw new TypeError('array-unique expects an array.');
+ }
+
+ var len = arr.length;
+ var i = -1;
+
+ while (i++ < len) {
+ var j = i + 1;
+
+ for (; j < arr.length; ++j) {
+ if (arr[i] === arr[j]) {
+ arr.splice(j--, 1);
+ }
+ }
+ }
+ return arr;
+};
+
+module.exports.immutable = function uniqueImmutable(arr) {
+ if (!Array.isArray(arr)) {
+ throw new TypeError('array-unique expects an array.');
+ }
+
+ var arrLen = arr.length;
+ var newArr = new Array(arrLen);
+
+ for (var i = 0; i < arrLen; i++) {
+ newArr[i] = arr[i];
+ }
+
+ return module.exports(newArr);
+};
diff --git a/chatto/src/main/javascript/node_modules/array-unique/package.json b/chatto/src/main/javascript/node_modules/array-unique/package.json
new file mode 100644
index 0000000..1eb13c5
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/array-unique/package.json
@@ -0,0 +1,96 @@
+{
+ "_from": "array-unique@^0.3.2",
+ "_id": "array-unique@0.3.2",
+ "_inBundle": false,
+ "_integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=",
+ "_location": "/array-unique",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "array-unique@^0.3.2",
+ "name": "array-unique",
+ "escapedName": "array-unique",
+ "rawSpec": "^0.3.2",
+ "saveSpec": null,
+ "fetchSpec": "^0.3.2"
+ },
+ "_requiredBy": [
+ "/braces",
+ "/extglob",
+ "/micromatch",
+ "/nanomatch"
+ ],
+ "_resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
+ "_shasum": "a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428",
+ "_spec": "array-unique@^0.3.2",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/micromatch",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/array-unique/issues"
+ },
+ "bundleDependencies": false,
+ "deprecated": false,
+ "description": "Remove duplicate values from an array. Fastest ES5 implementation.",
+ "devDependencies": {
+ "array-uniq": "^1.0.2",
+ "benchmarked": "^0.1.3",
+ "gulp-format-md": "^0.1.9",
+ "mocha": "^2.5.3",
+ "should": "^10.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js",
+ "LICENSE",
+ "README.md"
+ ],
+ "homepage": "https://github.com/jonschlinkert/array-unique",
+ "keywords": [
+ "array",
+ "unique"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "array-unique",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/array-unique.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "related": {
+ "list": [
+ "arr-diff",
+ "arr-union",
+ "arr-flatten",
+ "arr-reduce",
+ "arr-map",
+ "arr-pluck"
+ ]
+ },
+ "reflinks": [
+ "verb",
+ "verb-generate-readme"
+ ],
+ "lint": {
+ "reflinks": true
+ }
+ },
+ "version": "0.3.2"
+}
diff --git a/chatto/src/main/javascript/node_modules/assign-symbols/LICENSE b/chatto/src/main/javascript/node_modules/assign-symbols/LICENSE
new file mode 100644
index 0000000..65f90ac
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/assign-symbols/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chatto/src/main/javascript/node_modules/assign-symbols/README.md b/chatto/src/main/javascript/node_modules/assign-symbols/README.md
new file mode 100644
index 0000000..422729d
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/assign-symbols/README.md
@@ -0,0 +1,73 @@
+# assign-symbols [![NPM version](https://badge.fury.io/js/assign-symbols.svg)](http://badge.fury.io/js/assign-symbols)
+
+> Assign the enumerable es6 Symbol properties from an object (or objects) to the first object passed on the arguments. Can be used as a supplement to other extend, assign or merge methods as a polyfill for the Symbols part of the es6 Object.assign method.
+
+From the [Mozilla Developer docs for Symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol):
+
+> A symbol is a unique and immutable data type and may be used as an identifier for object properties. The symbol object is an implicit object wrapper for the symbol primitive data type.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/)
+
+```sh
+$ npm i assign-symbols --save
+```
+
+## Usage
+
+```js
+var assignSymbols = require('assign-symbols');
+var obj = {};
+
+var one = {};
+var symbolOne = Symbol('aaa');
+one[symbolOne] = 'bbb';
+
+var two = {};
+var symbolTwo = Symbol('ccc');
+two[symbolTwo] = 'ddd';
+
+assignSymbols(obj, one, two);
+
+console.log(obj[symbolOne]);
+//=> 'bbb'
+console.log(obj[symbolTwo]);
+//=> 'ddd'
+```
+
+## Similar projects
+
+* [assign-deep](https://www.npmjs.com/package/assign-deep): Deeply assign the enumerable properties of source objects to a destination object. | [homepage](https://github.com/jonschlinkert/assign-deep)
+* [clone-deep](https://www.npmjs.com/package/clone-deep): Recursively (deep) clone JavaScript native types, like Object, Array, RegExp, Date as well as primitives. | [homepage](https://github.com/jonschlinkert/clone-deep)
+* [extend-shallow](https://www.npmjs.com/package/extend-shallow): Extend an object with the properties of additional objects. node.js/javascript util. | [homepage](https://github.com/jonschlinkert/extend-shallow)
+* [merge-deep](https://www.npmjs.com/package/merge-deep): Recursively merge values in a javascript object. | [homepage](https://github.com/jonschlinkert/merge-deep)
+* [mixin-deep](https://www.npmjs.com/package/mixin-deep): Deeply mix the properties of objects into the first object. Like merge-deep, but doesn't clone. | [homepage](https://github.com/jonschlinkert/mixin-deep)
+
+## Running tests
+
+Install dev dependencies:
+
+```sh
+$ npm i -d && npm test
+```
+
+## Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](https://github.com/jonschlinkert/assign-symbols/issues/new).
+
+## Author
+
+**Jon Schlinkert**
+
++ [github/jonschlinkert](https://github.com/jonschlinkert)
++ [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
+
+## License
+
+Copyright © 2015 Jon Schlinkert
+Released under the MIT license.
+
+***
+
+_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on November 06, 2015._
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/assign-symbols/index.js b/chatto/src/main/javascript/node_modules/assign-symbols/index.js
new file mode 100644
index 0000000..c08a232
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/assign-symbols/index.js
@@ -0,0 +1,40 @@
+/*!
+ * assign-symbols
+ *
+ * Copyright (c) 2015, Jon Schlinkert.
+ * Licensed under the MIT License.
+ */
+
+'use strict';
+
+module.exports = function(receiver, objects) {
+ if (receiver === null || typeof receiver === 'undefined') {
+ throw new TypeError('expected first argument to be an object.');
+ }
+
+ if (typeof objects === 'undefined' || typeof Symbol === 'undefined') {
+ return receiver;
+ }
+
+ if (typeof Object.getOwnPropertySymbols !== 'function') {
+ return receiver;
+ }
+
+ var isEnumerable = Object.prototype.propertyIsEnumerable;
+ var target = Object(receiver);
+ var len = arguments.length, i = 0;
+
+ while (++i < len) {
+ var provider = Object(arguments[i]);
+ var names = Object.getOwnPropertySymbols(provider);
+
+ for (var j = 0; j < names.length; j++) {
+ var key = names[j];
+
+ if (isEnumerable.call(provider, key)) {
+ target[key] = provider[key];
+ }
+ }
+ }
+ return target;
+};
diff --git a/chatto/src/main/javascript/node_modules/assign-symbols/package.json b/chatto/src/main/javascript/node_modules/assign-symbols/package.json
new file mode 100644
index 0000000..40e61a9
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/assign-symbols/package.json
@@ -0,0 +1,71 @@
+{
+ "_from": "assign-symbols@^1.0.0",
+ "_id": "assign-symbols@1.0.0",
+ "_inBundle": false,
+ "_integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
+ "_location": "/assign-symbols",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "assign-symbols@^1.0.0",
+ "name": "assign-symbols",
+ "escapedName": "assign-symbols",
+ "rawSpec": "^1.0.0",
+ "saveSpec": null,
+ "fetchSpec": "^1.0.0"
+ },
+ "_requiredBy": [
+ "/extend-shallow"
+ ],
+ "_resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz",
+ "_shasum": "59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367",
+ "_spec": "assign-symbols@^1.0.0",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/extend-shallow",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/assign-symbols/issues"
+ },
+ "bundleDependencies": false,
+ "deprecated": false,
+ "description": "Assign the enumerable es6 Symbol properties from an object (or objects) to the first object passed on the arguments. Can be used as a supplement to other extend, assign or merge methods as a polyfill for the Symbols part of the es6 Object.assign method.",
+ "devDependencies": {
+ "mocha": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/jonschlinkert/assign-symbols",
+ "keywords": [
+ "assign",
+ "symbols"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "assign-symbols",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/assign-symbols.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "related": {
+ "list": [
+ "assign-deep",
+ "mixin-deep",
+ "merge-deep",
+ "extend-shallow",
+ "clone-deep"
+ ]
+ }
+ },
+ "version": "1.0.0"
+}
diff --git a/chatto/src/main/javascript/node_modules/atob/LICENSE b/chatto/src/main/javascript/node_modules/atob/LICENSE
new file mode 100644
index 0000000..2d9338b
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/atob/LICENSE
@@ -0,0 +1,230 @@
+At your option you may choose either of the following licenses:
+
+ * The MIT License (MIT)
+ * The Apache License 2.0 (Apache-2.0)
+
+
+The MIT License (MIT)
+
+Copyright (c) 2015 AJ ONeal
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2015 AJ ONeal
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/chatto/src/main/javascript/node_modules/atob/LICENSE.DOCS b/chatto/src/main/javascript/node_modules/atob/LICENSE.DOCS
new file mode 100644
index 0000000..1d658d6
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/atob/LICENSE.DOCS
@@ -0,0 +1,319 @@
+Creative Commons Legal Code
+
+Attribution 3.0 Unported
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
+ DAMAGES RESULTING FROM ITS USE.
+
+License
+
+THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
+COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
+COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
+AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
+
+BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
+TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
+BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
+CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
+CONDITIONS.
+
+1. Definitions
+
+ a. "Adaptation" means a work based upon the Work, or upon the Work and
+ other pre-existing works, such as a translation, adaptation,
+ derivative work, arrangement of music or other alterations of a
+ literary or artistic work, or phonogram or performance and includes
+ cinematographic adaptations or any other form in which the Work may be
+ recast, transformed, or adapted including in any form recognizably
+ derived from the original, except that a work that constitutes a
+ Collection will not be considered an Adaptation for the purpose of
+ this License. For the avoidance of doubt, where the Work is a musical
+ work, performance or phonogram, the synchronization of the Work in
+ timed-relation with a moving image ("synching") will be considered an
+ Adaptation for the purpose of this License.
+ b. "Collection" means a collection of literary or artistic works, such as
+ encyclopedias and anthologies, or performances, phonograms or
+ broadcasts, or other works or subject matter other than works listed
+ in Section 1(f) below, which, by reason of the selection and
+ arrangement of their contents, constitute intellectual creations, in
+ which the Work is included in its entirety in unmodified form along
+ with one or more other contributions, each constituting separate and
+ independent works in themselves, which together are assembled into a
+ collective whole. A work that constitutes a Collection will not be
+ considered an Adaptation (as defined above) for the purposes of this
+ License.
+ c. "Distribute" means to make available to the public the original and
+ copies of the Work or Adaptation, as appropriate, through sale or
+ other transfer of ownership.
+ d. "Licensor" means the individual, individuals, entity or entities that
+ offer(s) the Work under the terms of this License.
+ e. "Original Author" means, in the case of a literary or artistic work,
+ the individual, individuals, entity or entities who created the Work
+ or if no individual or entity can be identified, the publisher; and in
+ addition (i) in the case of a performance the actors, singers,
+ musicians, dancers, and other persons who act, sing, deliver, declaim,
+ play in, interpret or otherwise perform literary or artistic works or
+ expressions of folklore; (ii) in the case of a phonogram the producer
+ being the person or legal entity who first fixes the sounds of a
+ performance or other sounds; and, (iii) in the case of broadcasts, the
+ organization that transmits the broadcast.
+ f. "Work" means the literary and/or artistic work offered under the terms
+ of this License including without limitation any production in the
+ literary, scientific and artistic domain, whatever may be the mode or
+ form of its expression including digital form, such as a book,
+ pamphlet and other writing; a lecture, address, sermon or other work
+ of the same nature; a dramatic or dramatico-musical work; a
+ choreographic work or entertainment in dumb show; a musical
+ composition with or without words; a cinematographic work to which are
+ assimilated works expressed by a process analogous to cinematography;
+ a work of drawing, painting, architecture, sculpture, engraving or
+ lithography; a photographic work to which are assimilated works
+ expressed by a process analogous to photography; a work of applied
+ art; an illustration, map, plan, sketch or three-dimensional work
+ relative to geography, topography, architecture or science; a
+ performance; a broadcast; a phonogram; a compilation of data to the
+ extent it is protected as a copyrightable work; or a work performed by
+ a variety or circus performer to the extent it is not otherwise
+ considered a literary or artistic work.
+ g. "You" means an individual or entity exercising rights under this
+ License who has not previously violated the terms of this License with
+ respect to the Work, or who has received express permission from the
+ Licensor to exercise rights under this License despite a previous
+ violation.
+ h. "Publicly Perform" means to perform public recitations of the Work and
+ to communicate to the public those public recitations, by any means or
+ process, including by wire or wireless means or public digital
+ performances; to make available to the public Works in such a way that
+ members of the public may access these Works from a place and at a
+ place individually chosen by them; to perform the Work to the public
+ by any means or process and the communication to the public of the
+ performances of the Work, including by public digital performance; to
+ broadcast and rebroadcast the Work by any means including signs,
+ sounds or images.
+ i. "Reproduce" means to make copies of the Work by any means including
+ without limitation by sound or visual recordings and the right of
+ fixation and reproducing fixations of the Work, including storage of a
+ protected performance or phonogram in digital form or other electronic
+ medium.
+
+2. Fair Dealing Rights. Nothing in this License is intended to reduce,
+limit, or restrict any uses free from copyright or rights arising from
+limitations or exceptions that are provided for in connection with the
+copyright protection under copyright law or other applicable laws.
+
+3. License Grant. Subject to the terms and conditions of this License,
+Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
+perpetual (for the duration of the applicable copyright) license to
+exercise the rights in the Work as stated below:
+
+ a. to Reproduce the Work, to incorporate the Work into one or more
+ Collections, and to Reproduce the Work as incorporated in the
+ Collections;
+ b. to create and Reproduce Adaptations provided that any such Adaptation,
+ including any translation in any medium, takes reasonable steps to
+ clearly label, demarcate or otherwise identify that changes were made
+ to the original Work. For example, a translation could be marked "The
+ original work was translated from English to Spanish," or a
+ modification could indicate "The original work has been modified.";
+ c. to Distribute and Publicly Perform the Work including as incorporated
+ in Collections; and,
+ d. to Distribute and Publicly Perform Adaptations.
+ e. For the avoidance of doubt:
+
+ i. Non-waivable Compulsory License Schemes. In those jurisdictions in
+ which the right to collect royalties through any statutory or
+ compulsory licensing scheme cannot be waived, the Licensor
+ reserves the exclusive right to collect such royalties for any
+ exercise by You of the rights granted under this License;
+ ii. Waivable Compulsory License Schemes. In those jurisdictions in
+ which the right to collect royalties through any statutory or
+ compulsory licensing scheme can be waived, the Licensor waives the
+ exclusive right to collect such royalties for any exercise by You
+ of the rights granted under this License; and,
+ iii. Voluntary License Schemes. The Licensor waives the right to
+ collect royalties, whether individually or, in the event that the
+ Licensor is a member of a collecting society that administers
+ voluntary licensing schemes, via that society, from any exercise
+ by You of the rights granted under this License.
+
+The above rights may be exercised in all media and formats whether now
+known or hereafter devised. The above rights include the right to make
+such modifications as are technically necessary to exercise the rights in
+other media and formats. Subject to Section 8(f), all rights not expressly
+granted by Licensor are hereby reserved.
+
+4. Restrictions. The license granted in Section 3 above is expressly made
+subject to and limited by the following restrictions:
+
+ a. You may Distribute or Publicly Perform the Work only under the terms
+ of this License. You must include a copy of, or the Uniform Resource
+ Identifier (URI) for, this License with every copy of the Work You
+ Distribute or Publicly Perform. You may not offer or impose any terms
+ on the Work that restrict the terms of this License or the ability of
+ the recipient of the Work to exercise the rights granted to that
+ recipient under the terms of the License. You may not sublicense the
+ Work. You must keep intact all notices that refer to this License and
+ to the disclaimer of warranties with every copy of the Work You
+ Distribute or Publicly Perform. When You Distribute or Publicly
+ Perform the Work, You may not impose any effective technological
+ measures on the Work that restrict the ability of a recipient of the
+ Work from You to exercise the rights granted to that recipient under
+ the terms of the License. This Section 4(a) applies to the Work as
+ incorporated in a Collection, but this does not require the Collection
+ apart from the Work itself to be made subject to the terms of this
+ License. If You create a Collection, upon notice from any Licensor You
+ must, to the extent practicable, remove from the Collection any credit
+ as required by Section 4(b), as requested. If You create an
+ Adaptation, upon notice from any Licensor You must, to the extent
+ practicable, remove from the Adaptation any credit as required by
+ Section 4(b), as requested.
+ b. If You Distribute, or Publicly Perform the Work or any Adaptations or
+ Collections, You must, unless a request has been made pursuant to
+ Section 4(a), keep intact all copyright notices for the Work and
+ provide, reasonable to the medium or means You are utilizing: (i) the
+ name of the Original Author (or pseudonym, if applicable) if supplied,
+ and/or if the Original Author and/or Licensor designate another party
+ or parties (e.g., a sponsor institute, publishing entity, journal) for
+ attribution ("Attribution Parties") in Licensor's copyright notice,
+ terms of service or by other reasonable means, the name of such party
+ or parties; (ii) the title of the Work if supplied; (iii) to the
+ extent reasonably practicable, the URI, if any, that Licensor
+ specifies to be associated with the Work, unless such URI does not
+ refer to the copyright notice or licensing information for the Work;
+ and (iv) , consistent with Section 3(b), in the case of an Adaptation,
+ a credit identifying the use of the Work in the Adaptation (e.g.,
+ "French translation of the Work by Original Author," or "Screenplay
+ based on original Work by Original Author"). The credit required by
+ this Section 4 (b) may be implemented in any reasonable manner;
+ provided, however, that in the case of a Adaptation or Collection, at
+ a minimum such credit will appear, if a credit for all contributing
+ authors of the Adaptation or Collection appears, then as part of these
+ credits and in a manner at least as prominent as the credits for the
+ other contributing authors. For the avoidance of doubt, You may only
+ use the credit required by this Section for the purpose of attribution
+ in the manner set out above and, by exercising Your rights under this
+ License, You may not implicitly or explicitly assert or imply any
+ connection with, sponsorship or endorsement by the Original Author,
+ Licensor and/or Attribution Parties, as appropriate, of You or Your
+ use of the Work, without the separate, express prior written
+ permission of the Original Author, Licensor and/or Attribution
+ Parties.
+ c. Except as otherwise agreed in writing by the Licensor or as may be
+ otherwise permitted by applicable law, if You Reproduce, Distribute or
+ Publicly Perform the Work either by itself or as part of any
+ Adaptations or Collections, You must not distort, mutilate, modify or
+ take other derogatory action in relation to the Work which would be
+ prejudicial to the Original Author's honor or reputation. Licensor
+ agrees that in those jurisdictions (e.g. Japan), in which any exercise
+ of the right granted in Section 3(b) of this License (the right to
+ make Adaptations) would be deemed to be a distortion, mutilation,
+ modification or other derogatory action prejudicial to the Original
+ Author's honor and reputation, the Licensor will waive or not assert,
+ as appropriate, this Section, to the fullest extent permitted by the
+ applicable national law, to enable You to reasonably exercise Your
+ right under Section 3(b) of this License (right to make Adaptations)
+ but not otherwise.
+
+5. Representations, Warranties and Disclaimer
+
+UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
+OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
+KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
+INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
+FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
+LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
+WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
+OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
+
+6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
+LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
+ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
+ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+7. Termination
+
+ a. This License and the rights granted hereunder will terminate
+ automatically upon any breach by You of the terms of this License.
+ Individuals or entities who have received Adaptations or Collections
+ from You under this License, however, will not have their licenses
+ terminated provided such individuals or entities remain in full
+ compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
+ survive any termination of this License.
+ b. Subject to the above terms and conditions, the license granted here is
+ perpetual (for the duration of the applicable copyright in the Work).
+ Notwithstanding the above, Licensor reserves the right to release the
+ Work under different license terms or to stop distributing the Work at
+ any time; provided, however that any such election will not serve to
+ withdraw this License (or any other license that has been, or is
+ required to be, granted under the terms of this License), and this
+ License will continue in full force and effect unless terminated as
+ stated above.
+
+8. Miscellaneous
+
+ a. Each time You Distribute or Publicly Perform the Work or a Collection,
+ the Licensor offers to the recipient a license to the Work on the same
+ terms and conditions as the license granted to You under this License.
+ b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
+ offers to the recipient a license to the original Work on the same
+ terms and conditions as the license granted to You under this License.
+ c. If any provision of this License is invalid or unenforceable under
+ applicable law, it shall not affect the validity or enforceability of
+ the remainder of the terms of this License, and without further action
+ by the parties to this agreement, such provision shall be reformed to
+ the minimum extent necessary to make such provision valid and
+ enforceable.
+ d. No term or provision of this License shall be deemed waived and no
+ breach consented to unless such waiver or consent shall be in writing
+ and signed by the party to be charged with such waiver or consent.
+ e. This License constitutes the entire agreement between the parties with
+ respect to the Work licensed here. There are no understandings,
+ agreements or representations with respect to the Work not specified
+ here. Licensor shall not be bound by any additional provisions that
+ may appear in any communication from You. This License may not be
+ modified without the mutual written agreement of the Licensor and You.
+ f. The rights granted under, and the subject matter referenced, in this
+ License were drafted utilizing the terminology of the Berne Convention
+ for the Protection of Literary and Artistic Works (as amended on
+ September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
+ Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
+ and the Universal Copyright Convention (as revised on July 24, 1971).
+ These rights and subject matter take effect in the relevant
+ jurisdiction in which the License terms are sought to be enforced
+ according to the corresponding provisions of the implementation of
+ those treaty provisions in the applicable national law. If the
+ standard suite of rights granted under applicable copyright law
+ includes additional rights not granted under this License, such
+ additional rights are deemed to be included in the License; this
+ License is not intended to restrict the license of any rights under
+ applicable law.
+
+
+Creative Commons Notice
+
+ Creative Commons is not a party to this License, and makes no warranty
+ whatsoever in connection with the Work. Creative Commons will not be
+ liable to You or any party on any legal theory for any damages
+ whatsoever, including without limitation any general, special,
+ incidental or consequential damages arising in connection to this
+ license. Notwithstanding the foregoing two (2) sentences, if Creative
+ Commons has expressly identified itself as the Licensor hereunder, it
+ shall have all rights and obligations of Licensor.
+
+ Except for the limited purpose of indicating to the public that the
+ Work is licensed under the CCPL, Creative Commons does not authorize
+ the use by either party of the trademark "Creative Commons" or any
+ related trademark or logo of Creative Commons without the prior
+ written consent of Creative Commons. Any permitted use will be in
+ compliance with Creative Commons' then-current trademark usage
+ guidelines, as may be published on its website or otherwise made
+ available upon request from time to time. For the avoidance of doubt,
+ this trademark restriction does not form part of this License.
+
+ Creative Commons may be contacted at http://creativecommons.org/.
diff --git a/chatto/src/main/javascript/node_modules/atob/README.md b/chatto/src/main/javascript/node_modules/atob/README.md
new file mode 100644
index 0000000..e15ef86
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/atob/README.md
@@ -0,0 +1,49 @@
+atob
+===
+
+| **atob**
+| [btoa](https://git.coolaj86.com/coolaj86/btoa.js)
+| [unibabel.js](https://git.coolaj86.com/coolaj86/unibabel.js)
+| Sponsored by [ppl](https://ppl.family)
+
+Uses `Buffer` to emulate the exact functionality of the browser's atob.
+
+Note: Unicode may be handled incorrectly (like the browser).
+
+It turns base64-encoded ascii data back **to** binary.
+
+```javascript
+(function () {
+ "use strict";
+
+ var atob = require('atob');
+ var b64 = "SGVsbG8sIFdvcmxkIQ==";
+ var bin = atob(b64);
+
+ console.log(bin); // "Hello, World!"
+}());
+```
+
+### Need Unicode and Binary Support in the Browser?
+
+Check out [unibabel.js](https://git.coolaj86.com/coolaj86/unibabel.js)
+
+Changelog
+=======
+
+ * v2.1.0 address a few issues and PRs, update URLs
+ * v2.0.0 provide browser version for ios web workers
+ * v1.2.0 provide (empty) browser version
+ * v1.1.3 add MIT license
+ * v1.1.2 node only
+
+LICENSE
+=======
+
+Code copyright 2012-2018 AJ ONeal
+
+Dual-licensed MIT and Apache-2.0
+
+Docs copyright 2012-2018 AJ ONeal
+
+Docs released under [Creative Commons](https://git.coolaj86.com/coolaj86/atob.js/blob/master/LICENSE.DOCS).
diff --git a/chatto/src/main/javascript/node_modules/atob/bin/atob.js b/chatto/src/main/javascript/node_modules/atob/bin/atob.js
new file mode 100755
index 0000000..a56ac2e
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/atob/bin/atob.js
@@ -0,0 +1,6 @@
+#!/usr/bin/env node
+'use strict';
+
+var atob = require('../node-atob');
+var str = process.argv[2];
+console.log(atob(str));
diff --git a/chatto/src/main/javascript/node_modules/atob/bower.json b/chatto/src/main/javascript/node_modules/atob/bower.json
new file mode 100644
index 0000000..e3ef66e
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/atob/bower.json
@@ -0,0 +1,24 @@
+{
+ "name": "atob",
+ "description": "atob for isomorphic environments",
+ "main": "browser-atob.js",
+ "authors": [
+ "AJ ONeal (https://coolaj86.com)"
+ ],
+ "license": "(MIT OR Apache-2.0)",
+ "keywords": [
+ "atob",
+ "browser"
+ ],
+ "homepage": "https://github.com/node-browser-compat/atob",
+ "moduleType": [
+ "globals"
+ ],
+ "ignore": [
+ "**/.*",
+ "node_modules",
+ "bower_components",
+ "test",
+ "tests"
+ ]
+}
diff --git a/chatto/src/main/javascript/node_modules/atob/browser-atob.js b/chatto/src/main/javascript/node_modules/atob/browser-atob.js
new file mode 100644
index 0000000..af4f357
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/atob/browser-atob.js
@@ -0,0 +1,44 @@
+(function (w) {
+ "use strict";
+
+ function findBest(atobNative) {
+ // normal window
+ if ('function' === typeof atobNative) { return atobNative; }
+
+
+ // browserify (web worker)
+ if ('function' === typeof Buffer) {
+ return function atobBrowserify(a) {
+ //!! Deliberately using an API that's deprecated in node.js because
+ //!! this file is for browsers and we expect them to cope with it.
+ //!! Discussion: github.com/node-browser-compat/atob/pull/9
+ return new Buffer(a, 'base64').toString('binary');
+ };
+ }
+
+ // ios web worker with base64js
+ if ('object' === typeof w.base64js) {
+ // bufferToBinaryString
+ // https://git.coolaj86.com/coolaj86/unibabel.js/blob/master/index.js#L50
+ return function atobWebWorker_iOS(a) {
+ var buf = w.base64js.b64ToByteArray(a);
+ return Array.prototype.map.call(buf, function (ch) {
+ return String.fromCharCode(ch);
+ }).join('');
+ };
+ }
+
+ return function () {
+ // ios web worker without base64js
+ throw new Error("You're probably in an old browser or an iOS webworker." +
+ " It might help to include beatgammit's base64-js.");
+ };
+ }
+
+ var atobBest = findBest(w.atob);
+ w.atob = atobBest;
+
+ if ((typeof module === 'object') && module && module.exports) {
+ module.exports = atobBest;
+ }
+}(window));
diff --git a/chatto/src/main/javascript/node_modules/atob/node-atob.js b/chatto/src/main/javascript/node_modules/atob/node-atob.js
new file mode 100644
index 0000000..d7305a3
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/atob/node-atob.js
@@ -0,0 +1,7 @@
+"use strict";
+
+function atob(str) {
+ return Buffer.from(str, 'base64').toString('binary');
+}
+
+module.exports = atob.atob = atob;
diff --git a/chatto/src/main/javascript/node_modules/atob/package.json b/chatto/src/main/javascript/node_modules/atob/package.json
new file mode 100644
index 0000000..0b27dad
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/atob/package.json
@@ -0,0 +1,53 @@
+{
+ "_from": "atob@^2.1.1",
+ "_id": "atob@2.1.2",
+ "_inBundle": false,
+ "_integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
+ "_location": "/atob",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "atob@^2.1.1",
+ "name": "atob",
+ "escapedName": "atob",
+ "rawSpec": "^2.1.1",
+ "saveSpec": null,
+ "fetchSpec": "^2.1.1"
+ },
+ "_requiredBy": [
+ "/source-map-resolve"
+ ],
+ "_resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
+ "_shasum": "6d9517eb9e030d2436666651e86bd9f6f13533c9",
+ "_spec": "atob@^2.1.1",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/source-map-resolve",
+ "author": {
+ "name": "AJ ONeal",
+ "email": "coolaj86@gmail.com",
+ "url": "https://coolaj86.com"
+ },
+ "bin": {
+ "atob": "bin/atob.js"
+ },
+ "browser": "browser-atob.js",
+ "bundleDependencies": false,
+ "deprecated": false,
+ "description": "atob for Node.JS and Linux / Mac / Windows CLI (it's a one-liner)",
+ "engines": {
+ "node": ">= 4.5.0"
+ },
+ "homepage": "https://git.coolaj86.com/coolaj86/atob.js.git",
+ "keywords": [
+ "atob",
+ "browser"
+ ],
+ "license": "(MIT OR Apache-2.0)",
+ "main": "node-atob.js",
+ "name": "atob",
+ "repository": {
+ "type": "git",
+ "url": "git://git.coolaj86.com/coolaj86/atob.js.git"
+ },
+ "version": "2.1.2"
+}
diff --git a/chatto/src/main/javascript/node_modules/atob/test.js b/chatto/src/main/javascript/node_modules/atob/test.js
new file mode 100644
index 0000000..bd80a4e
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/atob/test.js
@@ -0,0 +1,18 @@
+(function () {
+ "use strict";
+
+ var atob = require('.');
+ var encoded = "SGVsbG8sIFdvcmxkIQ=="
+ var unencoded = "Hello, World!";
+ /*
+ , encoded = "SGVsbG8sIBZM"
+ , unencoded = "Hello, 世界"
+ */
+
+ if (unencoded !== atob(encoded)) {
+ console.log('[FAIL]', unencoded, atob(encoded));
+ return;
+ }
+
+ console.log('[PASS] all tests pass');
+}());
diff --git a/chatto/src/main/javascript/node_modules/base/LICENSE b/chatto/src/main/javascript/node_modules/base/LICENSE
new file mode 100644
index 0000000..e33d14b
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-2017, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chatto/src/main/javascript/node_modules/base/README.md b/chatto/src/main/javascript/node_modules/base/README.md
new file mode 100644
index 0000000..c77cdaf
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/README.md
@@ -0,0 +1,491 @@
+
+
+
+
+
+
+# base [![NPM version](https://img.shields.io/npm/v/base.svg?style=flat)](https://www.npmjs.com/package/base) [![NPM monthly downloads](https://img.shields.io/npm/dm/base.svg?style=flat)](https://npmjs.org/package/base) [![NPM total downloads](https://img.shields.io/npm/dt/base.svg?style=flat)](https://npmjs.org/package/base) [![Linux Build Status](https://img.shields.io/travis/node-base/base.svg?style=flat&label=Travis)](https://travis-ci.org/node-base/base)
+
+> base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting with a handful of common methods, like `set`, `get`, `del` and `use`.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save base
+```
+
+## What is Base?
+
+Base is a framework for rapidly creating high quality node.js applications, using plugins like building blocks.
+
+### Guiding principles
+
+The core team follows these principles to help guide API decisions:
+
+* **Compact API surface**: The smaller the API surface, the easier the library will be to learn and use.
+* **Easy to extend**: Implementors can use any npm package, and write plugins in pure JavaScript. If you're building complex apps, Base simplifies inheritance.
+* **Easy to test**: No special setup should be required to unit test `Base` or base plugins
+
+### Minimal API surface
+
+[The API](#api) was designed to provide only the minimum necessary functionality for creating a useful application, with or without [plugins](#plugins).
+
+**Base core**
+
+Base itself ships with only a handful of [useful methods](#api), such as:
+
+* `.set`: for setting values on the instance
+* `.get`: for getting values from the instance
+* `.has`: to check if a property exists on the instance
+* `.define`: for setting non-enumerable values on the instance
+* `.use`: for adding plugins
+
+**Be generic**
+
+When deciding on method to add or remove, we try to answer these questions:
+
+1. Will all or most Base applications need this method?
+2. Will this method encourage practices or enforce conventions that are beneficial to implementors?
+3. Can or should this be done in a plugin instead?
+
+### Composability
+
+**Plugin system**
+
+It couldn't be easier to extend Base with any features or custom functionality you can think of.
+
+Base plugins are just functions that take an instance of `Base`:
+
+```js
+var base = new Base();
+
+function plugin(base) {
+ // do plugin stuff, in pure JavaScript
+}
+// use the plugin
+base.use(plugin);
+```
+
+**Inheritance**
+
+Easily inherit Base using `.extend`:
+
+```js
+var Base = require('base');
+
+function MyApp() {
+ Base.call(this);
+}
+Base.extend(MyApp);
+
+var app = new MyApp();
+app.set('a', 'b');
+app.get('a');
+//=> 'b';
+```
+
+**Inherit or instantiate with a namespace**
+
+By default, the `.get`, `.set` and `.has` methods set and get values from the root of the `base` instance. You can customize this using the `.namespace` method exposed on the exported function. For example:
+
+```js
+var Base = require('base');
+// get and set values on the `base.cache` object
+var base = Base.namespace('cache');
+
+var app = base();
+app.set('foo', 'bar');
+console.log(app.cache.foo);
+//=> 'bar'
+```
+
+## API
+
+**Usage**
+
+```js
+var Base = require('base');
+var app = new Base();
+app.set('foo', 'bar');
+console.log(app.foo);
+//=> 'bar'
+```
+
+### [Base](index.js#L44)
+
+Create an instance of `Base` with the given `config` and `options`.
+
+**Params**
+
+* `config` **{Object}**: If supplied, this object is passed to [cache-base](https://github.com/jonschlinkert/cache-base) to merge onto the the instance upon instantiation.
+* `options` **{Object}**: If supplied, this object is used to initialize the `base.options` object.
+
+**Example**
+
+```js
+// initialize with `config` and `options`
+var app = new Base({isApp: true}, {abc: true});
+app.set('foo', 'bar');
+
+// values defined with the given `config` object will be on the root of the instance
+console.log(app.baz); //=> undefined
+console.log(app.foo); //=> 'bar'
+// or use `.get`
+console.log(app.get('isApp')); //=> true
+console.log(app.get('foo')); //=> 'bar'
+
+// values defined with the given `options` object will be on `app.options
+console.log(app.options.abc); //=> true
+```
+
+### [.is](index.js#L107)
+
+Set the given `name` on `app._name` and `app.is*` properties. Used for doing lookups in plugins.
+
+**Params**
+
+* `name` **{String}**
+* `returns` **{Boolean}**
+
+**Example**
+
+```js
+app.is('foo');
+console.log(app._name);
+//=> 'foo'
+console.log(app.isFoo);
+//=> true
+app.is('bar');
+console.log(app.isFoo);
+//=> true
+console.log(app.isBar);
+//=> true
+console.log(app._name);
+//=> 'bar'
+```
+
+### [.isRegistered](index.js#L145)
+
+Returns true if a plugin has already been registered on an instance.
+
+Plugin implementors are encouraged to use this first thing in a plugin
+to prevent the plugin from being called more than once on the same
+instance.
+
+**Params**
+
+* `name` **{String}**: The plugin name.
+* `register` **{Boolean}**: If the plugin if not already registered, to record it as being registered pass `true` as the second argument.
+* `returns` **{Boolean}**: Returns true if a plugin is already registered.
+
+**Events**
+
+* `emits`: `plugin` Emits the name of the plugin being registered. Useful for unit tests, to ensure plugins are only registered once.
+
+**Example**
+
+```js
+var base = new Base();
+base.use(function(app) {
+ if (app.isRegistered('myPlugin')) return;
+ // do stuff to `app`
+});
+
+// to also record the plugin as being registered
+base.use(function(app) {
+ if (app.isRegistered('myPlugin', true)) return;
+ // do stuff to `app`
+});
+```
+
+### [.use](index.js#L175)
+
+Define a plugin function to be called immediately upon init. Plugins are chainable and expose the following arguments to the plugin function:
+
+* `app`: the current instance of `Base`
+* `base`: the [first ancestor instance](#base) of `Base`
+
+**Params**
+
+* `fn` **{Function}**: plugin function to call
+* `returns` **{Object}**: Returns the item instance for chaining.
+
+**Example**
+
+```js
+var app = new Base()
+ .use(foo)
+ .use(bar)
+ .use(baz)
+```
+
+### [.define](index.js#L197)
+
+The `.define` method is used for adding non-enumerable property on the instance. Dot-notation is **not supported** with `define`.
+
+**Params**
+
+* `key` **{String}**: The name of the property to define.
+* `value` **{any}**
+* `returns` **{Object}**: Returns the instance for chaining.
+
+**Example**
+
+```js
+// arbitrary `render` function using lodash `template`
+app.define('render', function(str, locals) {
+ return _.template(str)(locals);
+});
+```
+
+### [.mixin](index.js#L222)
+
+Mix property `key` onto the Base prototype. If base is inherited using `Base.extend` this method will be overridden by a new `mixin` method that will only add properties to the prototype of the inheriting application.
+
+**Params**
+
+* `key` **{String}**
+* `val` **{Object|Array}**
+* `returns` **{Object}**: Returns the `base` instance for chaining.
+
+**Example**
+
+```js
+app.mixin('foo', function() {
+ // do stuff
+});
+```
+
+### [.base](index.js#L268)
+
+Getter/setter used when creating nested instances of `Base`, for storing a reference to the first ancestor instance. This works by setting an instance of `Base` on the `parent` property of a "child" instance. The `base` property defaults to the current instance if no `parent` property is defined.
+
+**Example**
+
+```js
+// create an instance of `Base`, this is our first ("base") instance
+var first = new Base();
+first.foo = 'bar'; // arbitrary property, to make it easier to see what's happening later
+
+// create another instance
+var second = new Base();
+// create a reference to the first instance (`first`)
+second.parent = first;
+
+// create another instance
+var third = new Base();
+// create a reference to the previous instance (`second`)
+// repeat this pattern every time a "child" instance is created
+third.parent = second;
+
+// we can always access the first instance using the `base` property
+console.log(first.base.foo);
+//=> 'bar'
+console.log(second.base.foo);
+//=> 'bar'
+console.log(third.base.foo);
+//=> 'bar'
+// and now you know how to get to third base ;)
+```
+
+### [#use](index.js#L293)
+
+Static method for adding global plugin functions that will be added to an instance when created.
+
+**Params**
+
+* `fn` **{Function}**: Plugin function to use on each instance.
+* `returns` **{Object}**: Returns the `Base` constructor for chaining
+
+**Example**
+
+```js
+Base.use(function(app) {
+ app.foo = 'bar';
+});
+var app = new Base();
+console.log(app.foo);
+//=> 'bar'
+```
+
+### [#extend](index.js#L337)
+
+Static method for inheriting the prototype and static methods of the `Base` class. This method greatly simplifies the process of creating inheritance-based applications. See [static-extend](https://github.com/jonschlinkert/static-extend) for more details.
+
+**Params**
+
+* `Ctor` **{Function}**: constructor to extend
+* `methods` **{Object}**: Optional prototype properties to mix in.
+* `returns` **{Object}**: Returns the `Base` constructor for chaining
+
+**Example**
+
+```js
+var extend = cu.extend(Parent);
+Parent.extend(Child);
+
+// optional methods
+Parent.extend(Child, {
+ foo: function() {},
+ bar: function() {}
+});
+```
+
+### [#mixin](index.js#L379)
+
+Used for adding methods to the `Base` prototype, and/or to the prototype of child instances. When a mixin function returns a function, the returned function is pushed onto the `.mixins` array, making it available to be used on inheriting classes whenever `Base.mixins()` is called (e.g. `Base.mixins(Child)`).
+
+**Params**
+
+* `fn` **{Function}**: Function to call
+* `returns` **{Object}**: Returns the `Base` constructor for chaining
+
+**Example**
+
+```js
+Base.mixin(function(proto) {
+ proto.foo = function(msg) {
+ return 'foo ' + msg;
+ };
+});
+```
+
+### [#mixins](index.js#L401)
+
+Static method for running global mixin functions against a child constructor. Mixins must be registered before calling this method.
+
+**Params**
+
+* `Child` **{Function}**: Constructor function of a child class
+* `returns` **{Object}**: Returns the `Base` constructor for chaining
+
+**Example**
+
+```js
+Base.extend(Child);
+Base.mixins(Child);
+```
+
+### [#inherit](index.js#L420)
+
+Similar to `util.inherit`, but copies all static properties, prototype properties, and getters/setters from `Provider` to `Receiver`. See [class-utils](https://github.com/jonschlinkert/class-utils#inherit) for more details.
+
+**Params**
+
+* `Receiver` **{Function}**: Receiving (child) constructor
+* `Provider` **{Function}**: Providing (parent) constructor
+* `returns` **{Object}**: Returns the `Base` constructor for chaining
+
+**Example**
+
+```js
+Base.inherit(Foo, Bar);
+```
+
+## In the wild
+
+The following node.js applications were built with `Base`:
+
+* [assemble](https://github.com/assemble/assemble)
+* [verb](https://github.com/verbose/verb)
+* [generate](https://github.com/generate/generate)
+* [scaffold](https://github.com/jonschlinkert/scaffold)
+* [boilerplate](https://github.com/jonschlinkert/boilerplate)
+
+## Test coverage
+
+```
+Statements : 98.91% ( 91/92 )
+Branches : 92.86% ( 26/28 )
+Functions : 100% ( 17/17 )
+Lines : 98.9% ( 90/91 )
+```
+
+## History
+
+### v0.11.2
+
+* fixes https://github.com/micromatch/micromatch/issues/99
+
+### v0.11.0
+
+**Breaking changes**
+
+* Static `.use` and `.run` methods are now non-enumerable
+
+### v0.9.0
+
+**Breaking changes**
+
+* `.is` no longer takes a function, a string must be passed
+* all remaining `.debug` code has been removed
+* `app._namespace` was removed (related to `debug`)
+* `.plugin`, `.use`, and `.define` no longer emit events
+* `.assertPlugin` was removed
+* `.lazy` was removed
+
+## About
+
+### Related projects
+
+* [base-cwd](https://www.npmjs.com/package/base-cwd): Base plugin that adds a getter/setter for the current working directory. | [homepage](https://github.com/node-base/base-cwd "Base plugin that adds a getter/setter for the current working directory.")
+* [base-data](https://www.npmjs.com/package/base-data): adds a `data` method to base-methods. | [homepage](https://github.com/node-base/base-data "adds a `data` method to base-methods.")
+* [base-fs](https://www.npmjs.com/package/base-fs): base-methods plugin that adds vinyl-fs methods to your 'base' application for working with the file… [more](https://github.com/node-base/base-fs) | [homepage](https://github.com/node-base/base-fs "base-methods plugin that adds vinyl-fs methods to your 'base' application for working with the file system, like src, dest, copy and symlink.")
+* [base-generators](https://www.npmjs.com/package/base-generators): Adds project-generator support to your `base` application. | [homepage](https://github.com/node-base/base-generators "Adds project-generator support to your `base` application.")
+* [base-option](https://www.npmjs.com/package/base-option): Adds a few options methods to base, like `option`, `enable` and `disable`. See the readme… [more](https://github.com/node-base/base-option) | [homepage](https://github.com/node-base/base-option "Adds a few options methods to base, like `option`, `enable` and `disable`. See the readme for the full API.")
+* [base-pipeline](https://www.npmjs.com/package/base-pipeline): base-methods plugin that adds pipeline and plugin methods for dynamically composing streaming plugin pipelines. | [homepage](https://github.com/node-base/base-pipeline "base-methods plugin that adds pipeline and plugin methods for dynamically composing streaming plugin pipelines.")
+* [base-pkg](https://www.npmjs.com/package/base-pkg): Plugin for adding a `pkg` method that exposes pkg-store to your base application. | [homepage](https://github.com/node-base/base-pkg "Plugin for adding a `pkg` method that exposes pkg-store to your base application.")
+* [base-plugins](https://www.npmjs.com/package/base-plugins): Adds 'smart plugin' support to your base application. | [homepage](https://github.com/node-base/base-plugins "Adds 'smart plugin' support to your base application.")
+* [base-questions](https://www.npmjs.com/package/base-questions): Plugin for base-methods that adds methods for prompting the user and storing the answers on… [more](https://github.com/node-base/base-questions) | [homepage](https://github.com/node-base/base-questions "Plugin for base-methods that adds methods for prompting the user and storing the answers on a project-by-project basis.")
+* [base-store](https://www.npmjs.com/package/base-store): Plugin for getting and persisting config values with your base-methods application. Adds a 'store' object… [more](https://github.com/node-base/base-store) | [homepage](https://github.com/node-base/base-store "Plugin for getting and persisting config values with your base-methods application. Adds a 'store' object that exposes all of the methods from the data-store library. Also now supports sub-stores!")
+* [base-task](https://www.npmjs.com/package/base-task): base plugin that provides a very thin wrapper around [https://github.com/doowb/composer](https://github.com/doowb/composer) for adding task methods to… [more](https://github.com/node-base/base-task) | [homepage](https://github.com/node-base/base-task "base plugin that provides a very thin wrapper around for adding task methods to your application.")
+
+### Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+### Contributors
+
+| **Commits** | **Contributor** |
+| --- | --- |
+| 141 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 30 | [doowb](https://github.com/doowb) |
+| 3 | [charlike](https://github.com/charlike) |
+| 1 | [criticalmash](https://github.com/criticalmash) |
+| 1 | [wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg) |
+
+### Building docs
+
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
+
+To generate the readme, run the following command:
+
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
+
+### Running tests
+
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
+
+```sh
+$ npm install && npm test
+```
+
+### Author
+
+**Jon Schlinkert**
+
+* [github/jonschlinkert](https://github.com/jonschlinkert)
+* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
+
+### License
+
+Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on September 07, 2017._
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/base/index.js b/chatto/src/main/javascript/node_modules/base/index.js
new file mode 100644
index 0000000..fb68048
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/index.js
@@ -0,0 +1,435 @@
+'use strict';
+
+var util = require('util');
+var define = require('define-property');
+var CacheBase = require('cache-base');
+var Emitter = require('component-emitter');
+var isObject = require('isobject');
+var merge = require('mixin-deep');
+var pascal = require('pascalcase');
+var cu = require('class-utils');
+
+/**
+ * Optionally define a custom `cache` namespace to use.
+ */
+
+function namespace(name) {
+ var Cache = name ? CacheBase.namespace(name) : CacheBase;
+ var fns = [];
+
+ /**
+ * Create an instance of `Base` with the given `config` and `options`.
+ *
+ * ```js
+ * // initialize with `config` and `options`
+ * var app = new Base({isApp: true}, {abc: true});
+ * app.set('foo', 'bar');
+ *
+ * // values defined with the given `config` object will be on the root of the instance
+ * console.log(app.baz); //=> undefined
+ * console.log(app.foo); //=> 'bar'
+ * // or use `.get`
+ * console.log(app.get('isApp')); //=> true
+ * console.log(app.get('foo')); //=> 'bar'
+ *
+ * // values defined with the given `options` object will be on `app.options
+ * console.log(app.options.abc); //=> true
+ * ```
+ *
+ * @param {Object} `config` If supplied, this object is passed to [cache-base][] to merge onto the the instance upon instantiation.
+ * @param {Object} `options` If supplied, this object is used to initialize the `base.options` object.
+ * @api public
+ */
+
+ function Base(config, options) {
+ if (!(this instanceof Base)) {
+ return new Base(config, options);
+ }
+ Cache.call(this, config);
+ this.is('base');
+ this.initBase(config, options);
+ }
+
+ /**
+ * Inherit cache-base
+ */
+
+ util.inherits(Base, Cache);
+
+ /**
+ * Add static emitter methods
+ */
+
+ Emitter(Base);
+
+ /**
+ * Initialize `Base` defaults with the given `config` object
+ */
+
+ Base.prototype.initBase = function(config, options) {
+ this.options = merge({}, this.options, options);
+ this.cache = this.cache || {};
+ this.define('registered', {});
+ if (name) this[name] = {};
+
+ // make `app._callbacks` non-enumerable
+ this.define('_callbacks', this._callbacks);
+ if (isObject(config)) {
+ this.visit('set', config);
+ }
+ Base.run(this, 'use', fns);
+ };
+
+ /**
+ * Set the given `name` on `app._name` and `app.is*` properties. Used for doing
+ * lookups in plugins.
+ *
+ * ```js
+ * app.is('foo');
+ * console.log(app._name);
+ * //=> 'foo'
+ * console.log(app.isFoo);
+ * //=> true
+ * app.is('bar');
+ * console.log(app.isFoo);
+ * //=> true
+ * console.log(app.isBar);
+ * //=> true
+ * console.log(app._name);
+ * //=> 'bar'
+ * ```
+ * @name .is
+ * @param {String} `name`
+ * @return {Boolean}
+ * @api public
+ */
+
+ Base.prototype.is = function(name) {
+ if (typeof name !== 'string') {
+ throw new TypeError('expected name to be a string');
+ }
+ this.define('is' + pascal(name), true);
+ this.define('_name', name);
+ this.define('_appname', name);
+ return this;
+ };
+
+ /**
+ * Returns true if a plugin has already been registered on an instance.
+ *
+ * Plugin implementors are encouraged to use this first thing in a plugin
+ * to prevent the plugin from being called more than once on the same
+ * instance.
+ *
+ * ```js
+ * var base = new Base();
+ * base.use(function(app) {
+ * if (app.isRegistered('myPlugin')) return;
+ * // do stuff to `app`
+ * });
+ *
+ * // to also record the plugin as being registered
+ * base.use(function(app) {
+ * if (app.isRegistered('myPlugin', true)) return;
+ * // do stuff to `app`
+ * });
+ * ```
+ * @name .isRegistered
+ * @emits `plugin` Emits the name of the plugin being registered. Useful for unit tests, to ensure plugins are only registered once.
+ * @param {String} `name` The plugin name.
+ * @param {Boolean} `register` If the plugin if not already registered, to record it as being registered pass `true` as the second argument.
+ * @return {Boolean} Returns true if a plugin is already registered.
+ * @api public
+ */
+
+ Base.prototype.isRegistered = function(name, register) {
+ if (this.registered.hasOwnProperty(name)) {
+ return true;
+ }
+ if (register !== false) {
+ this.registered[name] = true;
+ this.emit('plugin', name);
+ }
+ return false;
+ };
+
+ /**
+ * Define a plugin function to be called immediately upon init. Plugins are chainable
+ * and expose the following arguments to the plugin function:
+ *
+ * - `app`: the current instance of `Base`
+ * - `base`: the [first ancestor instance](#base) of `Base`
+ *
+ * ```js
+ * var app = new Base()
+ * .use(foo)
+ * .use(bar)
+ * .use(baz)
+ * ```
+ * @name .use
+ * @param {Function} `fn` plugin function to call
+ * @return {Object} Returns the item instance for chaining.
+ * @api public
+ */
+
+ Base.prototype.use = function(fn) {
+ fn.call(this, this);
+ return this;
+ };
+
+ /**
+ * The `.define` method is used for adding non-enumerable property on the instance.
+ * Dot-notation is **not supported** with `define`.
+ *
+ * ```js
+ * // arbitrary `render` function using lodash `template`
+ * app.define('render', function(str, locals) {
+ * return _.template(str)(locals);
+ * });
+ * ```
+ * @name .define
+ * @param {String} `key` The name of the property to define.
+ * @param {any} `value`
+ * @return {Object} Returns the instance for chaining.
+ * @api public
+ */
+
+ Base.prototype.define = function(key, val) {
+ if (isObject(key)) {
+ return this.visit('define', key);
+ }
+ define(this, key, val);
+ return this;
+ };
+
+ /**
+ * Mix property `key` onto the Base prototype. If base is inherited using
+ * `Base.extend` this method will be overridden by a new `mixin` method that will
+ * only add properties to the prototype of the inheriting application.
+ *
+ * ```js
+ * app.mixin('foo', function() {
+ * // do stuff
+ * });
+ * ```
+ * @name .mixin
+ * @param {String} `key`
+ * @param {Object|Array} `val`
+ * @return {Object} Returns the `base` instance for chaining.
+ * @api public
+ */
+
+ Base.prototype.mixin = function(key, val) {
+ Base.prototype[key] = val;
+ return this;
+ };
+
+ /**
+ * Non-enumberable mixin array, used by the static [Base.mixin]() method.
+ */
+
+ Base.prototype.mixins = Base.prototype.mixins || [];
+
+ /**
+ * Getter/setter used when creating nested instances of `Base`, for storing a reference
+ * to the first ancestor instance. This works by setting an instance of `Base` on the `parent`
+ * property of a "child" instance. The `base` property defaults to the current instance if
+ * no `parent` property is defined.
+ *
+ * ```js
+ * // create an instance of `Base`, this is our first ("base") instance
+ * var first = new Base();
+ * first.foo = 'bar'; // arbitrary property, to make it easier to see what's happening later
+ *
+ * // create another instance
+ * var second = new Base();
+ * // create a reference to the first instance (`first`)
+ * second.parent = first;
+ *
+ * // create another instance
+ * var third = new Base();
+ * // create a reference to the previous instance (`second`)
+ * // repeat this pattern every time a "child" instance is created
+ * third.parent = second;
+ *
+ * // we can always access the first instance using the `base` property
+ * console.log(first.base.foo);
+ * //=> 'bar'
+ * console.log(second.base.foo);
+ * //=> 'bar'
+ * console.log(third.base.foo);
+ * //=> 'bar'
+ * // and now you know how to get to third base ;)
+ * ```
+ * @name .base
+ * @api public
+ */
+
+ Object.defineProperty(Base.prototype, 'base', {
+ configurable: true,
+ get: function() {
+ return this.parent ? this.parent.base : this;
+ }
+ });
+
+ /**
+ * Static method for adding global plugin functions that will
+ * be added to an instance when created.
+ *
+ * ```js
+ * Base.use(function(app) {
+ * app.foo = 'bar';
+ * });
+ * var app = new Base();
+ * console.log(app.foo);
+ * //=> 'bar'
+ * ```
+ * @name #use
+ * @param {Function} `fn` Plugin function to use on each instance.
+ * @return {Object} Returns the `Base` constructor for chaining
+ * @api public
+ */
+
+ define(Base, 'use', function(fn) {
+ fns.push(fn);
+ return Base;
+ });
+
+ /**
+ * Run an array of functions by passing each function
+ * to a method on the given object specified by the given property.
+ *
+ * @param {Object} `obj` Object containing method to use.
+ * @param {String} `prop` Name of the method on the object to use.
+ * @param {Array} `arr` Array of functions to pass to the method.
+ */
+
+ define(Base, 'run', function(obj, prop, arr) {
+ var len = arr.length, i = 0;
+ while (len--) {
+ obj[prop](arr[i++]);
+ }
+ return Base;
+ });
+
+ /**
+ * Static method for inheriting the prototype and static methods of the `Base` class.
+ * This method greatly simplifies the process of creating inheritance-based applications.
+ * See [static-extend][] for more details.
+ *
+ * ```js
+ * var extend = cu.extend(Parent);
+ * Parent.extend(Child);
+ *
+ * // optional methods
+ * Parent.extend(Child, {
+ * foo: function() {},
+ * bar: function() {}
+ * });
+ * ```
+ * @name #extend
+ * @param {Function} `Ctor` constructor to extend
+ * @param {Object} `methods` Optional prototype properties to mix in.
+ * @return {Object} Returns the `Base` constructor for chaining
+ * @api public
+ */
+
+ define(Base, 'extend', cu.extend(Base, function(Ctor, Parent) {
+ Ctor.prototype.mixins = Ctor.prototype.mixins || [];
+
+ define(Ctor, 'mixin', function(fn) {
+ var mixin = fn(Ctor.prototype, Ctor);
+ if (typeof mixin === 'function') {
+ Ctor.prototype.mixins.push(mixin);
+ }
+ return Ctor;
+ });
+
+ define(Ctor, 'mixins', function(Child) {
+ Base.run(Child, 'mixin', Ctor.prototype.mixins);
+ return Ctor;
+ });
+
+ Ctor.prototype.mixin = function(key, value) {
+ Ctor.prototype[key] = value;
+ return this;
+ };
+ return Base;
+ }));
+
+ /**
+ * Used for adding methods to the `Base` prototype, and/or to the prototype of child instances.
+ * When a mixin function returns a function, the returned function is pushed onto the `.mixins`
+ * array, making it available to be used on inheriting classes whenever `Base.mixins()` is
+ * called (e.g. `Base.mixins(Child)`).
+ *
+ * ```js
+ * Base.mixin(function(proto) {
+ * proto.foo = function(msg) {
+ * return 'foo ' + msg;
+ * };
+ * });
+ * ```
+ * @name #mixin
+ * @param {Function} `fn` Function to call
+ * @return {Object} Returns the `Base` constructor for chaining
+ * @api public
+ */
+
+ define(Base, 'mixin', function(fn) {
+ var mixin = fn(Base.prototype, Base);
+ if (typeof mixin === 'function') {
+ Base.prototype.mixins.push(mixin);
+ }
+ return Base;
+ });
+
+ /**
+ * Static method for running global mixin functions against a child constructor.
+ * Mixins must be registered before calling this method.
+ *
+ * ```js
+ * Base.extend(Child);
+ * Base.mixins(Child);
+ * ```
+ * @name #mixins
+ * @param {Function} `Child` Constructor function of a child class
+ * @return {Object} Returns the `Base` constructor for chaining
+ * @api public
+ */
+
+ define(Base, 'mixins', function(Child) {
+ Base.run(Child, 'mixin', Base.prototype.mixins);
+ return Base;
+ });
+
+ /**
+ * Similar to `util.inherit`, but copies all static properties, prototype properties, and
+ * getters/setters from `Provider` to `Receiver`. See [class-utils][]{#inherit} for more details.
+ *
+ * ```js
+ * Base.inherit(Foo, Bar);
+ * ```
+ * @name #inherit
+ * @param {Function} `Receiver` Receiving (child) constructor
+ * @param {Function} `Provider` Providing (parent) constructor
+ * @return {Object} Returns the `Base` constructor for chaining
+ * @api public
+ */
+
+ define(Base, 'inherit', cu.inherit);
+ define(Base, 'bubble', cu.bubble);
+ return Base;
+}
+
+/**
+ * Expose `Base` with default settings
+ */
+
+module.exports = namespace();
+
+/**
+ * Allow users to define a namespace
+ */
+
+module.exports.namespace = namespace;
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/define-property/LICENSE b/chatto/src/main/javascript/node_modules/base/node_modules/define-property/LICENSE
new file mode 100644
index 0000000..ec85897
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/define-property/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015, 2017, Jon Schlinkert
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/define-property/README.md b/chatto/src/main/javascript/node_modules/base/node_modules/define-property/README.md
new file mode 100644
index 0000000..2f1af05
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/define-property/README.md
@@ -0,0 +1,95 @@
+# define-property [![NPM version](https://img.shields.io/npm/v/define-property.svg?style=flat)](https://www.npmjs.com/package/define-property) [![NPM monthly downloads](https://img.shields.io/npm/dm/define-property.svg?style=flat)](https://npmjs.org/package/define-property) [![NPM total downloads](https://img.shields.io/npm/dt/define-property.svg?style=flat)](https://npmjs.org/package/define-property) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/define-property.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/define-property)
+
+> Define a non-enumerable property on an object.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save define-property
+```
+
+Install with [yarn](https://yarnpkg.com):
+
+```sh
+$ yarn add define-property
+```
+
+## Usage
+
+**Params**
+
+* `obj`: The object on which to define the property.
+* `prop`: The name of the property to be defined or modified.
+* `descriptor`: The descriptor for the property being defined or modified.
+
+```js
+var define = require('define-property');
+var obj = {};
+define(obj, 'foo', function(val) {
+ return val.toUpperCase();
+});
+
+console.log(obj);
+//=> {}
+
+console.log(obj.foo('bar'));
+//=> 'BAR'
+```
+
+**get/set**
+
+```js
+define(obj, 'foo', {
+ get: function() {},
+ set: function() {}
+});
+```
+
+## About
+
+### Related projects
+
+* [assign-deep](https://www.npmjs.com/package/assign-deep): Deeply assign the enumerable properties and/or es6 Symbol properies of source objects to the target… [more](https://github.com/jonschlinkert/assign-deep) | [homepage](https://github.com/jonschlinkert/assign-deep "Deeply assign the enumerable properties and/or es6 Symbol properies of source objects to the target (first) object.")
+* [extend-shallow](https://www.npmjs.com/package/extend-shallow): Extend an object with the properties of additional objects. node.js/javascript util. | [homepage](https://github.com/jonschlinkert/extend-shallow "Extend an object with the properties of additional objects. node.js/javascript util.")
+* [merge-deep](https://www.npmjs.com/package/merge-deep): Recursively merge values in a javascript object. | [homepage](https://github.com/jonschlinkert/merge-deep "Recursively merge values in a javascript object.")
+* [mixin-deep](https://www.npmjs.com/package/mixin-deep): Deeply mix the properties of objects into the first object. Like merge-deep, but doesn't clone. | [homepage](https://github.com/jonschlinkert/mixin-deep "Deeply mix the properties of objects into the first object. Like merge-deep, but doesn't clone.")
+
+### Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+### Building docs
+
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
+
+To generate the readme, run the following command:
+
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
+
+### Running tests
+
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
+
+```sh
+$ npm install && npm test
+```
+
+### Author
+
+**Jon Schlinkert**
+
+* [github/jonschlinkert](https://github.com/jonschlinkert)
+* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
+
+### License
+
+Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.5.0, on April 20, 2017._
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/define-property/index.js b/chatto/src/main/javascript/node_modules/base/node_modules/define-property/index.js
new file mode 100644
index 0000000..27c19eb
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/define-property/index.js
@@ -0,0 +1,31 @@
+/*!
+ * define-property
+ *
+ * Copyright (c) 2015, 2017, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+'use strict';
+
+var isDescriptor = require('is-descriptor');
+
+module.exports = function defineProperty(obj, prop, val) {
+ if (typeof obj !== 'object' && typeof obj !== 'function') {
+ throw new TypeError('expected an object or function.');
+ }
+
+ if (typeof prop !== 'string') {
+ throw new TypeError('expected `prop` to be a string.');
+ }
+
+ if (isDescriptor(val) && ('set' in val || 'get' in val)) {
+ return Object.defineProperty(obj, prop, val);
+ }
+
+ return Object.defineProperty(obj, prop, {
+ configurable: true,
+ enumerable: false,
+ writable: true,
+ value: val
+ });
+};
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/define-property/package.json b/chatto/src/main/javascript/node_modules/base/node_modules/define-property/package.json
new file mode 100644
index 0000000..0dd9905
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/define-property/package.json
@@ -0,0 +1,93 @@
+{
+ "_from": "define-property@^1.0.0",
+ "_id": "define-property@1.0.0",
+ "_inBundle": false,
+ "_integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
+ "_location": "/base/define-property",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "define-property@^1.0.0",
+ "name": "define-property",
+ "escapedName": "define-property",
+ "rawSpec": "^1.0.0",
+ "saveSpec": null,
+ "fetchSpec": "^1.0.0"
+ },
+ "_requiredBy": [
+ "/base"
+ ],
+ "_resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+ "_shasum": "769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6",
+ "_spec": "define-property@^1.0.0",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/base",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/define-property/issues"
+ },
+ "bundleDependencies": false,
+ "dependencies": {
+ "is-descriptor": "^1.0.0"
+ },
+ "deprecated": false,
+ "description": "Define a non-enumerable property on an object.",
+ "devDependencies": {
+ "gulp-format-md": "^0.1.12",
+ "mocha": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/jonschlinkert/define-property",
+ "keywords": [
+ "define",
+ "define-property",
+ "enumerable",
+ "key",
+ "non",
+ "non-enumerable",
+ "object",
+ "prop",
+ "property",
+ "value"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "define-property",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/define-property.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "related": {
+ "list": [
+ "extend-shallow",
+ "merge-deep",
+ "assign-deep",
+ "mixin-deep"
+ ]
+ },
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "lint": {
+ "reflinks": true
+ }
+ },
+ "version": "1.0.0"
+}
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/is-accessor-descriptor/LICENSE b/chatto/src/main/javascript/node_modules/base/node_modules/is-accessor-descriptor/LICENSE
new file mode 100644
index 0000000..e33d14b
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/is-accessor-descriptor/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-2017, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/is-accessor-descriptor/README.md b/chatto/src/main/javascript/node_modules/base/node_modules/is-accessor-descriptor/README.md
new file mode 100644
index 0000000..d198e1f
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/is-accessor-descriptor/README.md
@@ -0,0 +1,144 @@
+# is-accessor-descriptor [![NPM version](https://img.shields.io/npm/v/is-accessor-descriptor.svg?style=flat)](https://www.npmjs.com/package/is-accessor-descriptor) [![NPM monthly downloads](https://img.shields.io/npm/dm/is-accessor-descriptor.svg?style=flat)](https://npmjs.org/package/is-accessor-descriptor) [![NPM total downloads](https://img.shields.io/npm/dt/is-accessor-descriptor.svg?style=flat)](https://npmjs.org/package/is-accessor-descriptor) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/is-accessor-descriptor.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/is-accessor-descriptor)
+
+> Returns true if a value has the characteristics of a valid JavaScript accessor descriptor.
+
+Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save is-accessor-descriptor
+```
+
+## Usage
+
+```js
+var isAccessor = require('is-accessor-descriptor');
+
+isAccessor({get: function() {}});
+//=> true
+```
+
+You may also pass an object and property name to check if the property is an accessor:
+
+```js
+isAccessor(foo, 'bar');
+```
+
+## Examples
+
+`false` when not an object
+
+```js
+isAccessor('a')
+isAccessor(null)
+isAccessor([])
+//=> false
+```
+
+`true` when the object has valid properties
+
+and the properties all have the correct JavaScript types:
+
+```js
+isAccessor({get: noop, set: noop})
+isAccessor({get: noop})
+isAccessor({set: noop})
+//=> true
+```
+
+`false` when the object has invalid properties
+
+```js
+isAccessor({get: noop, set: noop, bar: 'baz'})
+isAccessor({get: noop, writable: true})
+isAccessor({get: noop, value: true})
+//=> false
+```
+
+`false` when an accessor is not a function
+
+```js
+isAccessor({get: noop, set: 'baz'})
+isAccessor({get: 'foo', set: noop})
+isAccessor({get: 'foo', bar: 'baz'})
+isAccessor({get: 'foo', set: 'baz'})
+//=> false
+```
+
+`false` when a value is not the correct type
+
+```js
+isAccessor({get: noop, set: noop, enumerable: 'foo'})
+isAccessor({set: noop, configurable: 'foo'})
+isAccessor({get: noop, configurable: 'foo'})
+//=> false
+```
+
+## About
+
+
+Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+
+
+
+Running Tests
+
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
+
+```sh
+$ npm install && npm test
+```
+
+
+
+
+Building docs
+
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
+
+To generate the readme, run the following command:
+
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
+
+
+
+### Related projects
+
+You might also be interested in these projects:
+
+* [is-accessor-descriptor](https://www.npmjs.com/package/is-accessor-descriptor): Returns true if a value has the characteristics of a valid JavaScript accessor descriptor. | [homepage](https://github.com/jonschlinkert/is-accessor-descriptor "Returns true if a value has the characteristics of a valid JavaScript accessor descriptor.")
+* [is-data-descriptor](https://www.npmjs.com/package/is-data-descriptor): Returns true if a value has the characteristics of a valid JavaScript data descriptor. | [homepage](https://github.com/jonschlinkert/is-data-descriptor "Returns true if a value has the characteristics of a valid JavaScript data descriptor.")
+* [is-descriptor](https://www.npmjs.com/package/is-descriptor): Returns true if a value has the characteristics of a valid JavaScript descriptor. Works for… [more](https://github.com/jonschlinkert/is-descriptor) | [homepage](https://github.com/jonschlinkert/is-descriptor "Returns true if a value has the characteristics of a valid JavaScript descriptor. Works for data descriptors and accessor descriptors.")
+* [is-plain-object](https://www.npmjs.com/package/is-plain-object): Returns true if an object was created by the `Object` constructor. | [homepage](https://github.com/jonschlinkert/is-plain-object "Returns true if an object was created by the `Object` constructor.")
+* [isobject](https://www.npmjs.com/package/isobject): Returns true if the value is an object and not an array or null. | [homepage](https://github.com/jonschlinkert/isobject "Returns true if the value is an object and not an array or null.")
+
+### Contributors
+
+| **Commits** | **Contributor** |
+| --- | --- |
+| 22 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 2 | [realityking](https://github.com/realityking) |
+
+### Author
+
+**Jon Schlinkert**
+
+* [github/jonschlinkert](https://github.com/jonschlinkert)
+* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
+
+### License
+
+Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on November 01, 2017._
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/is-accessor-descriptor/index.js b/chatto/src/main/javascript/node_modules/base/node_modules/is-accessor-descriptor/index.js
new file mode 100644
index 0000000..d2e6fe8
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/is-accessor-descriptor/index.js
@@ -0,0 +1,69 @@
+/*!
+ * is-accessor-descriptor
+ *
+ * Copyright (c) 2015-2017, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+'use strict';
+
+var typeOf = require('kind-of');
+
+// accessor descriptor properties
+var accessor = {
+ get: 'function',
+ set: 'function',
+ configurable: 'boolean',
+ enumerable: 'boolean'
+};
+
+function isAccessorDescriptor(obj, prop) {
+ if (typeof prop === 'string') {
+ var val = Object.getOwnPropertyDescriptor(obj, prop);
+ return typeof val !== 'undefined';
+ }
+
+ if (typeOf(obj) !== 'object') {
+ return false;
+ }
+
+ if (has(obj, 'value') || has(obj, 'writable')) {
+ return false;
+ }
+
+ if (!has(obj, 'get') || typeof obj.get !== 'function') {
+ return false;
+ }
+
+ // tldr: it's valid to have "set" be undefined
+ // "set" might be undefined if `Object.getOwnPropertyDescriptor`
+ // was used to get the value, and only `get` was defined by the user
+ if (has(obj, 'set') && typeof obj[key] !== 'function' && typeof obj[key] !== 'undefined') {
+ return false;
+ }
+
+ for (var key in obj) {
+ if (!accessor.hasOwnProperty(key)) {
+ continue;
+ }
+
+ if (typeOf(obj[key]) === accessor[key]) {
+ continue;
+ }
+
+ if (typeof obj[key] !== 'undefined') {
+ return false;
+ }
+ }
+ return true;
+}
+
+function has(obj, key) {
+ return {}.hasOwnProperty.call(obj, key);
+}
+
+/**
+ * Expose `isAccessorDescriptor`
+ */
+
+module.exports = isAccessorDescriptor;
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/is-accessor-descriptor/package.json b/chatto/src/main/javascript/node_modules/base/node_modules/is-accessor-descriptor/package.json
new file mode 100644
index 0000000..159e543
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/is-accessor-descriptor/package.json
@@ -0,0 +1,110 @@
+{
+ "_from": "is-accessor-descriptor@^1.0.0",
+ "_id": "is-accessor-descriptor@1.0.0",
+ "_inBundle": false,
+ "_integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
+ "_location": "/base/is-accessor-descriptor",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "is-accessor-descriptor@^1.0.0",
+ "name": "is-accessor-descriptor",
+ "escapedName": "is-accessor-descriptor",
+ "rawSpec": "^1.0.0",
+ "saveSpec": null,
+ "fetchSpec": "^1.0.0"
+ },
+ "_requiredBy": [
+ "/base/is-descriptor"
+ ],
+ "_resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+ "_shasum": "169c2f6d3df1f992618072365c9b0ea1f6878656",
+ "_spec": "is-accessor-descriptor@^1.0.0",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/is-accessor-descriptor/issues"
+ },
+ "bundleDependencies": false,
+ "contributors": [
+ {
+ "name": "Jon Schlinkert",
+ "url": "http://twitter.com/jonschlinkert"
+ },
+ {
+ "name": "Rouven Weßling",
+ "url": "www.rouvenwessling.de"
+ }
+ ],
+ "dependencies": {
+ "kind-of": "^6.0.0"
+ },
+ "deprecated": false,
+ "description": "Returns true if a value has the characteristics of a valid JavaScript accessor descriptor.",
+ "devDependencies": {
+ "gulp-format-md": "^1.0.0",
+ "mocha": "^3.5.3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/jonschlinkert/is-accessor-descriptor",
+ "keywords": [
+ "accessor",
+ "check",
+ "data",
+ "descriptor",
+ "get",
+ "getter",
+ "is",
+ "keys",
+ "object",
+ "properties",
+ "property",
+ "set",
+ "setter",
+ "type",
+ "valid",
+ "value"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "is-accessor-descriptor",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/is-accessor-descriptor.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "related": {
+ "list": [
+ "is-accessor-descriptor",
+ "is-data-descriptor",
+ "is-descriptor",
+ "is-plain-object",
+ "isobject"
+ ]
+ },
+ "lint": {
+ "reflinks": true
+ }
+ },
+ "version": "1.0.0"
+}
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/is-data-descriptor/LICENSE b/chatto/src/main/javascript/node_modules/base/node_modules/is-data-descriptor/LICENSE
new file mode 100644
index 0000000..e33d14b
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/is-data-descriptor/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-2017, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/is-data-descriptor/README.md b/chatto/src/main/javascript/node_modules/base/node_modules/is-data-descriptor/README.md
new file mode 100644
index 0000000..42b0714
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/is-data-descriptor/README.md
@@ -0,0 +1,161 @@
+# is-data-descriptor [![NPM version](https://img.shields.io/npm/v/is-data-descriptor.svg?style=flat)](https://www.npmjs.com/package/is-data-descriptor) [![NPM monthly downloads](https://img.shields.io/npm/dm/is-data-descriptor.svg?style=flat)](https://npmjs.org/package/is-data-descriptor) [![NPM total downloads](https://img.shields.io/npm/dt/is-data-descriptor.svg?style=flat)](https://npmjs.org/package/is-data-descriptor) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/is-data-descriptor.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/is-data-descriptor)
+
+> Returns true if a value has the characteristics of a valid JavaScript data descriptor.
+
+Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save is-data-descriptor
+```
+
+## Usage
+
+```js
+var isDataDesc = require('is-data-descriptor');
+```
+
+## Examples
+
+`true` when the descriptor has valid properties with valid values.
+
+```js
+// `value` can be anything
+isDataDesc({value: 'foo'})
+isDataDesc({value: function() {}})
+isDataDesc({value: true})
+//=> true
+```
+
+`false` when not an object
+
+```js
+isDataDesc('a')
+//=> false
+isDataDesc(null)
+//=> false
+isDataDesc([])
+//=> false
+```
+
+`false` when the object has invalid properties
+
+```js
+isDataDesc({value: 'foo', bar: 'baz'})
+//=> false
+isDataDesc({value: 'foo', bar: 'baz'})
+//=> false
+isDataDesc({value: 'foo', get: function(){}})
+//=> false
+isDataDesc({get: function(){}, value: 'foo'})
+//=> false
+```
+
+`false` when a value is not the correct type
+
+```js
+isDataDesc({value: 'foo', enumerable: 'foo'})
+//=> false
+isDataDesc({value: 'foo', configurable: 'foo'})
+//=> false
+isDataDesc({value: 'foo', writable: 'foo'})
+//=> false
+```
+
+## Valid properties
+
+The only valid data descriptor properties are the following:
+
+* `configurable` (required)
+* `enumerable` (required)
+* `value` (optional)
+* `writable` (optional)
+
+To be a valid data descriptor, either `value` or `writable` must be defined.
+
+**Invalid properties**
+
+A descriptor may have additional _invalid_ properties (an error will **not** be thrown).
+
+```js
+var foo = {};
+
+Object.defineProperty(foo, 'bar', {
+ enumerable: true,
+ whatever: 'blah', // invalid, but doesn't cause an error
+ get: function() {
+ return 'baz';
+ }
+});
+
+console.log(foo.bar);
+//=> 'baz'
+```
+
+## About
+
+
+Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+
+
+
+Running Tests
+
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
+
+```sh
+$ npm install && npm test
+```
+
+
+
+
+Building docs
+
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
+
+To generate the readme, run the following command:
+
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
+
+
+
+### Related projects
+
+You might also be interested in these projects:
+
+* [is-accessor-descriptor](https://www.npmjs.com/package/is-accessor-descriptor): Returns true if a value has the characteristics of a valid JavaScript accessor descriptor. | [homepage](https://github.com/jonschlinkert/is-accessor-descriptor "Returns true if a value has the characteristics of a valid JavaScript accessor descriptor.")
+* [is-data-descriptor](https://www.npmjs.com/package/is-data-descriptor): Returns true if a value has the characteristics of a valid JavaScript data descriptor. | [homepage](https://github.com/jonschlinkert/is-data-descriptor "Returns true if a value has the characteristics of a valid JavaScript data descriptor.")
+* [is-descriptor](https://www.npmjs.com/package/is-descriptor): Returns true if a value has the characteristics of a valid JavaScript descriptor. Works for… [more](https://github.com/jonschlinkert/is-descriptor) | [homepage](https://github.com/jonschlinkert/is-descriptor "Returns true if a value has the characteristics of a valid JavaScript descriptor. Works for data descriptors and accessor descriptors.")
+* [isobject](https://www.npmjs.com/package/isobject): Returns true if the value is an object and not an array or null. | [homepage](https://github.com/jonschlinkert/isobject "Returns true if the value is an object and not an array or null.")
+
+### Contributors
+
+| **Commits** | **Contributor** |
+| --- | --- |
+| 21 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 2 | [realityking](https://github.com/realityking) |
+
+### Author
+
+**Jon Schlinkert**
+
+* [github/jonschlinkert](https://github.com/jonschlinkert)
+* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
+
+### License
+
+Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on November 01, 2017._
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/is-data-descriptor/index.js b/chatto/src/main/javascript/node_modules/base/node_modules/is-data-descriptor/index.js
new file mode 100644
index 0000000..cfeae36
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/is-data-descriptor/index.js
@@ -0,0 +1,49 @@
+/*!
+ * is-data-descriptor
+ *
+ * Copyright (c) 2015-2017, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+'use strict';
+
+var typeOf = require('kind-of');
+
+module.exports = function isDataDescriptor(obj, prop) {
+ // data descriptor properties
+ var data = {
+ configurable: 'boolean',
+ enumerable: 'boolean',
+ writable: 'boolean'
+ };
+
+ if (typeOf(obj) !== 'object') {
+ return false;
+ }
+
+ if (typeof prop === 'string') {
+ var val = Object.getOwnPropertyDescriptor(obj, prop);
+ return typeof val !== 'undefined';
+ }
+
+ if (!('value' in obj) && !('writable' in obj)) {
+ return false;
+ }
+
+ for (var key in obj) {
+ if (key === 'value') continue;
+
+ if (!data.hasOwnProperty(key)) {
+ continue;
+ }
+
+ if (typeOf(obj[key]) === data[key]) {
+ continue;
+ }
+
+ if (typeof obj[key] !== 'undefined') {
+ return false;
+ }
+ }
+ return true;
+};
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/is-data-descriptor/package.json b/chatto/src/main/javascript/node_modules/base/node_modules/is-data-descriptor/package.json
new file mode 100644
index 0000000..3bb4fe4
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/is-data-descriptor/package.json
@@ -0,0 +1,109 @@
+{
+ "_from": "is-data-descriptor@^1.0.0",
+ "_id": "is-data-descriptor@1.0.0",
+ "_inBundle": false,
+ "_integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
+ "_location": "/base/is-data-descriptor",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "is-data-descriptor@^1.0.0",
+ "name": "is-data-descriptor",
+ "escapedName": "is-data-descriptor",
+ "rawSpec": "^1.0.0",
+ "saveSpec": null,
+ "fetchSpec": "^1.0.0"
+ },
+ "_requiredBy": [
+ "/base/is-descriptor"
+ ],
+ "_resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+ "_shasum": "d84876321d0e7add03990406abbbbd36ba9268c7",
+ "_spec": "is-data-descriptor@^1.0.0",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/is-data-descriptor/issues"
+ },
+ "bundleDependencies": false,
+ "contributors": [
+ {
+ "name": "Jon Schlinkert",
+ "url": "http://twitter.com/jonschlinkert"
+ },
+ {
+ "name": "Rouven Weßling",
+ "url": "www.rouvenwessling.de"
+ }
+ ],
+ "dependencies": {
+ "kind-of": "^6.0.0"
+ },
+ "deprecated": false,
+ "description": "Returns true if a value has the characteristics of a valid JavaScript data descriptor.",
+ "devDependencies": {
+ "gulp-format-md": "^1.0.0",
+ "mocha": "^3.5.3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/jonschlinkert/is-data-descriptor",
+ "keywords": [
+ "accessor",
+ "check",
+ "data",
+ "descriptor",
+ "get",
+ "getter",
+ "is",
+ "keys",
+ "object",
+ "properties",
+ "property",
+ "set",
+ "setter",
+ "type",
+ "valid",
+ "value"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "is-data-descriptor",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/is-data-descriptor.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "related": {
+ "list": [
+ "is-accessor-descriptor",
+ "is-data-descriptor",
+ "is-descriptor",
+ "isobject"
+ ]
+ },
+ "lint": {
+ "reflinks": true
+ }
+ },
+ "version": "1.0.0"
+}
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor/LICENSE b/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor/LICENSE
new file mode 100644
index 0000000..c0d7f13
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-2017, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor/README.md b/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor/README.md
new file mode 100644
index 0000000..658e533
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor/README.md
@@ -0,0 +1,193 @@
+# is-descriptor [![NPM version](https://img.shields.io/npm/v/is-descriptor.svg?style=flat)](https://www.npmjs.com/package/is-descriptor) [![NPM monthly downloads](https://img.shields.io/npm/dm/is-descriptor.svg?style=flat)](https://npmjs.org/package/is-descriptor) [![NPM total downloads](https://img.shields.io/npm/dt/is-descriptor.svg?style=flat)](https://npmjs.org/package/is-descriptor) [![Linux Build Status](https://img.shields.io/travis/jonschlinkert/is-descriptor.svg?style=flat&label=Travis)](https://travis-ci.org/jonschlinkert/is-descriptor)
+
+> Returns true if a value has the characteristics of a valid JavaScript descriptor. Works for data descriptors and accessor descriptors.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save is-descriptor
+```
+
+## Usage
+
+```js
+var isDescriptor = require('is-descriptor');
+
+isDescriptor({value: 'foo'})
+//=> true
+isDescriptor({get: function(){}, set: function(){}})
+//=> true
+isDescriptor({get: 'foo', set: function(){}})
+//=> false
+```
+
+You may also check for a descriptor by passing an object as the first argument and property name (`string`) as the second argument.
+
+```js
+var obj = {};
+obj.foo = 'abc';
+
+Object.defineProperty(obj, 'bar', {
+ value: 'xyz'
+});
+
+isDescriptor(obj, 'foo');
+//=> true
+isDescriptor(obj, 'bar');
+//=> true
+```
+
+## Examples
+
+### value type
+
+`false` when not an object
+
+```js
+isDescriptor('a');
+//=> false
+isDescriptor(null);
+//=> false
+isDescriptor([]);
+//=> false
+```
+
+### data descriptor
+
+`true` when the object has valid properties with valid values.
+
+```js
+isDescriptor({value: 'foo'});
+//=> true
+isDescriptor({value: noop});
+//=> true
+```
+
+`false` when the object has invalid properties
+
+```js
+isDescriptor({value: 'foo', bar: 'baz'});
+//=> false
+isDescriptor({value: 'foo', bar: 'baz'});
+//=> false
+isDescriptor({value: 'foo', get: noop});
+//=> false
+isDescriptor({get: noop, value: noop});
+//=> false
+```
+
+`false` when a value is not the correct type
+
+```js
+isDescriptor({value: 'foo', enumerable: 'foo'});
+//=> false
+isDescriptor({value: 'foo', configurable: 'foo'});
+//=> false
+isDescriptor({value: 'foo', writable: 'foo'});
+//=> false
+```
+
+### accessor descriptor
+
+`true` when the object has valid properties with valid values.
+
+```js
+isDescriptor({get: noop, set: noop});
+//=> true
+isDescriptor({get: noop});
+//=> true
+isDescriptor({set: noop});
+//=> true
+```
+
+`false` when the object has invalid properties
+
+```js
+isDescriptor({get: noop, set: noop, bar: 'baz'});
+//=> false
+isDescriptor({get: noop, writable: true});
+//=> false
+isDescriptor({get: noop, value: true});
+//=> false
+```
+
+`false` when an accessor is not a function
+
+```js
+isDescriptor({get: noop, set: 'baz'});
+//=> false
+isDescriptor({get: 'foo', set: noop});
+//=> false
+isDescriptor({get: 'foo', bar: 'baz'});
+//=> false
+isDescriptor({get: 'foo', set: 'baz'});
+//=> false
+```
+
+`false` when a value is not the correct type
+
+```js
+isDescriptor({get: noop, set: noop, enumerable: 'foo'});
+//=> false
+isDescriptor({set: noop, configurable: 'foo'});
+//=> false
+isDescriptor({get: noop, configurable: 'foo'});
+//=> false
+```
+
+## About
+
+### Related projects
+
+* [is-accessor-descriptor](https://www.npmjs.com/package/is-accessor-descriptor): Returns true if a value has the characteristics of a valid JavaScript accessor descriptor. | [homepage](https://github.com/jonschlinkert/is-accessor-descriptor "Returns true if a value has the characteristics of a valid JavaScript accessor descriptor.")
+* [is-data-descriptor](https://www.npmjs.com/package/is-data-descriptor): Returns true if a value has the characteristics of a valid JavaScript data descriptor. | [homepage](https://github.com/jonschlinkert/is-data-descriptor "Returns true if a value has the characteristics of a valid JavaScript data descriptor.")
+* [is-descriptor](https://www.npmjs.com/package/is-descriptor): Returns true if a value has the characteristics of a valid JavaScript descriptor. Works for… [more](https://github.com/jonschlinkert/is-descriptor) | [homepage](https://github.com/jonschlinkert/is-descriptor "Returns true if a value has the characteristics of a valid JavaScript descriptor. Works for data descriptors and accessor descriptors.")
+* [isobject](https://www.npmjs.com/package/isobject): Returns true if the value is an object and not an array or null. | [homepage](https://github.com/jonschlinkert/isobject "Returns true if the value is an object and not an array or null.")
+
+### Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+### Contributors
+
+| **Commits** | **Contributor** |
+| --- | --- |
+| 24 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 1 | [doowb](https://github.com/doowb) |
+| 1 | [wtgtybhertgeghgtwtg](https://github.com/wtgtybhertgeghgtwtg) |
+
+### Building docs
+
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
+
+To generate the readme, run the following command:
+
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
+
+### Running tests
+
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
+
+```sh
+$ npm install && npm test
+```
+
+### Author
+
+**Jon Schlinkert**
+
+* [github/jonschlinkert](https://github.com/jonschlinkert)
+* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
+
+### License
+
+Copyright © 2017, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on July 22, 2017._
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor/index.js b/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor/index.js
new file mode 100644
index 0000000..c9b91d7
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor/index.js
@@ -0,0 +1,22 @@
+/*!
+ * is-descriptor
+ *
+ * Copyright (c) 2015-2017, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+'use strict';
+
+var typeOf = require('kind-of');
+var isAccessor = require('is-accessor-descriptor');
+var isData = require('is-data-descriptor');
+
+module.exports = function isDescriptor(obj, key) {
+ if (typeOf(obj) !== 'object') {
+ return false;
+ }
+ if ('get' in obj) {
+ return isAccessor(obj, key);
+ }
+ return isData(obj, key);
+};
diff --git a/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor/package.json b/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor/package.json
new file mode 100644
index 0000000..19885a3
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/node_modules/is-descriptor/package.json
@@ -0,0 +1,114 @@
+{
+ "_from": "is-descriptor@^1.0.0",
+ "_id": "is-descriptor@1.0.2",
+ "_inBundle": false,
+ "_integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+ "_location": "/base/is-descriptor",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "is-descriptor@^1.0.0",
+ "name": "is-descriptor",
+ "escapedName": "is-descriptor",
+ "rawSpec": "^1.0.0",
+ "saveSpec": null,
+ "fetchSpec": "^1.0.0"
+ },
+ "_requiredBy": [
+ "/base/define-property"
+ ],
+ "_resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+ "_shasum": "3b159746a66604b04f8c81524ba365c5f14d86ec",
+ "_spec": "is-descriptor@^1.0.0",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/base/node_modules/define-property",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/is-descriptor/issues"
+ },
+ "bundleDependencies": false,
+ "contributors": [
+ {
+ "name": "Brian Woodward",
+ "url": "https://twitter.com/doowb"
+ },
+ {
+ "name": "Jon Schlinkert",
+ "url": "http://twitter.com/jonschlinkert"
+ },
+ {
+ "url": "https://github.com/wtgtybhertgeghgtwtg"
+ }
+ ],
+ "dependencies": {
+ "is-accessor-descriptor": "^1.0.0",
+ "is-data-descriptor": "^1.0.0",
+ "kind-of": "^6.0.2"
+ },
+ "deprecated": false,
+ "description": "Returns true if a value has the characteristics of a valid JavaScript descriptor. Works for data descriptors and accessor descriptors.",
+ "devDependencies": {
+ "gulp-format-md": "^1.0.0",
+ "mocha": "^3.5.3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/jonschlinkert/is-descriptor",
+ "keywords": [
+ "accessor",
+ "check",
+ "data",
+ "descriptor",
+ "get",
+ "getter",
+ "is",
+ "keys",
+ "object",
+ "properties",
+ "property",
+ "set",
+ "setter",
+ "type",
+ "valid",
+ "value"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "is-descriptor",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/is-descriptor.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "related": {
+ "list": [
+ "is-accessor-descriptor",
+ "is-data-descriptor",
+ "is-descriptor",
+ "isobject"
+ ]
+ },
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "lint": {
+ "reflinks": true
+ }
+ },
+ "version": "1.0.2"
+}
diff --git a/chatto/src/main/javascript/node_modules/base/package.json b/chatto/src/main/javascript/node_modules/base/package.json
new file mode 100644
index 0000000..866d474
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/base/package.json
@@ -0,0 +1,164 @@
+{
+ "_from": "base@^0.11.1",
+ "_id": "base@0.11.2",
+ "_inBundle": false,
+ "_integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==",
+ "_location": "/base",
+ "_phantomChildren": {
+ "kind-of": "6.0.2"
+ },
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "base@^0.11.1",
+ "name": "base",
+ "escapedName": "base",
+ "rawSpec": "^0.11.1",
+ "saveSpec": null,
+ "fetchSpec": "^0.11.1"
+ },
+ "_requiredBy": [
+ "/snapdragon"
+ ],
+ "_resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz",
+ "_shasum": "7bde5ced145b6d551a90db87f83c558b4eb48a8f",
+ "_spec": "base@^0.11.1",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/snapdragon",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/node-base/base/issues"
+ },
+ "bundleDependencies": false,
+ "contributors": [
+ {
+ "name": "Brian Woodward",
+ "url": "https://twitter.com/doowb"
+ },
+ {
+ "name": "John O'Donnell",
+ "url": "https://github.com/criticalmash"
+ },
+ {
+ "name": "Jon Schlinkert",
+ "url": "http://twitter.com/jonschlinkert"
+ },
+ {
+ "name": "tunnckoCore",
+ "url": "https://i.am.charlike.online"
+ },
+ {
+ "url": "https://github.com/wtgtybhertgeghgtwtg"
+ }
+ ],
+ "dependencies": {
+ "cache-base": "^1.0.1",
+ "class-utils": "^0.3.5",
+ "component-emitter": "^1.2.1",
+ "define-property": "^1.0.0",
+ "isobject": "^3.0.1",
+ "mixin-deep": "^1.2.0",
+ "pascalcase": "^0.1.1"
+ },
+ "deprecated": false,
+ "description": "base is the foundation for creating modular, unit testable and highly pluggable node.js applications, starting with a handful of common methods, like `set`, `get`, `del` and `use`.",
+ "devDependencies": {
+ "gulp": "^3.9.1",
+ "gulp-eslint": "^4.0.0",
+ "gulp-format-md": "^1.0.0",
+ "gulp-istanbul": "^1.1.2",
+ "gulp-mocha": "^3.0.1",
+ "helper-coverage": "^0.1.3",
+ "mocha": "^3.5.0",
+ "should": "^13.0.1",
+ "through2": "^2.0.3",
+ "verb-generate-readme": "^0.6.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/node-base/base",
+ "keywords": [
+ "base",
+ "boilerplate",
+ "cache",
+ "del",
+ "get",
+ "inherit",
+ "methods",
+ "set",
+ "starter",
+ "unset",
+ "visit"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "maintainers": [
+ {
+ "name": "Brian Woodward",
+ "url": "https://github.com/doowb"
+ },
+ {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ }
+ ],
+ "name": "base",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/node-base/base.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "verb": {
+ "run": true,
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "helpers": [
+ "helper-coverage"
+ ],
+ "related": {
+ "description": "There are a number of different plugins available for extending base. Let us know if you create your own!",
+ "hightlight": "generate",
+ "list": [
+ "base-cwd",
+ "base-data",
+ "base-fs",
+ "base-generators",
+ "base-option",
+ "base-pipeline",
+ "base-pkg",
+ "base-plugins",
+ "base-questions",
+ "base-store",
+ "base-task"
+ ]
+ },
+ "reflinks": [
+ "assemble",
+ "boilerplate",
+ "cache-base",
+ "class-utils",
+ "generate",
+ "scaffold",
+ "static-extend",
+ "verb"
+ ],
+ "lint": {
+ "reflinks": true
+ }
+ },
+ "version": "0.11.2"
+}
diff --git a/chatto/src/main/javascript/node_modules/braces/LICENSE b/chatto/src/main/javascript/node_modules/braces/LICENSE
new file mode 100644
index 0000000..d32ab44
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/braces/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2018, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chatto/src/main/javascript/node_modules/braces/README.md b/chatto/src/main/javascript/node_modules/braces/README.md
new file mode 100644
index 0000000..f909bfb
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/braces/README.md
@@ -0,0 +1,640 @@
+# braces [![NPM version](https://img.shields.io/npm/v/braces.svg?style=flat)](https://www.npmjs.com/package/braces) [![NPM monthly downloads](https://img.shields.io/npm/dm/braces.svg?style=flat)](https://npmjs.org/package/braces) [![NPM total downloads](https://img.shields.io/npm/dt/braces.svg?style=flat)](https://npmjs.org/package/braces) [![Linux Build Status](https://img.shields.io/travis/micromatch/braces.svg?style=flat&label=Travis)](https://travis-ci.org/micromatch/braces) [![Windows Build Status](https://img.shields.io/appveyor/ci/micromatch/braces.svg?style=flat&label=AppVeyor)](https://ci.appveyor.com/project/micromatch/braces)
+
+> Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.
+
+Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/):
+
+```sh
+$ npm install --save braces
+```
+
+## Why use braces?
+
+Brace patterns are great for matching ranges. Users (and implementors) shouldn't have to think about whether or not they will break their application (or yours) from accidentally defining an aggressive brace pattern. _Braces is the only library that offers a [solution to this problem](#performance)_.
+
+* **Safe(r)**: Braces isn't vulnerable to DoS attacks like [brace-expansion](https://github.com/juliangruber/brace-expansion), [minimatch](https://github.com/isaacs/minimatch) and [multimatch](https://github.com/sindresorhus/multimatch) (a different bug than the [other regex DoS bug](https://medium.com/node-security/minimatch-redos-vulnerability-590da24e6d3c#.jew0b6mpc)).
+* **Accurate**: complete support for the [Bash 4.3 Brace Expansion](www.gnu.org/software/bash/) specification (passes all of the Bash braces tests)
+* **[fast and performant](#benchmarks)**: Starts fast, runs fast and [scales well](#performance) as patterns increase in complexity.
+* **Organized code base**: with parser and compiler that are eas(y|ier) to maintain and update when edge cases crop up.
+* **Well-tested**: thousands of test assertions. Passes 100% of the [minimatch](https://github.com/isaacs/minimatch) and [brace-expansion](https://github.com/juliangruber/brace-expansion) unit tests as well (as of the writing of this).
+
+## Usage
+
+The main export is a function that takes one or more brace `patterns` and `options`.
+
+```js
+var braces = require('braces');
+braces(pattern[, options]);
+```
+
+By default, braces returns an optimized regex-source string. To get an array of brace patterns, use `brace.expand()`.
+
+The following section explains the difference in more detail. _(If you're curious about "why" braces does this by default, see [brace matching pitfalls](#brace-matching-pitfalls)_.
+
+### Optimized vs. expanded braces
+
+**Optimized**
+
+By default, patterns are optimized for regex and matching:
+
+```js
+console.log(braces('a/{x,y,z}/b'));
+//=> ['a/(x|y|z)/b']
+```
+
+**Expanded**
+
+To expand patterns the same way as Bash or [minimatch](https://github.com/isaacs/minimatch), use the [.expand](#expand) method:
+
+```js
+console.log(braces.expand('a/{x,y,z}/b'));
+//=> ['a/x/b', 'a/y/b', 'a/z/b']
+```
+
+Or use [options.expand](#optionsexpand):
+
+```js
+console.log(braces('a/{x,y,z}/b', {expand: true}));
+//=> ['a/x/b', 'a/y/b', 'a/z/b']
+```
+
+## Features
+
+* [lists](#lists): Supports "lists": `a/{b,c}/d` => `['a/b/d', 'a/c/d']`
+* [sequences](#sequences): Supports alphabetical or numerical "sequences" (ranges): `{1..3}` => `['1', '2', '3']`
+* [steps](#steps): Supports "steps" or increments: `{2..10..2}` => `['2', '4', '6', '8', '10']`
+* [escaping](#escaping)
+* [options](#options)
+
+### Lists
+
+Uses [fill-range](https://github.com/jonschlinkert/fill-range) for expanding alphabetical or numeric lists:
+
+```js
+console.log(braces('a/{foo,bar,baz}/*.js'));
+//=> ['a/(foo|bar|baz)/*.js']
+
+console.log(braces.expand('a/{foo,bar,baz}/*.js'));
+//=> ['a/foo/*.js', 'a/bar/*.js', 'a/baz/*.js']
+```
+
+### Sequences
+
+Uses [fill-range](https://github.com/jonschlinkert/fill-range) for expanding alphabetical or numeric ranges (bash "sequences"):
+
+```js
+console.log(braces.expand('{1..3}')); // ['1', '2', '3']
+console.log(braces.expand('a{01..03}b')); // ['a01b', 'a02b', 'a03b']
+console.log(braces.expand('a{1..3}b')); // ['a1b', 'a2b', 'a3b']
+console.log(braces.expand('{a..c}')); // ['a', 'b', 'c']
+console.log(braces.expand('foo/{a..c}')); // ['foo/a', 'foo/b', 'foo/c']
+
+// supports padded ranges
+console.log(braces('a{01..03}b')); //=> [ 'a(0[1-3])b' ]
+console.log(braces('a{001..300}b')); //=> [ 'a(0{2}[1-9]|0[1-9][0-9]|[12][0-9]{2}|300)b' ]
+```
+
+### Steps
+
+Steps, or increments, may be used with ranges:
+
+```js
+console.log(braces.expand('{2..10..2}'));
+//=> ['2', '4', '6', '8', '10']
+
+console.log(braces('{2..10..2}'));
+//=> ['(2|4|6|8|10)']
+```
+
+When the [.optimize](#optimize) method is used, or [options.optimize](#optionsoptimize) is set to true, sequences are passed to [to-regex-range](https://github.com/jonschlinkert/to-regex-range) for expansion.
+
+### Nesting
+
+Brace patterns may be nested. The results of each expanded string are not sorted, and left to right order is preserved.
+
+**"Expanded" braces**
+
+```js
+console.log(braces.expand('a{b,c,/{x,y}}/e'));
+//=> ['ab/e', 'ac/e', 'a/x/e', 'a/y/e']
+
+console.log(braces.expand('a/{x,{1..5},y}/c'));
+//=> ['a/x/c', 'a/1/c', 'a/2/c', 'a/3/c', 'a/4/c', 'a/5/c', 'a/y/c']
+```
+
+**"Optimized" braces**
+
+```js
+console.log(braces('a{b,c,/{x,y}}/e'));
+//=> ['a(b|c|/(x|y))/e']
+
+console.log(braces('a/{x,{1..5},y}/c'));
+//=> ['a/(x|([1-5])|y)/c']
+```
+
+### Escaping
+
+**Escaping braces**
+
+A brace pattern will not be expanded or evaluted if _either the opening or closing brace is escaped_:
+
+```js
+console.log(braces.expand('a\\{d,c,b}e'));
+//=> ['a{d,c,b}e']
+
+console.log(braces.expand('a{d,c,b\\}e'));
+//=> ['a{d,c,b}e']
+```
+
+**Escaping commas**
+
+Commas inside braces may also be escaped:
+
+```js
+console.log(braces.expand('a{b\\,c}d'));
+//=> ['a{b,c}d']
+
+console.log(braces.expand('a{d\\,c,b}e'));
+//=> ['ad,ce', 'abe']
+```
+
+**Single items**
+
+Following bash conventions, a brace pattern is also not expanded when it contains a single character:
+
+```js
+console.log(braces.expand('a{b}c'));
+//=> ['a{b}c']
+```
+
+## Options
+
+### options.maxLength
+
+**Type**: `Number`
+
+**Default**: `65,536`
+
+**Description**: Limit the length of the input string. Useful when the input string is generated or your application allows users to pass a string, et cetera.
+
+```js
+console.log(braces('a/{b,c}/d', { maxLength: 3 })); //=> throws an error
+```
+
+### options.expand
+
+**Type**: `Boolean`
+
+**Default**: `undefined`
+
+**Description**: Generate an "expanded" brace pattern (this option is unncessary with the `.expand` method, which does the same thing).
+
+```js
+console.log(braces('a/{b,c}/d', {expand: true}));
+//=> [ 'a/b/d', 'a/c/d' ]
+```
+
+### options.optimize
+
+**Type**: `Boolean`
+
+**Default**: `true`
+
+**Description**: Enabled by default.
+
+```js
+console.log(braces('a/{b,c}/d'));
+//=> [ 'a/(b|c)/d' ]
+```
+
+### options.nodupes
+
+**Type**: `Boolean`
+
+**Default**: `true`
+
+**Description**: Duplicates are removed by default. To keep duplicates, pass `{nodupes: false}` on the options
+
+### options.rangeLimit
+
+**Type**: `Number`
+
+**Default**: `250`
+
+**Description**: When `braces.expand()` is used, or `options.expand` is true, brace patterns will automatically be [optimized](#optionsoptimize) when the difference between the range minimum and range maximum exceeds the `rangeLimit`. This is to prevent huge ranges from freezing your application.
+
+You can set this to any number, or change `options.rangeLimit` to `Inifinity` to disable this altogether.
+
+**Examples**
+
+```js
+// pattern exceeds the "rangeLimit", so it's optimized automatically
+console.log(braces.expand('{1..1000}'));
+//=> ['([1-9]|[1-9][0-9]{1,2}|1000)']
+
+// pattern does not exceed "rangeLimit", so it's NOT optimized
+console.log(braces.expand('{1..100}'));
+//=> ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14', '15', '16', '17', '18', '19', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '90', '91', '92', '93', '94', '95', '96', '97', '98', '99', '100']
+```
+
+### options.transform
+
+**Type**: `Function`
+
+**Default**: `undefined`
+
+**Description**: Customize range expansion.
+
+```js
+var range = braces.expand('x{a..e}y', {
+ transform: function(str) {
+ return 'foo' + str;
+ }
+});
+
+console.log(range);
+//=> [ 'xfooay', 'xfooby', 'xfoocy', 'xfoody', 'xfooey' ]
+```
+
+### options.quantifiers
+
+**Type**: `Boolean`
+
+**Default**: `undefined`
+
+**Description**: In regular expressions, quanitifiers can be used to specify how many times a token can be repeated. For example, `a{1,3}` will match the letter `a` one to three times.
+
+Unfortunately, regex quantifiers happen to share the same syntax as [Bash lists](#lists)
+
+The `quantifiers` option tells braces to detect when [regex quantifiers](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#quantifiers) are defined in the given pattern, and not to try to expand them as lists.
+
+**Examples**
+
+```js
+var braces = require('braces');
+console.log(braces('a/b{1,3}/{x,y,z}'));
+//=> [ 'a/b(1|3)/(x|y|z)' ]
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true}));
+//=> [ 'a/b{1,3}/(x|y|z)' ]
+console.log(braces('a/b{1,3}/{x,y,z}', {quantifiers: true, expand: true}));
+//=> [ 'a/b{1,3}/x', 'a/b{1,3}/y', 'a/b{1,3}/z' ]
+```
+
+### options.unescape
+
+**Type**: `Boolean`
+
+**Default**: `undefined`
+
+**Description**: Strip backslashes that were used for escaping from the result.
+
+## What is "brace expansion"?
+
+Brace expansion is a type of parameter expansion that was made popular by unix shells for generating lists of strings, as well as regex-like matching when used alongside wildcards (globs).
+
+In addition to "expansion", braces are also used for matching. In other words:
+
+* [brace expansion](#brace-expansion) is for generating new lists
+* [brace matching](#brace-matching) is for filtering existing lists
+
+
+More about brace expansion (click to expand)
+
+There are two main types of brace expansion:
+
+1. **lists**: which are defined using comma-separated values inside curly braces: `{a,b,c}`
+2. **sequences**: which are defined using a starting value and an ending value, separated by two dots: `a{1..3}b`. Optionally, a third argument may be passed to define a "step" or increment to use: `a{1..100..10}b`. These are also sometimes referred to as "ranges".
+
+Here are some example brace patterns to illustrate how they work:
+
+**Sets**
+
+```
+{a,b,c} => a b c
+{a,b,c}{1,2} => a1 a2 b1 b2 c1 c2
+```
+
+**Sequences**
+
+```
+{1..9} => 1 2 3 4 5 6 7 8 9
+{4..-4} => 4 3 2 1 0 -1 -2 -3 -4
+{1..20..3} => 1 4 7 10 13 16 19
+{a..j} => a b c d e f g h i j
+{j..a} => j i h g f e d c b a
+{a..z..3} => a d g j m p s v y
+```
+
+**Combination**
+
+Sets and sequences can be mixed together or used along with any other strings.
+
+```
+{a,b,c}{1..3} => a1 a2 a3 b1 b2 b3 c1 c2 c3
+foo/{a,b,c}/bar => foo/a/bar foo/b/bar foo/c/bar
+```
+
+The fact that braces can be "expanded" from relatively simple patterns makes them ideal for quickly generating test fixtures, file paths, and similar use cases.
+
+## Brace matching
+
+In addition to _expansion_, brace patterns are also useful for performing regular-expression-like matching.
+
+For example, the pattern `foo/{1..3}/bar` would match any of following strings:
+
+```
+foo/1/bar
+foo/2/bar
+foo/3/bar
+```
+
+But not:
+
+```
+baz/1/qux
+baz/2/qux
+baz/3/qux
+```
+
+Braces can also be combined with [glob patterns](https://github.com/jonschlinkert/micromatch) to perform more advanced wildcard matching. For example, the pattern `*/{1..3}/*` would match any of following strings:
+
+```
+foo/1/bar
+foo/2/bar
+foo/3/bar
+baz/1/qux
+baz/2/qux
+baz/3/qux
+```
+
+## Brace matching pitfalls
+
+Although brace patterns offer a user-friendly way of matching ranges or sets of strings, there are also some major disadvantages and potential risks you should be aware of.
+
+### tldr
+
+**"brace bombs"**
+
+* brace expansion can eat up a huge amount of processing resources
+* as brace patterns increase _linearly in size_, the system resources required to expand the pattern increase exponentially
+* users can accidentally (or intentially) exhaust your system's resources resulting in the equivalent of a DoS attack (bonus: no programming knowledge is required!)
+
+For a more detailed explanation with examples, see the [geometric complexity](#geometric-complexity) section.
+
+### The solution
+
+Jump to the [performance section](#performance) to see how Braces solves this problem in comparison to other libraries.
+
+### Geometric complexity
+
+At minimum, brace patterns with sets limited to two elements have quadradic or `O(n^2)` complexity. But the complexity of the algorithm increases exponentially as the number of sets, _and elements per set_, increases, which is `O(n^c)`.
+
+For example, the following sets demonstrate quadratic (`O(n^2)`) complexity:
+
+```
+{1,2}{3,4} => (2X2) => 13 14 23 24
+{1,2}{3,4}{5,6} => (2X2X2) => 135 136 145 146 235 236 245 246
+```
+
+But add an element to a set, and we get a n-fold Cartesian product with `O(n^c)` complexity:
+
+```
+{1,2,3}{4,5,6}{7,8,9} => (3X3X3) => 147 148 149 157 158 159 167 168 169 247 248
+ 249 257 258 259 267 268 269 347 348 349 357
+ 358 359 367 368 369
+```
+
+Now, imagine how this complexity grows given that each element is a n-tuple:
+
+```
+{1..100}{1..100} => (100X100) => 10,000 elements (38.4 kB)
+{1..100}{1..100}{1..100} => (100X100X100) => 1,000,000 elements (5.76 MB)
+```
+
+Although these examples are clearly contrived, they demonstrate how brace patterns can quickly grow out of control.
+
+**More information**
+
+Interested in learning more about brace expansion?
+
+* [linuxjournal/bash-brace-expansion](http://www.linuxjournal.com/content/bash-brace-expansion)
+* [rosettacode/Brace_expansion](https://rosettacode.org/wiki/Brace_expansion)
+* [cartesian product](https://en.wikipedia.org/wiki/Cartesian_product)
+
+
+
+## Performance
+
+Braces is not only screaming fast, it's also more accurate the other brace expansion libraries.
+
+### Better algorithms
+
+Fortunately there is a solution to the ["brace bomb" problem](#brace-matching-pitfalls): _don't expand brace patterns into an array when they're used for matching_.
+
+Instead, convert the pattern into an optimized regular expression. This is easier said than done, and braces is the only library that does this currently.
+
+**The proof is in the numbers**
+
+Minimatch gets exponentially slower as patterns increase in complexity, braces does not. The following results were generated using `braces()` and `minimatch.braceExpand()`, respectively.
+
+| **Pattern** | **braces** | **[minimatch](https://github.com/isaacs/minimatch)** |
+| --- | --- | --- |
+| `{1..9007199254740991}` | `298 B` (5ms 459μs) | N/A (freezes) |
+| `{1..1000000000000000}` | `41 B` (1ms 15μs) | N/A (freezes) |
+| `{1..100000000000000}` | `40 B` (890μs) | N/A (freezes) |
+| `{1..10000000000000}` | `39 B` (2ms 49μs) | N/A (freezes) |
+| `{1..1000000000000}` | `38 B` (608μs) | N/A (freezes) |
+| `{1..100000000000}` | `37 B` (397μs) | N/A (freezes) |
+| `{1..10000000000}` | `35 B` (983μs) | N/A (freezes) |
+| `{1..1000000000}` | `34 B` (798μs) | N/A (freezes) |
+| `{1..100000000}` | `33 B` (733μs) | N/A (freezes) |
+| `{1..10000000}` | `32 B` (5ms 632μs) | `78.89 MB` (16s 388ms 569μs) |
+| `{1..1000000}` | `31 B` (1ms 381μs) | `6.89 MB` (1s 496ms 887μs) |
+| `{1..100000}` | `30 B` (950μs) | `588.89 kB` (146ms 921μs) |
+| `{1..10000}` | `29 B` (1ms 114μs) | `48.89 kB` (14ms 187μs) |
+| `{1..1000}` | `28 B` (760μs) | `3.89 kB` (1ms 453μs) |
+| `{1..100}` | `22 B` (345μs) | `291 B` (196μs) |
+| `{1..10}` | `10 B` (533μs) | `20 B` (37μs) |
+| `{1..3}` | `7 B` (190μs) | `5 B` (27μs) |
+
+### Faster algorithms
+
+When you need expansion, braces is still much faster.
+
+_(the following results were generated using `braces.expand()` and `minimatch.braceExpand()`, respectively)_
+
+| **Pattern** | **braces** | **[minimatch](https://github.com/isaacs/minimatch)** |
+| --- | --- | --- |
+| `{1..10000000}` | `78.89 MB` (2s 698ms 642μs) | `78.89 MB` (18s 601ms 974μs) |
+| `{1..1000000}` | `6.89 MB` (458ms 576μs) | `6.89 MB` (1s 491ms 621μs) |
+| `{1..100000}` | `588.89 kB` (20ms 728μs) | `588.89 kB` (156ms 919μs) |
+| `{1..10000}` | `48.89 kB` (2ms 202μs) | `48.89 kB` (13ms 641μs) |
+| `{1..1000}` | `3.89 kB` (1ms 796μs) | `3.89 kB` (1ms 958μs) |
+| `{1..100}` | `291 B` (424μs) | `291 B` (211μs) |
+| `{1..10}` | `20 B` (487μs) | `20 B` (72μs) |
+| `{1..3}` | `5 B` (166μs) | `5 B` (27μs) |
+
+If you'd like to run these comparisons yourself, see [test/support/generate.js](test/support/generate.js).
+
+## Benchmarks
+
+### Running benchmarks
+
+Install dev dependencies:
+
+```bash
+npm i -d && npm benchmark
+```
+
+### Latest results
+
+```bash
+Benchmarking: (8 of 8)
+ · combination-nested
+ · combination
+ · escaped
+ · list-basic
+ · list-multiple
+ · no-braces
+ · sequence-basic
+ · sequence-multiple
+
+# benchmark/fixtures/combination-nested.js (52 bytes)
+ brace-expansion x 4,756 ops/sec ±1.09% (86 runs sampled)
+ braces x 11,202,303 ops/sec ±1.06% (88 runs sampled)
+ minimatch x 4,816 ops/sec ±0.99% (87 runs sampled)
+
+ fastest is braces
+
+# benchmark/fixtures/combination.js (51 bytes)
+ brace-expansion x 625 ops/sec ±0.87% (87 runs sampled)
+ braces x 11,031,884 ops/sec ±0.72% (90 runs sampled)
+ minimatch x 637 ops/sec ±0.84% (88 runs sampled)
+
+ fastest is braces
+
+# benchmark/fixtures/escaped.js (44 bytes)
+ brace-expansion x 163,325 ops/sec ±1.05% (87 runs sampled)
+ braces x 10,655,071 ops/sec ±1.22% (88 runs sampled)
+ minimatch x 147,495 ops/sec ±0.96% (88 runs sampled)
+
+ fastest is braces
+
+# benchmark/fixtures/list-basic.js (40 bytes)
+ brace-expansion x 99,726 ops/sec ±1.07% (83 runs sampled)
+ braces x 10,596,584 ops/sec ±0.98% (88 runs sampled)
+ minimatch x 100,069 ops/sec ±1.17% (86 runs sampled)
+
+ fastest is braces
+
+# benchmark/fixtures/list-multiple.js (52 bytes)
+ brace-expansion x 34,348 ops/sec ±1.08% (88 runs sampled)
+ braces x 9,264,131 ops/sec ±1.12% (88 runs sampled)
+ minimatch x 34,893 ops/sec ±0.87% (87 runs sampled)
+
+ fastest is braces
+
+# benchmark/fixtures/no-braces.js (48 bytes)
+ brace-expansion x 275,368 ops/sec ±1.18% (89 runs sampled)
+ braces x 9,134,677 ops/sec ±0.95% (88 runs sampled)
+ minimatch x 3,755,954 ops/sec ±1.13% (89 runs sampled)
+
+ fastest is braces
+
+# benchmark/fixtures/sequence-basic.js (41 bytes)
+ brace-expansion x 5,492 ops/sec ±1.35% (87 runs sampled)
+ braces x 8,485,034 ops/sec ±1.28% (89 runs sampled)
+ minimatch x 5,341 ops/sec ±1.17% (87 runs sampled)
+
+ fastest is braces
+
+# benchmark/fixtures/sequence-multiple.js (51 bytes)
+ brace-expansion x 116 ops/sec ±0.77% (77 runs sampled)
+ braces x 9,445,118 ops/sec ±1.32% (84 runs sampled)
+ minimatch x 109 ops/sec ±1.16% (76 runs sampled)
+
+ fastest is braces
+```
+
+## About
+
+
+Contributing
+
+Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
+
+
+
+
+Running Tests
+
+Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
+
+```sh
+$ npm install && npm test
+```
+
+
+
+
+Building docs
+
+_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
+
+To generate the readme, run the following command:
+
+```sh
+$ npm install -g verbose/verb#dev verb-generate-readme && verb
+```
+
+
+
+### Related projects
+
+You might also be interested in these projects:
+
+* [expand-brackets](https://www.npmjs.com/package/expand-brackets): Expand POSIX bracket expressions (character classes) in glob patterns. | [homepage](https://github.com/jonschlinkert/expand-brackets "Expand POSIX bracket expressions (character classes) in glob patterns.")
+* [extglob](https://www.npmjs.com/package/extglob): Extended glob support for JavaScript. Adds (almost) the expressive power of regular expressions to glob… [more](https://github.com/micromatch/extglob) | [homepage](https://github.com/micromatch/extglob "Extended glob support for JavaScript. Adds (almost) the expressive power of regular expressions to glob patterns.")
+* [fill-range](https://www.npmjs.com/package/fill-range): Fill in a range of numbers or letters, optionally passing an increment or `step` to… [more](https://github.com/jonschlinkert/fill-range) | [homepage](https://github.com/jonschlinkert/fill-range "Fill in a range of numbers or letters, optionally passing an increment or `step` to use, or create a regex-compatible range with `options.toRegex`")
+* [micromatch](https://www.npmjs.com/package/micromatch): Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch. | [homepage](https://github.com/micromatch/micromatch "Glob matching for javascript/node.js. A drop-in replacement and faster alternative to minimatch and multimatch.")
+* [nanomatch](https://www.npmjs.com/package/nanomatch): Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash… [more](https://github.com/micromatch/nanomatch) | [homepage](https://github.com/micromatch/nanomatch "Fast, minimal glob matcher for node.js. Similar to micromatch, minimatch and multimatch, but complete Bash 4.3 wildcard support only (no support for exglobs, posix brackets or braces)")
+
+### Contributors
+
+| **Commits** | **Contributor** |
+| --- | --- |
+| 188 | [jonschlinkert](https://github.com/jonschlinkert) |
+| 4 | [doowb](https://github.com/doowb) |
+| 1 | [es128](https://github.com/es128) |
+| 1 | [eush77](https://github.com/eush77) |
+| 1 | [hemanth](https://github.com/hemanth) |
+
+### Author
+
+**Jon Schlinkert**
+
+* [linkedin/in/jonschlinkert](https://linkedin.com/in/jonschlinkert)
+* [github/jonschlinkert](https://github.com/jonschlinkert)
+* [twitter/jonschlinkert](https://twitter.com/jonschlinkert)
+
+### License
+
+Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert).
+Released under the [MIT License](LICENSE).
+
+***
+
+_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on February 17, 2018._
+
+
+
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/braces/index.js b/chatto/src/main/javascript/node_modules/braces/index.js
new file mode 100644
index 0000000..048e1c2
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/braces/index.js
@@ -0,0 +1,318 @@
+'use strict';
+
+/**
+ * Module dependencies
+ */
+
+var toRegex = require('to-regex');
+var unique = require('array-unique');
+var extend = require('extend-shallow');
+
+/**
+ * Local dependencies
+ */
+
+var compilers = require('./lib/compilers');
+var parsers = require('./lib/parsers');
+var Braces = require('./lib/braces');
+var utils = require('./lib/utils');
+var MAX_LENGTH = 1024 * 64;
+var cache = {};
+
+/**
+ * Convert the given `braces` pattern into a regex-compatible string. By default, only one string is generated for every input string. Set `options.expand` to true to return an array of patterns (similar to Bash or minimatch. Before using `options.expand`, it's recommended that you read the [performance notes](#performance)).
+ *
+ * ```js
+ * var braces = require('braces');
+ * console.log(braces('{a,b,c}'));
+ * //=> ['(a|b|c)']
+ *
+ * console.log(braces('{a,b,c}', {expand: true}));
+ * //=> ['a', 'b', 'c']
+ * ```
+ * @param {String} `str`
+ * @param {Object} `options`
+ * @return {String}
+ * @api public
+ */
+
+function braces(pattern, options) {
+ var key = utils.createKey(String(pattern), options);
+ var arr = [];
+
+ var disabled = options && options.cache === false;
+ if (!disabled && cache.hasOwnProperty(key)) {
+ return cache[key];
+ }
+
+ if (Array.isArray(pattern)) {
+ for (var i = 0; i < pattern.length; i++) {
+ arr.push.apply(arr, braces.create(pattern[i], options));
+ }
+ } else {
+ arr = braces.create(pattern, options);
+ }
+
+ if (options && options.nodupes === true) {
+ arr = unique(arr);
+ }
+
+ if (!disabled) {
+ cache[key] = arr;
+ }
+ return arr;
+}
+
+/**
+ * Expands a brace pattern into an array. This method is called by the main [braces](#braces) function when `options.expand` is true. Before using this method it's recommended that you read the [performance notes](#performance)) and advantages of using [.optimize](#optimize) instead.
+ *
+ * ```js
+ * var braces = require('braces');
+ * console.log(braces.expand('a/{b,c}/d'));
+ * //=> ['a/b/d', 'a/c/d'];
+ * ```
+ * @param {String} `pattern` Brace pattern
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.expand = function(pattern, options) {
+ return braces.create(pattern, extend({}, options, {expand: true}));
+};
+
+/**
+ * Expands a brace pattern into a regex-compatible, optimized string. This method is called by the main [braces](#braces) function by default.
+ *
+ * ```js
+ * var braces = require('braces');
+ * console.log(braces.expand('a/{b,c}/d'));
+ * //=> ['a/(b|c)/d']
+ * ```
+ * @param {String} `pattern` Brace pattern
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.optimize = function(pattern, options) {
+ return braces.create(pattern, options);
+};
+
+/**
+ * Processes a brace pattern and returns either an expanded array (if `options.expand` is true), a highly optimized regex-compatible string. This method is called by the main [braces](#braces) function.
+ *
+ * ```js
+ * var braces = require('braces');
+ * console.log(braces.create('user-{200..300}/project-{a,b,c}-{1..10}'))
+ * //=> 'user-(20[0-9]|2[1-9][0-9]|300)/project-(a|b|c)-([1-9]|10)'
+ * ```
+ * @param {String} `pattern` Brace pattern
+ * @param {Object} `options`
+ * @return {Array} Returns an array of expanded values.
+ * @api public
+ */
+
+braces.create = function(pattern, options) {
+ if (typeof pattern !== 'string') {
+ throw new TypeError('expected a string');
+ }
+
+ var maxLength = (options && options.maxLength) || MAX_LENGTH;
+ if (pattern.length >= maxLength) {
+ throw new Error('expected pattern to be less than ' + maxLength + ' characters');
+ }
+
+ function create() {
+ if (pattern === '' || pattern.length < 3) {
+ return [pattern];
+ }
+
+ if (utils.isEmptySets(pattern)) {
+ return [];
+ }
+
+ if (utils.isQuotedString(pattern)) {
+ return [pattern.slice(1, -1)];
+ }
+
+ var proto = new Braces(options);
+ var result = !options || options.expand !== true
+ ? proto.optimize(pattern, options)
+ : proto.expand(pattern, options);
+
+ // get the generated pattern(s)
+ var arr = result.output;
+
+ // filter out empty strings if specified
+ if (options && options.noempty === true) {
+ arr = arr.filter(Boolean);
+ }
+
+ // filter out duplicates if specified
+ if (options && options.nodupes === true) {
+ arr = unique(arr);
+ }
+
+ Object.defineProperty(arr, 'result', {
+ enumerable: false,
+ value: result
+ });
+
+ return arr;
+ }
+
+ return memoize('create', pattern, options, create);
+};
+
+/**
+ * Create a regular expression from the given string `pattern`.
+ *
+ * ```js
+ * var braces = require('braces');
+ *
+ * console.log(braces.makeRe('id-{200..300}'));
+ * //=> /^(?:id-(20[0-9]|2[1-9][0-9]|300))$/
+ * ```
+ * @param {String} `pattern` The pattern to convert to regex.
+ * @param {Object} `options`
+ * @return {RegExp}
+ * @api public
+ */
+
+braces.makeRe = function(pattern, options) {
+ if (typeof pattern !== 'string') {
+ throw new TypeError('expected a string');
+ }
+
+ var maxLength = (options && options.maxLength) || MAX_LENGTH;
+ if (pattern.length >= maxLength) {
+ throw new Error('expected pattern to be less than ' + maxLength + ' characters');
+ }
+
+ function makeRe() {
+ var arr = braces(pattern, options);
+ var opts = extend({strictErrors: false}, options);
+ return toRegex(arr, opts);
+ }
+
+ return memoize('makeRe', pattern, options, makeRe);
+};
+
+/**
+ * Parse the given `str` with the given `options`.
+ *
+ * ```js
+ * var braces = require('braces');
+ * var ast = braces.parse('a/{b,c}/d');
+ * console.log(ast);
+ * // { type: 'root',
+ * // errors: [],
+ * // input: 'a/{b,c}/d',
+ * // nodes:
+ * // [ { type: 'bos', val: '' },
+ * // { type: 'text', val: 'a/' },
+ * // { type: 'brace',
+ * // nodes:
+ * // [ { type: 'brace.open', val: '{' },
+ * // { type: 'text', val: 'b,c' },
+ * // { type: 'brace.close', val: '}' } ] },
+ * // { type: 'text', val: '/d' },
+ * // { type: 'eos', val: '' } ] }
+ * ```
+ * @param {String} `pattern` Brace pattern to parse
+ * @param {Object} `options`
+ * @return {Object} Returns an AST
+ * @api public
+ */
+
+braces.parse = function(pattern, options) {
+ var proto = new Braces(options);
+ return proto.parse(pattern, options);
+};
+
+/**
+ * Compile the given `ast` or string with the given `options`.
+ *
+ * ```js
+ * var braces = require('braces');
+ * var ast = braces.parse('a/{b,c}/d');
+ * console.log(braces.compile(ast));
+ * // { options: { source: 'string' },
+ * // state: {},
+ * // compilers:
+ * // { eos: [Function],
+ * // noop: [Function],
+ * // bos: [Function],
+ * // brace: [Function],
+ * // 'brace.open': [Function],
+ * // text: [Function],
+ * // 'brace.close': [Function] },
+ * // output: [ 'a/(b|c)/d' ],
+ * // ast:
+ * // { ... },
+ * // parsingErrors: [] }
+ * ```
+ * @param {Object|String} `ast` AST from [.parse](#parse). If a string is passed it will be parsed first.
+ * @param {Object} `options`
+ * @return {Object} Returns an object that has an `output` property with the compiled string.
+ * @api public
+ */
+
+braces.compile = function(ast, options) {
+ var proto = new Braces(options);
+ return proto.compile(ast, options);
+};
+
+/**
+ * Clear the regex cache.
+ *
+ * ```js
+ * braces.clearCache();
+ * ```
+ * @api public
+ */
+
+braces.clearCache = function() {
+ cache = braces.cache = {};
+};
+
+/**
+ * Memoize a generated regex or function. A unique key is generated
+ * from the method name, pattern, and user-defined options. Set
+ * options.memoize to false to disable.
+ */
+
+function memoize(type, pattern, options, fn) {
+ var key = utils.createKey(type + ':' + pattern, options);
+ var disabled = options && options.cache === false;
+ if (disabled) {
+ braces.clearCache();
+ return fn(pattern, options);
+ }
+
+ if (cache.hasOwnProperty(key)) {
+ return cache[key];
+ }
+
+ var res = fn(pattern, options);
+ cache[key] = res;
+ return res;
+}
+
+/**
+ * Expose `Braces` constructor and methods
+ * @type {Function}
+ */
+
+braces.Braces = Braces;
+braces.compilers = compilers;
+braces.parsers = parsers;
+braces.cache = cache;
+
+/**
+ * Expose `braces`
+ * @type {Function}
+ */
+
+module.exports = braces;
diff --git a/chatto/src/main/javascript/node_modules/braces/lib/braces.js b/chatto/src/main/javascript/node_modules/braces/lib/braces.js
new file mode 100644
index 0000000..baf6bf1
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/braces/lib/braces.js
@@ -0,0 +1,104 @@
+'use strict';
+
+var extend = require('extend-shallow');
+var Snapdragon = require('snapdragon');
+var compilers = require('./compilers');
+var parsers = require('./parsers');
+var utils = require('./utils');
+
+/**
+ * Customize Snapdragon parser and renderer
+ */
+
+function Braces(options) {
+ this.options = extend({}, options);
+}
+
+/**
+ * Initialize braces
+ */
+
+Braces.prototype.init = function(options) {
+ if (this.isInitialized) return;
+ this.isInitialized = true;
+ var opts = utils.createOptions({}, this.options, options);
+ this.snapdragon = this.options.snapdragon || new Snapdragon(opts);
+ this.compiler = this.snapdragon.compiler;
+ this.parser = this.snapdragon.parser;
+
+ compilers(this.snapdragon, opts);
+ parsers(this.snapdragon, opts);
+
+ /**
+ * Call Snapdragon `.parse` method. When AST is returned, we check to
+ * see if any unclosed braces are left on the stack and, if so, we iterate
+ * over the stack and correct the AST so that compilers are called in the correct
+ * order and unbalance braces are properly escaped.
+ */
+
+ utils.define(this.snapdragon, 'parse', function(pattern, options) {
+ var parsed = Snapdragon.prototype.parse.apply(this, arguments);
+ this.parser.ast.input = pattern;
+
+ var stack = this.parser.stack;
+ while (stack.length) {
+ addParent({type: 'brace.close', val: ''}, stack.pop());
+ }
+
+ function addParent(node, parent) {
+ utils.define(node, 'parent', parent);
+ parent.nodes.push(node);
+ }
+
+ // add non-enumerable parser reference
+ utils.define(parsed, 'parser', this.parser);
+ return parsed;
+ });
+};
+
+/**
+ * Decorate `.parse` method
+ */
+
+Braces.prototype.parse = function(ast, options) {
+ if (ast && typeof ast === 'object' && ast.nodes) return ast;
+ this.init(options);
+ return this.snapdragon.parse(ast, options);
+};
+
+/**
+ * Decorate `.compile` method
+ */
+
+Braces.prototype.compile = function(ast, options) {
+ if (typeof ast === 'string') {
+ ast = this.parse(ast, options);
+ } else {
+ this.init(options);
+ }
+ return this.snapdragon.compile(ast, options);
+};
+
+/**
+ * Expand
+ */
+
+Braces.prototype.expand = function(pattern) {
+ var ast = this.parse(pattern, {expand: true});
+ return this.compile(ast, {expand: true});
+};
+
+/**
+ * Optimize
+ */
+
+Braces.prototype.optimize = function(pattern) {
+ var ast = this.parse(pattern, {optimize: true});
+ return this.compile(ast, {optimize: true});
+};
+
+/**
+ * Expose `Braces`
+ */
+
+module.exports = Braces;
diff --git a/chatto/src/main/javascript/node_modules/braces/lib/compilers.js b/chatto/src/main/javascript/node_modules/braces/lib/compilers.js
new file mode 100644
index 0000000..a3b820e
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/braces/lib/compilers.js
@@ -0,0 +1,282 @@
+'use strict';
+
+var utils = require('./utils');
+
+module.exports = function(braces, options) {
+ braces.compiler
+
+ /**
+ * bos
+ */
+
+ .set('bos', function() {
+ if (this.output) return;
+ this.ast.queue = isEscaped(this.ast) ? [this.ast.val] : [];
+ this.ast.count = 1;
+ })
+
+ /**
+ * Square brackets
+ */
+
+ .set('bracket', function(node) {
+ var close = node.close;
+ var open = !node.escaped ? '[' : '\\[';
+ var negated = node.negated;
+ var inner = node.inner;
+
+ inner = inner.replace(/\\(?=[\\\w]|$)/g, '\\\\');
+ if (inner === ']-') {
+ inner = '\\]\\-';
+ }
+
+ if (negated && inner.indexOf('.') === -1) {
+ inner += '.';
+ }
+ if (negated && inner.indexOf('/') === -1) {
+ inner += '/';
+ }
+
+ var val = open + negated + inner + close;
+ var queue = node.parent.queue;
+ var last = utils.arrayify(queue.pop());
+
+ queue.push(utils.join(last, val));
+ queue.push.apply(queue, []);
+ })
+
+ /**
+ * Brace
+ */
+
+ .set('brace', function(node) {
+ node.queue = isEscaped(node) ? [node.val] : [];
+ node.count = 1;
+ return this.mapVisit(node.nodes);
+ })
+
+ /**
+ * Open
+ */
+
+ .set('brace.open', function(node) {
+ node.parent.open = node.val;
+ })
+
+ /**
+ * Inner
+ */
+
+ .set('text', function(node) {
+ var queue = node.parent.queue;
+ var escaped = node.escaped;
+ var segs = [node.val];
+
+ if (node.optimize === false) {
+ options = utils.extend({}, options, {optimize: false});
+ }
+
+ if (node.multiplier > 1) {
+ node.parent.count *= node.multiplier;
+ }
+
+ if (options.quantifiers === true && utils.isQuantifier(node.val)) {
+ escaped = true;
+
+ } else if (node.val.length > 1) {
+ if (isType(node.parent, 'brace') && !isEscaped(node)) {
+ var expanded = utils.expand(node.val, options);
+ segs = expanded.segs;
+
+ if (expanded.isOptimized) {
+ node.parent.isOptimized = true;
+ }
+
+ // if nothing was expanded, we probably have a literal brace
+ if (!segs.length) {
+ var val = (expanded.val || node.val);
+ if (options.unescape !== false) {
+ // unescape unexpanded brace sequence/set separators
+ val = val.replace(/\\([,.])/g, '$1');
+ // strip quotes
+ val = val.replace(/["'`]/g, '');
+ }
+
+ segs = [val];
+ escaped = true;
+ }
+ }
+
+ } else if (node.val === ',') {
+ if (options.expand) {
+ node.parent.queue.push(['']);
+ segs = [''];
+ } else {
+ segs = ['|'];
+ }
+ } else {
+ escaped = true;
+ }
+
+ if (escaped && isType(node.parent, 'brace')) {
+ if (node.parent.nodes.length <= 4 && node.parent.count === 1) {
+ node.parent.escaped = true;
+ } else if (node.parent.length <= 3) {
+ node.parent.escaped = true;
+ }
+ }
+
+ if (!hasQueue(node.parent)) {
+ node.parent.queue = segs;
+ return;
+ }
+
+ var last = utils.arrayify(queue.pop());
+ if (node.parent.count > 1 && options.expand) {
+ last = multiply(last, node.parent.count);
+ node.parent.count = 1;
+ }
+
+ queue.push(utils.join(utils.flatten(last), segs.shift()));
+ queue.push.apply(queue, segs);
+ })
+
+ /**
+ * Close
+ */
+
+ .set('brace.close', function(node) {
+ var queue = node.parent.queue;
+ var prev = node.parent.parent;
+ var last = prev.queue.pop();
+ var open = node.parent.open;
+ var close = node.val;
+
+ if (open && close && isOptimized(node, options)) {
+ open = '(';
+ close = ')';
+ }
+
+ // if a close brace exists, and the previous segment is one character
+ // don't wrap the result in braces or parens
+ var ele = utils.last(queue);
+ if (node.parent.count > 1 && options.expand) {
+ ele = multiply(queue.pop(), node.parent.count);
+ node.parent.count = 1;
+ queue.push(ele);
+ }
+
+ if (close && typeof ele === 'string' && ele.length === 1) {
+ open = '';
+ close = '';
+ }
+
+ if ((isLiteralBrace(node, options) || noInner(node)) && !node.parent.hasEmpty) {
+ queue.push(utils.join(open, queue.pop() || ''));
+ queue = utils.flatten(utils.join(queue, close));
+ }
+
+ if (typeof last === 'undefined') {
+ prev.queue = [queue];
+ } else {
+ prev.queue.push(utils.flatten(utils.join(last, queue)));
+ }
+ })
+
+ /**
+ * eos
+ */
+
+ .set('eos', function(node) {
+ if (this.input) return;
+
+ if (options.optimize !== false) {
+ this.output = utils.last(utils.flatten(this.ast.queue));
+ } else if (Array.isArray(utils.last(this.ast.queue))) {
+ this.output = utils.flatten(this.ast.queue.pop());
+ } else {
+ this.output = utils.flatten(this.ast.queue);
+ }
+
+ if (node.parent.count > 1 && options.expand) {
+ this.output = multiply(this.output, node.parent.count);
+ }
+
+ this.output = utils.arrayify(this.output);
+ this.ast.queue = [];
+ });
+
+};
+
+/**
+ * Multiply the segments in the current brace level
+ */
+
+function multiply(queue, n, options) {
+ return utils.flatten(utils.repeat(utils.arrayify(queue), n));
+}
+
+/**
+ * Return true if `node` is escaped
+ */
+
+function isEscaped(node) {
+ return node.escaped === true;
+}
+
+/**
+ * Returns true if regex parens should be used for sets. If the parent `type`
+ * is not `brace`, then we're on a root node, which means we should never
+ * expand segments and open/close braces should be `{}` (since this indicates
+ * a brace is missing from the set)
+ */
+
+function isOptimized(node, options) {
+ if (node.parent.isOptimized) return true;
+ return isType(node.parent, 'brace')
+ && !isEscaped(node.parent)
+ && options.expand !== true;
+}
+
+/**
+ * Returns true if the value in `node` should be wrapped in a literal brace.
+ * @return {Boolean}
+ */
+
+function isLiteralBrace(node, options) {
+ return isEscaped(node.parent) || options.optimize !== false;
+}
+
+/**
+ * Returns true if the given `node` does not have an inner value.
+ * @return {Boolean}
+ */
+
+function noInner(node, type) {
+ if (node.parent.queue.length === 1) {
+ return true;
+ }
+ var nodes = node.parent.nodes;
+ return nodes.length === 3
+ && isType(nodes[0], 'brace.open')
+ && !isType(nodes[1], 'text')
+ && isType(nodes[2], 'brace.close');
+}
+
+/**
+ * Returns true if the given `node` is the given `type`
+ * @return {Boolean}
+ */
+
+function isType(node, type) {
+ return typeof node !== 'undefined' && node.type === type;
+}
+
+/**
+ * Returns true if the given `node` has a non-empty queue.
+ * @return {Boolean}
+ */
+
+function hasQueue(node) {
+ return Array.isArray(node.queue) && node.queue.length;
+}
diff --git a/chatto/src/main/javascript/node_modules/braces/lib/parsers.js b/chatto/src/main/javascript/node_modules/braces/lib/parsers.js
new file mode 100644
index 0000000..8bf3e92
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/braces/lib/parsers.js
@@ -0,0 +1,360 @@
+'use strict';
+
+var Node = require('snapdragon-node');
+var utils = require('./utils');
+
+/**
+ * Braces parsers
+ */
+
+module.exports = function(braces, options) {
+ braces.parser
+ .set('bos', function() {
+ if (!this.parsed) {
+ this.ast = this.nodes[0] = new Node(this.ast);
+ }
+ })
+
+ /**
+ * Character parsers
+ */
+
+ .set('escape', function() {
+ var pos = this.position();
+ var m = this.match(/^(?:\\(.)|\$\{)/);
+ if (!m) return;
+
+ var prev = this.prev();
+ var last = utils.last(prev.nodes);
+
+ var node = pos(new Node({
+ type: 'text',
+ multiplier: 1,
+ val: m[0]
+ }));
+
+ if (node.val === '\\\\') {
+ return node;
+ }
+
+ if (node.val === '${') {
+ var str = this.input;
+ var idx = -1;
+ var ch;
+
+ while ((ch = str[++idx])) {
+ this.consume(1);
+ node.val += ch;
+ if (ch === '\\') {
+ node.val += str[++idx];
+ continue;
+ }
+ if (ch === '}') {
+ break;
+ }
+ }
+ }
+
+ if (this.options.unescape !== false) {
+ node.val = node.val.replace(/\\([{}])/g, '$1');
+ }
+
+ if (last.val === '"' && this.input.charAt(0) === '"') {
+ last.val = node.val;
+ this.consume(1);
+ return;
+ }
+
+ return concatNodes.call(this, pos, node, prev, options);
+ })
+
+ /**
+ * Brackets: "[...]" (basic, this is overridden by
+ * other parsers in more advanced implementations)
+ */
+
+ .set('bracket', function() {
+ var isInside = this.isInside('brace');
+ var pos = this.position();
+ var m = this.match(/^(?:\[([!^]?)([^\]]{2,}|\]-)(\]|[^*+?]+)|\[)/);
+ if (!m) return;
+
+ var prev = this.prev();
+ var val = m[0];
+ var negated = m[1] ? '^' : '';
+ var inner = m[2] || '';
+ var close = m[3] || '';
+
+ if (isInside && prev.type === 'brace') {
+ prev.text = prev.text || '';
+ prev.text += val;
+ }
+
+ var esc = this.input.slice(0, 2);
+ if (inner === '' && esc === '\\]') {
+ inner += esc;
+ this.consume(2);
+
+ var str = this.input;
+ var idx = -1;
+ var ch;
+
+ while ((ch = str[++idx])) {
+ this.consume(1);
+ if (ch === ']') {
+ close = ch;
+ break;
+ }
+ inner += ch;
+ }
+ }
+
+ return pos(new Node({
+ type: 'bracket',
+ val: val,
+ escaped: close !== ']',
+ negated: negated,
+ inner: inner,
+ close: close
+ }));
+ })
+
+ /**
+ * Empty braces (we capture these early to
+ * speed up processing in the compiler)
+ */
+
+ .set('multiplier', function() {
+ var isInside = this.isInside('brace');
+ var pos = this.position();
+ var m = this.match(/^\{((?:,|\{,+\})+)\}/);
+ if (!m) return;
+
+ this.multiplier = true;
+ var prev = this.prev();
+ var val = m[0];
+
+ if (isInside && prev.type === 'brace') {
+ prev.text = prev.text || '';
+ prev.text += val;
+ }
+
+ var node = pos(new Node({
+ type: 'text',
+ multiplier: 1,
+ match: m,
+ val: val
+ }));
+
+ return concatNodes.call(this, pos, node, prev, options);
+ })
+
+ /**
+ * Open
+ */
+
+ .set('brace.open', function() {
+ var pos = this.position();
+ var m = this.match(/^\{(?!(?:[^\\}]?|,+)\})/);
+ if (!m) return;
+
+ var prev = this.prev();
+ var last = utils.last(prev.nodes);
+
+ // if the last parsed character was an extglob character
+ // we need to _not optimize_ the brace pattern because
+ // it might be mistaken for an extglob by a downstream parser
+ if (last && last.val && isExtglobChar(last.val.slice(-1))) {
+ last.optimize = false;
+ }
+
+ var open = pos(new Node({
+ type: 'brace.open',
+ val: m[0]
+ }));
+
+ var node = pos(new Node({
+ type: 'brace',
+ nodes: []
+ }));
+
+ node.push(open);
+ prev.push(node);
+ this.push('brace', node);
+ })
+
+ /**
+ * Close
+ */
+
+ .set('brace.close', function() {
+ var pos = this.position();
+ var m = this.match(/^\}/);
+ if (!m || !m[0]) return;
+
+ var brace = this.pop('brace');
+ var node = pos(new Node({
+ type: 'brace.close',
+ val: m[0]
+ }));
+
+ if (!this.isType(brace, 'brace')) {
+ if (this.options.strict) {
+ throw new Error('missing opening "{"');
+ }
+ node.type = 'text';
+ node.multiplier = 0;
+ node.escaped = true;
+ return node;
+ }
+
+ var prev = this.prev();
+ var last = utils.last(prev.nodes);
+ if (last.text) {
+ var lastNode = utils.last(last.nodes);
+ if (lastNode.val === ')' && /[!@*?+]\(/.test(last.text)) {
+ var open = last.nodes[0];
+ var text = last.nodes[1];
+ if (open.type === 'brace.open' && text && text.type === 'text') {
+ text.optimize = false;
+ }
+ }
+ }
+
+ if (brace.nodes.length > 2) {
+ var first = brace.nodes[1];
+ if (first.type === 'text' && first.val === ',') {
+ brace.nodes.splice(1, 1);
+ brace.nodes.push(first);
+ }
+ }
+
+ brace.push(node);
+ })
+
+ /**
+ * Capture boundary characters
+ */
+
+ .set('boundary', function() {
+ var pos = this.position();
+ var m = this.match(/^[$^](?!\{)/);
+ if (!m) return;
+ return pos(new Node({
+ type: 'text',
+ val: m[0]
+ }));
+ })
+
+ /**
+ * One or zero, non-comma characters wrapped in braces
+ */
+
+ .set('nobrace', function() {
+ var isInside = this.isInside('brace');
+ var pos = this.position();
+ var m = this.match(/^\{[^,]?\}/);
+ if (!m) return;
+
+ var prev = this.prev();
+ var val = m[0];
+
+ if (isInside && prev.type === 'brace') {
+ prev.text = prev.text || '';
+ prev.text += val;
+ }
+
+ return pos(new Node({
+ type: 'text',
+ multiplier: 0,
+ val: val
+ }));
+ })
+
+ /**
+ * Text
+ */
+
+ .set('text', function() {
+ var isInside = this.isInside('brace');
+ var pos = this.position();
+ var m = this.match(/^((?!\\)[^${}[\]])+/);
+ if (!m) return;
+
+ var prev = this.prev();
+ var val = m[0];
+
+ if (isInside && prev.type === 'brace') {
+ prev.text = prev.text || '';
+ prev.text += val;
+ }
+
+ var node = pos(new Node({
+ type: 'text',
+ multiplier: 1,
+ val: val
+ }));
+
+ return concatNodes.call(this, pos, node, prev, options);
+ });
+};
+
+/**
+ * Returns true if the character is an extglob character.
+ */
+
+function isExtglobChar(ch) {
+ return ch === '!' || ch === '@' || ch === '*' || ch === '?' || ch === '+';
+}
+
+/**
+ * Combine text nodes, and calculate empty sets (`{,,}`)
+ * @param {Function} `pos` Function to calculate node position
+ * @param {Object} `node` AST node
+ * @return {Object}
+ */
+
+function concatNodes(pos, node, parent, options) {
+ node.orig = node.val;
+ var prev = this.prev();
+ var last = utils.last(prev.nodes);
+ var isEscaped = false;
+
+ if (node.val.length > 1) {
+ var a = node.val.charAt(0);
+ var b = node.val.slice(-1);
+
+ isEscaped = (a === '"' && b === '"')
+ || (a === "'" && b === "'")
+ || (a === '`' && b === '`');
+ }
+
+ if (isEscaped && options.unescape !== false) {
+ node.val = node.val.slice(1, node.val.length - 1);
+ node.escaped = true;
+ }
+
+ if (node.match) {
+ var match = node.match[1];
+ if (!match || match.indexOf('}') === -1) {
+ match = node.match[0];
+ }
+
+ // replace each set with a single ","
+ var val = match.replace(/\{/g, ',').replace(/\}/g, '');
+ node.multiplier *= val.length;
+ node.val = '';
+ }
+
+ var simpleText = last.type === 'text'
+ && last.multiplier === 1
+ && node.multiplier === 1
+ && node.val;
+
+ if (simpleText) {
+ last.val += node.val;
+ return;
+ }
+
+ prev.push(node);
+}
diff --git a/chatto/src/main/javascript/node_modules/braces/lib/utils.js b/chatto/src/main/javascript/node_modules/braces/lib/utils.js
new file mode 100644
index 0000000..4716671
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/braces/lib/utils.js
@@ -0,0 +1,343 @@
+'use strict';
+
+var splitString = require('split-string');
+var utils = module.exports;
+
+/**
+ * Module dependencies
+ */
+
+utils.extend = require('extend-shallow');
+utils.flatten = require('arr-flatten');
+utils.isObject = require('isobject');
+utils.fillRange = require('fill-range');
+utils.repeat = require('repeat-element');
+utils.unique = require('array-unique');
+
+utils.define = function(obj, key, val) {
+ Object.defineProperty(obj, key, {
+ writable: true,
+ configurable: true,
+ enumerable: false,
+ value: val
+ });
+};
+
+/**
+ * Returns true if the given string contains only empty brace sets.
+ */
+
+utils.isEmptySets = function(str) {
+ return /^(?:\{,\})+$/.test(str);
+};
+
+/**
+ * Returns true if the given string contains only empty brace sets.
+ */
+
+utils.isQuotedString = function(str) {
+ var open = str.charAt(0);
+ if (open === '\'' || open === '"' || open === '`') {
+ return str.slice(-1) === open;
+ }
+ return false;
+};
+
+/**
+ * Create the key to use for memoization. The unique key is generated
+ * by iterating over the options and concatenating key-value pairs
+ * to the pattern string.
+ */
+
+utils.createKey = function(pattern, options) {
+ var id = pattern;
+ if (typeof options === 'undefined') {
+ return id;
+ }
+ var keys = Object.keys(options);
+ for (var i = 0; i < keys.length; i++) {
+ var key = keys[i];
+ id += ';' + key + '=' + String(options[key]);
+ }
+ return id;
+};
+
+/**
+ * Normalize options
+ */
+
+utils.createOptions = function(options) {
+ var opts = utils.extend.apply(null, arguments);
+ if (typeof opts.expand === 'boolean') {
+ opts.optimize = !opts.expand;
+ }
+ if (typeof opts.optimize === 'boolean') {
+ opts.expand = !opts.optimize;
+ }
+ if (opts.optimize === true) {
+ opts.makeRe = true;
+ }
+ return opts;
+};
+
+/**
+ * Join patterns in `a` to patterns in `b`
+ */
+
+utils.join = function(a, b, options) {
+ options = options || {};
+ a = utils.arrayify(a);
+ b = utils.arrayify(b);
+
+ if (!a.length) return b;
+ if (!b.length) return a;
+
+ var len = a.length;
+ var idx = -1;
+ var arr = [];
+
+ while (++idx < len) {
+ var val = a[idx];
+ if (Array.isArray(val)) {
+ for (var i = 0; i < val.length; i++) {
+ val[i] = utils.join(val[i], b, options);
+ }
+ arr.push(val);
+ continue;
+ }
+
+ for (var j = 0; j < b.length; j++) {
+ var bval = b[j];
+
+ if (Array.isArray(bval)) {
+ arr.push(utils.join(val, bval, options));
+ } else {
+ arr.push(val + bval);
+ }
+ }
+ }
+ return arr;
+};
+
+/**
+ * Split the given string on `,` if not escaped.
+ */
+
+utils.split = function(str, options) {
+ var opts = utils.extend({sep: ','}, options);
+ if (typeof opts.keepQuotes !== 'boolean') {
+ opts.keepQuotes = true;
+ }
+ if (opts.unescape === false) {
+ opts.keepEscaping = true;
+ }
+ return splitString(str, opts, utils.escapeBrackets(opts));
+};
+
+/**
+ * Expand ranges or sets in the given `pattern`.
+ *
+ * @param {String} `str`
+ * @param {Object} `options`
+ * @return {Object}
+ */
+
+utils.expand = function(str, options) {
+ var opts = utils.extend({rangeLimit: 10000}, options);
+ var segs = utils.split(str, opts);
+ var tok = { segs: segs };
+
+ if (utils.isQuotedString(str)) {
+ return tok;
+ }
+
+ if (opts.rangeLimit === true) {
+ opts.rangeLimit = 10000;
+ }
+
+ if (segs.length > 1) {
+ if (opts.optimize === false) {
+ tok.val = segs[0];
+ return tok;
+ }
+
+ tok.segs = utils.stringifyArray(tok.segs);
+ } else if (segs.length === 1) {
+ var arr = str.split('..');
+
+ if (arr.length === 1) {
+ tok.val = tok.segs[tok.segs.length - 1] || tok.val || str;
+ tok.segs = [];
+ return tok;
+ }
+
+ if (arr.length === 2 && arr[0] === arr[1]) {
+ tok.escaped = true;
+ tok.val = arr[0];
+ tok.segs = [];
+ return tok;
+ }
+
+ if (arr.length > 1) {
+ if (opts.optimize !== false) {
+ opts.optimize = true;
+ delete opts.expand;
+ }
+
+ if (opts.optimize !== true) {
+ var min = Math.min(arr[0], arr[1]);
+ var max = Math.max(arr[0], arr[1]);
+ var step = arr[2] || 1;
+
+ if (opts.rangeLimit !== false && ((max - min) / step >= opts.rangeLimit)) {
+ throw new RangeError('expanded array length exceeds range limit. Use options.rangeLimit to increase or disable the limit.');
+ }
+ }
+
+ arr.push(opts);
+ tok.segs = utils.fillRange.apply(null, arr);
+
+ if (!tok.segs.length) {
+ tok.escaped = true;
+ tok.val = str;
+ return tok;
+ }
+
+ if (opts.optimize === true) {
+ tok.segs = utils.stringifyArray(tok.segs);
+ }
+
+ if (tok.segs === '') {
+ tok.val = str;
+ } else {
+ tok.val = tok.segs[0];
+ }
+ return tok;
+ }
+ } else {
+ tok.val = str;
+ }
+ return tok;
+};
+
+/**
+ * Ensure commas inside brackets and parens are not split.
+ * @param {Object} `tok` Token from the `split-string` module
+ * @return {undefined}
+ */
+
+utils.escapeBrackets = function(options) {
+ return function(tok) {
+ if (tok.escaped && tok.val === 'b') {
+ tok.val = '\\b';
+ return;
+ }
+
+ if (tok.val !== '(' && tok.val !== '[') return;
+ var opts = utils.extend({}, options);
+ var brackets = [];
+ var parens = [];
+ var stack = [];
+ var val = tok.val;
+ var str = tok.str;
+ var i = tok.idx - 1;
+
+ while (++i < str.length) {
+ var ch = str[i];
+
+ if (ch === '\\') {
+ val += (opts.keepEscaping === false ? '' : ch) + str[++i];
+ continue;
+ }
+
+ if (ch === '(') {
+ parens.push(ch);
+ stack.push(ch);
+ }
+
+ if (ch === '[') {
+ brackets.push(ch);
+ stack.push(ch);
+ }
+
+ if (ch === ')') {
+ parens.pop();
+ stack.pop();
+ if (!stack.length) {
+ val += ch;
+ break;
+ }
+ }
+
+ if (ch === ']') {
+ brackets.pop();
+ stack.pop();
+ if (!stack.length) {
+ val += ch;
+ break;
+ }
+ }
+ val += ch;
+ }
+
+ tok.split = false;
+ tok.val = val.slice(1);
+ tok.idx = i;
+ };
+};
+
+/**
+ * Returns true if the given string looks like a regex quantifier
+ * @return {Boolean}
+ */
+
+utils.isQuantifier = function(str) {
+ return /^(?:[0-9]?,[0-9]|[0-9],)$/.test(str);
+};
+
+/**
+ * Cast `val` to an array.
+ * @param {*} `val`
+ */
+
+utils.stringifyArray = function(arr) {
+ return [utils.arrayify(arr).join('|')];
+};
+
+/**
+ * Cast `val` to an array.
+ * @param {*} `val`
+ */
+
+utils.arrayify = function(arr) {
+ if (typeof arr === 'undefined') {
+ return [];
+ }
+ if (typeof arr === 'string') {
+ return [arr];
+ }
+ return arr;
+};
+
+/**
+ * Returns true if the given `str` is a non-empty string
+ * @return {Boolean}
+ */
+
+utils.isString = function(str) {
+ return str != null && typeof str === 'string';
+};
+
+/**
+ * Get the last element from `array`
+ * @param {Array} `array`
+ * @return {*}
+ */
+
+utils.last = function(arr, n) {
+ return arr[arr.length - (n || 1)];
+};
+
+utils.escapeRegex = function(str) {
+ return str.replace(/\\?([!^*?()[\]{}+?/])/g, '\\$1');
+};
diff --git a/chatto/src/main/javascript/node_modules/braces/node_modules/extend-shallow/LICENSE b/chatto/src/main/javascript/node_modules/braces/node_modules/extend-shallow/LICENSE
new file mode 100644
index 0000000..fa30c4c
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/braces/node_modules/extend-shallow/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2015, Jon Schlinkert.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/chatto/src/main/javascript/node_modules/braces/node_modules/extend-shallow/README.md b/chatto/src/main/javascript/node_modules/braces/node_modules/extend-shallow/README.md
new file mode 100644
index 0000000..cdc45d4
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/braces/node_modules/extend-shallow/README.md
@@ -0,0 +1,61 @@
+# extend-shallow [![NPM version](https://badge.fury.io/js/extend-shallow.svg)](http://badge.fury.io/js/extend-shallow) [![Build Status](https://travis-ci.org/jonschlinkert/extend-shallow.svg)](https://travis-ci.org/jonschlinkert/extend-shallow)
+
+> Extend an object with the properties of additional objects. node.js/javascript util.
+
+## Install
+
+Install with [npm](https://www.npmjs.com/)
+
+```sh
+$ npm i extend-shallow --save
+```
+
+## Usage
+
+```js
+var extend = require('extend-shallow');
+
+extend({a: 'b'}, {c: 'd'})
+//=> {a: 'b', c: 'd'}
+```
+
+Pass an empty object to shallow clone:
+
+```js
+var obj = {};
+extend(obj, {a: 'b'}, {c: 'd'})
+//=> {a: 'b', c: 'd'}
+```
+
+## Related
+
+* [extend-shallow](https://github.com/jonschlinkert/extend-shallow): Extend an object with the properties of additional objects. node.js/javascript util.
+* [for-own](https://github.com/jonschlinkert/for-own): Iterate over the own enumerable properties of an object, and return an object with properties… [more](https://github.com/jonschlinkert/for-own)
+* [for-in](https://github.com/jonschlinkert/for-in): Iterate over the own and inherited enumerable properties of an objecte, and return an object… [more](https://github.com/jonschlinkert/for-in)
+* [is-plain-object](https://github.com/jonschlinkert/is-plain-object): Returns true if an object was created by the `Object` constructor.
+* [isobject](https://github.com/jonschlinkert/isobject): Returns true if the value is an object and not an array or null.
+* [kind-of](https://github.com/jonschlinkert/kind-of): Get the native type of a value.
+
+## Running tests
+
+Install dev dependencies:
+
+```sh
+$ npm i -d && npm test
+```
+
+## Author
+
+**Jon Schlinkert**
+
++ [github/jonschlinkert](https://github.com/jonschlinkert)
++ [twitter/jonschlinkert](http://twitter.com/jonschlinkert)
+
+## License
+
+Copyright © 2015 Jon Schlinkert
+Released under the MIT license.
+
+***
+
+_This file was generated by [verb-cli](https://github.com/assemble/verb-cli) on June 29, 2015._
\ No newline at end of file
diff --git a/chatto/src/main/javascript/node_modules/braces/node_modules/extend-shallow/index.js b/chatto/src/main/javascript/node_modules/braces/node_modules/extend-shallow/index.js
new file mode 100644
index 0000000..92a067f
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/braces/node_modules/extend-shallow/index.js
@@ -0,0 +1,33 @@
+'use strict';
+
+var isObject = require('is-extendable');
+
+module.exports = function extend(o/*, objects*/) {
+ if (!isObject(o)) { o = {}; }
+
+ var len = arguments.length;
+ for (var i = 1; i < len; i++) {
+ var obj = arguments[i];
+
+ if (isObject(obj)) {
+ assign(o, obj);
+ }
+ }
+ return o;
+};
+
+function assign(a, b) {
+ for (var key in b) {
+ if (hasOwn(b, key)) {
+ a[key] = b[key];
+ }
+ }
+}
+
+/**
+ * Returns true if the given `key` is an own property of `obj`.
+ */
+
+function hasOwn(obj, key) {
+ return Object.prototype.hasOwnProperty.call(obj, key);
+}
diff --git a/chatto/src/main/javascript/node_modules/braces/node_modules/extend-shallow/package.json b/chatto/src/main/javascript/node_modules/braces/node_modules/extend-shallow/package.json
new file mode 100644
index 0000000..2e2f3e4
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/braces/node_modules/extend-shallow/package.json
@@ -0,0 +1,87 @@
+{
+ "_from": "extend-shallow@^2.0.1",
+ "_id": "extend-shallow@2.0.1",
+ "_inBundle": false,
+ "_integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+ "_location": "/braces/extend-shallow",
+ "_phantomChildren": {},
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "extend-shallow@^2.0.1",
+ "name": "extend-shallow",
+ "escapedName": "extend-shallow",
+ "rawSpec": "^2.0.1",
+ "saveSpec": null,
+ "fetchSpec": "^2.0.1"
+ },
+ "_requiredBy": [
+ "/braces"
+ ],
+ "_resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+ "_shasum": "51af7d614ad9a9f610ea1bafbb989d6b1c56890f",
+ "_spec": "extend-shallow@^2.0.1",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/braces",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/jonschlinkert/extend-shallow/issues"
+ },
+ "bundleDependencies": false,
+ "dependencies": {
+ "is-extendable": "^0.1.0"
+ },
+ "deprecated": false,
+ "description": "Extend an object with the properties of additional objects. node.js/javascript util.",
+ "devDependencies": {
+ "array-slice": "^0.2.3",
+ "benchmarked": "^0.1.4",
+ "chalk": "^1.0.0",
+ "for-own": "^0.1.3",
+ "glob": "^5.0.12",
+ "is-plain-object": "^2.0.1",
+ "kind-of": "^2.0.0",
+ "minimist": "^1.1.1",
+ "mocha": "^2.2.5",
+ "should": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js"
+ ],
+ "homepage": "https://github.com/jonschlinkert/extend-shallow",
+ "keywords": [
+ "assign",
+ "extend",
+ "javascript",
+ "js",
+ "keys",
+ "merge",
+ "obj",
+ "object",
+ "prop",
+ "properties",
+ "property",
+ "props",
+ "shallow",
+ "util",
+ "utility",
+ "utils",
+ "value"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "extend-shallow",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jonschlinkert/extend-shallow.git"
+ },
+ "scripts": {
+ "test": "mocha"
+ },
+ "version": "2.0.1"
+}
diff --git a/chatto/src/main/javascript/node_modules/braces/package.json b/chatto/src/main/javascript/node_modules/braces/package.json
new file mode 100644
index 0000000..e22ffc8
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/braces/package.json
@@ -0,0 +1,156 @@
+{
+ "_from": "braces@^2.3.1",
+ "_id": "braces@2.3.2",
+ "_inBundle": false,
+ "_integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==",
+ "_location": "/braces",
+ "_phantomChildren": {
+ "is-extendable": "0.1.1"
+ },
+ "_requested": {
+ "type": "range",
+ "registry": true,
+ "raw": "braces@^2.3.1",
+ "name": "braces",
+ "escapedName": "braces",
+ "rawSpec": "^2.3.1",
+ "saveSpec": null,
+ "fetchSpec": "^2.3.1"
+ },
+ "_requiredBy": [
+ "/micromatch"
+ ],
+ "_resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz",
+ "_shasum": "5979fd3f14cd531565e5fa2df1abfff1dfaee729",
+ "_spec": "braces@^2.3.1",
+ "_where": "/home/rohan/git/chatto-spring/chatto/src/main/javascript/node_modules/micromatch",
+ "author": {
+ "name": "Jon Schlinkert",
+ "url": "https://github.com/jonschlinkert"
+ },
+ "bugs": {
+ "url": "https://github.com/micromatch/braces/issues"
+ },
+ "bundleDependencies": false,
+ "contributors": [
+ {
+ "name": "Brian Woodward",
+ "url": "https://twitter.com/doowb"
+ },
+ {
+ "name": "Elan Shanker",
+ "url": "https://github.com/es128"
+ },
+ {
+ "name": "Eugene Sharygin",
+ "url": "https://github.com/eush77"
+ },
+ {
+ "name": "hemanth.hm",
+ "url": "http://h3manth.com"
+ },
+ {
+ "name": "Jon Schlinkert",
+ "url": "http://twitter.com/jonschlinkert"
+ }
+ ],
+ "dependencies": {
+ "arr-flatten": "^1.1.0",
+ "array-unique": "^0.3.2",
+ "extend-shallow": "^2.0.1",
+ "fill-range": "^4.0.0",
+ "isobject": "^3.0.1",
+ "repeat-element": "^1.1.2",
+ "snapdragon": "^0.8.1",
+ "snapdragon-node": "^2.0.1",
+ "split-string": "^3.0.2",
+ "to-regex": "^3.0.1"
+ },
+ "deprecated": false,
+ "description": "Bash-like brace expansion, implemented in JavaScript. Safer than other brace expansion libs, with complete support for the Bash 4.3 braces specification, without sacrificing speed.",
+ "devDependencies": {
+ "ansi-cyan": "^0.1.1",
+ "benchmarked": "^2.0.0",
+ "brace-expansion": "^1.1.8",
+ "cross-spawn": "^5.1.0",
+ "gulp": "^3.9.1",
+ "gulp-eslint": "^4.0.0",
+ "gulp-format-md": "^1.0.0",
+ "gulp-istanbul": "^1.1.2",
+ "gulp-mocha": "^3.0.1",
+ "gulp-unused": "^0.2.1",
+ "is-windows": "^1.0.1",
+ "minimatch": "^3.0.4",
+ "mocha": "^3.2.0",
+ "noncharacters": "^1.1.0",
+ "text-table": "^0.2.0",
+ "time-diff": "^0.3.1",
+ "yargs-parser": "^8.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "files": [
+ "index.js",
+ "lib"
+ ],
+ "homepage": "https://github.com/micromatch/braces",
+ "keywords": [
+ "alpha",
+ "alphabetical",
+ "bash",
+ "brace",
+ "braces",
+ "expand",
+ "expansion",
+ "filepath",
+ "fill",
+ "fs",
+ "glob",
+ "globbing",
+ "letter",
+ "match",
+ "matches",
+ "matching",
+ "number",
+ "numerical",
+ "path",
+ "range",
+ "ranges",
+ "sh"
+ ],
+ "license": "MIT",
+ "main": "index.js",
+ "name": "braces",
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/micromatch/braces.git"
+ },
+ "scripts": {
+ "benchmark": "node benchmark",
+ "test": "mocha"
+ },
+ "verb": {
+ "toc": false,
+ "layout": "default",
+ "tasks": [
+ "readme"
+ ],
+ "lint": {
+ "reflinks": true
+ },
+ "plugins": [
+ "gulp-format-md"
+ ],
+ "related": {
+ "list": [
+ "expand-brackets",
+ "extglob",
+ "fill-range",
+ "micromatch",
+ "nanomatch"
+ ]
+ }
+ },
+ "version": "2.3.2"
+}
diff --git a/chatto/src/main/javascript/node_modules/browserify-css/.travis.yml b/chatto/src/main/javascript/node_modules/browserify-css/.travis.yml
new file mode 100644
index 0000000..f7a64e2
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/browserify-css/.travis.yml
@@ -0,0 +1,21 @@
+sudo: required
+dist: trusty
+group: edge
+
+language: node_js
+
+os:
+ - linux
+
+node_js:
+ - '6'
+ - '5'
+ - '4'
+
+before_install:
+ - npm install -g npm
+ - npm --version
+
+after_success:
+ - npm run coveralls
+ - npm run coverage-clean
diff --git a/chatto/src/main/javascript/node_modules/browserify-css/LICENSE b/chatto/src/main/javascript/node_modules/browserify-css/LICENSE
new file mode 100644
index 0000000..43532d0
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/browserify-css/LICENSE
@@ -0,0 +1,22 @@
+The MIT License (MIT)
+
+Copyright (c) 2014-2016 Cheton Wu
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
diff --git a/chatto/src/main/javascript/node_modules/browserify-css/README.md b/chatto/src/main/javascript/node_modules/browserify-css/README.md
new file mode 100644
index 0000000..f552f05
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/browserify-css/README.md
@@ -0,0 +1,449 @@
+# browserify-css [![build status](https://travis-ci.org/cheton/browserify-css.svg?branch=master)](https://travis-ci.org/cheton/browserify-css) [![Coverage Status](https://coveralls.io/repos/cheton/browserify-css/badge.svg?branch=master&service=github)](https://coveralls.io/github/cheton/browserify-css?branch=master)
+
+[![NPM](https://nodei.co/npm/browserify-css.png?downloads=true&stars=true)](https://www.npmjs.com/package/browserify-css)
+
+A Browserify transform for bundling, rebasing, inlining, and minifying CSS files. It's useful for CSS modularization where styles are scoped to their related bundles.
+
+## Getting Started
+
+If you're new to browserify, check out the [browserify handbook](https://github.com/substack/browserify-handbook) and the resources on [browserify.org](http://browserify.org/).
+
+## Installation
+
+`npm install --save-dev browserify-css`
+
+## Usage
+
+app.css:
+``` css
+@import url("modules/foo/index.css");
+@import url("modules/bar/index.css");
+body {
+ background-color: #fff;
+}
+```
+
+app.js:
+``` js
+var css = require('./app.css');
+console.log(css);
+```
+
+You can compile your app by passing -t browserify-css to browserify:
+``` bash
+$ browserify -t browserify-css app.js > bundle.js
+```
+
+Each `require('./path/to/file.css')` call will concatenate CSS files with @import statements, rebasing urls, inlining @import, and minifying CSS. It will add a style tag with an optional data-href attribute to the head section of the document during runtime:
+
+``` html
+
+
+
+
+
+```
+
+## Configuration
+
+You can set configuration to your package.json file:
+``` json
+{
+ "browserify-css": {
+ "autoInject": true,
+ "minify": true,
+ "rootDir": "."
+ }
+}
+```
+
+or use an external configuration file like below:
+``` json
+{
+ "browserify-css": "./config/browserify-css.js"
+}
+```
+
+config/browserify-css.js:
+``` js
+module.exports = {
+ "autoInject": true,
+ "minify": true,
+ "rootDir": "."
+};
+```
+
+Furthermore, browserify-css transform can obtain options from the command-line with subarg syntax:
+```
+$ browserify -t [ browserify-css --minify=true --output bundle.css ] -o bundle.js app.js
+```
+or from the api:
+```
+b.transform('browserify-css', {
+ minify: true,
+ output: 'bundle.css'
+});
+```
+
+## Options
+
+### autoInject
+
+Type: `Boolean`
+Default: `true`
+
+If true, each `require('path/to/file.css')` call will add a style tag to the head section of the document.
+
+### autoInjectOptions
+
+Type: `Object`
+Default:
+```js
+{
+ "verbose": true,
+ "insertAt": "bottom" // or "top"
+}
+```
+
+#### `verbose`
+
+If verbose is set to true, the path to CSS will be specified in the data-href attribute inside the style tag
+
+#### `insertAt`
+
+By default, browserify-css transform appends <style> elements to the end of the <head> tag of the page. This will cause CSS created by browserify-css transform to take priority over CSS already present in the document head. To insert style elements at the beginning of the head, set the insertAt parameter to 'top'.
+
+### inlineImages
+
+Type: `Boolean` or `Object`
+Default: `false`
+
+If true, each required css file will have image `url()` replaced with data urls. For example from:
+
+```css
+ background-image: url("background.png");
+```
+
+to:
+
+```css
+ background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAVQAAAHgCAYAAAD6yZXWAAAABmJLR0QA");
+```
+
+### inlineImagesOptions
+
+Type: `Object`
+Default:
+
+```js
+{
+ // maximum size (in bytes) of image file that will be inlined into css file
+ "limit": 0 // 0 means no limit - inline all images
+}
+```
+
+If a limit is set, then only files that are smaller than the number of bytes given will be inlined into the css file.
+
+### minify
+
+Type: `Boolean`
+Default: `false`
+
+### minifyOptions
+
+Type: `Object`
+Default: `{}`
+
+Check out a list of CSS minify options at [CleanCSS](https://github.com/jakubpawlowicz/clean-css#how-to-use-clean-css-programmatically).
+
+### onFlush
+
+Type: `Function`
+
+The `onFlush` option accepts a function which takes two arguments: (options, done).
+```js
+// @param {object} options The options object
+// @param {string} options.filename The filename
+// @param {string} options.data The CSS file content
+// @param {string} options.rootDir The root directory
+// @param {string} options.relativePath The relative path
+// @param {string} options.href The href attribute
+// @param {function} done The done callback
+onFlush: function(options, done) {
+ // Method 1:
+ // This will keep original module.exports unchanged
+ done();
+
+ // Method 2:
+ // Pass a null value to the done callback if you do not want to embed CSS into a JavaScript bundle
+ done(null);
+
+ // Method 3:
+ // Pass a text string to the done callback to customize module.exports
+ done('module.exports = ' + JSON.stringify(options.data) + ';');
+}
+```
+
+You can use the `onFlush` option to output each CSS to a separate file, or append multiple CSS into one file. For example:
+``` javascript
+var browserify = require('browserify');
+var fs = require('fs');
+
+fs.unlinkSync('dist/assets/app.css');
+
+browserify(options)
+ .add('src/index.js')
+ .transform(require('browserify-css'), {
+ rootDir: 'src',
+ onFlush: function(options, done) {
+ fs.appendFileSync('dist/assets/app.css', options.data);
+
+ // Do not embed CSS into a JavaScript bundle
+ done(null);
+ }
+ })
+ .bundle();
+```
+
+### output
+
+Type: `String`
+Default: ''
+
+The output path of the CSS file. When using this option, browserify-css will not embed stylesheets into a JavaScript bundle.
+
+```bash
+browserify -t [ browserify-css --minify=true --output bundle.css ] -o bundle.js index.js
+```
+
+### processRelativeUrl
+
+Type: `Function`
+
+The `processRelativeUrl` option accepts a function which takes one argument (the relative url) and returns the original `relativeUrl` string or the converted result. For example:
+``` javascript
+var browserify = require('browserify');
+
+browserify(options)
+ .add('src/index.js')
+ .transform(require('browserify-css'), {
+ rootDir: 'src',
+ processRelativeUrl: function(relativeUrl) {
+ return relativeUrl;
+ }
+ })
+ .bundle();
+```
+
+You can embed the image data directly into the CSS file with data URI, like so:
+``` javascript
+var _ = require('lodash');
+var path = require('path');
+var browserify = require('browserify');
+
+browserify(options)
+ .add('src/index.js')
+ .transform(require('browserify-css'), {
+ rootDir: 'src',
+ processRelativeUrl: function(relativeUrl) {
+ if (_.contains(['.jpg','.png','.gif'], path.extname(relativeUrl))) {
+ // Embed image data with data URI
+ var DataUri = require('datauri');
+ var dUri = new DataUri(relativeUrl);
+ return dUri.content;
+ }
+ return relativeUrl;
+ }
+ })
+ .bundle();
+```
+
+You may also want to check out the [FAQ](https://github.com/cheton/browserify-css#2-how-do-i-load-font-and-image-files-from-node_modules) for advanced usage.
+
+### rebaseUrls
+
+Type: `Boolean`
+Default: `true`
+
+If true, relative paths will be rebased in css files; if false, paths will be unchanged.
+
+### rootDir
+
+Type: `String`
+Default: `./`
+
+An absolute path to resolve relative paths against the project's base directory.
+
+### stripComments
+
+Type: `Boolean`
+Default: `false`
+
+Strip comments from CSS. Defaults to false.
+
+
+## FAQ
+### 1. How do I include CSS files located inside the node_modules folder?
+You can choose one of the following methods to include CSS files located inside the node_modules folder:
+
+1. The easiest way to do this is using the `@import` rule. For example:
+
+ app.js:
+ ``` javascript
+ require('./app.css');
+ ```
+
+ app.css:
+ ``` css
+ /* Use CSS from your node_modules folder */
+ @import "node_modules/foo/foo.css";
+
+ /* Or your own relative files */
+ @import "styles/common.css";
+ ```
+
+2. Use the global transform option (i.e. `--global-transform` or `-g`) on the command line to transform all files in a node_modules directory:
+
+ ``` bash
+ $ browserify -g browserify-css app.js > bundle.js
+ ```
+
+ or use the API directly:
+
+ ``` javascript
+ var browserify = require('browserify');
+ var b = browserify('./app.js');
+ b.transform('browserify-css', {global: true});
+ b.bundle().pipe(process.stdout);
+ ```
+ See [browserify transform options](https://github.com/substack/node-browserify#btransformtr-opts) for details.
+
+ Then you will be able to require CSS files from within node_modules. For example:
+ ``` javascript
+ require('bootstrap/dist/bootstrap.css');
+ ```
+
+3. Put browserify transform option into a submodule's package.json file inside the `node_modules` directory on a **per-module basis** like so:
+
+ node_modules/foo/package.json:
+ ``` json
+ {
+ "browserify": {
+ "transform": ["browserify-css"]
+ }
+ }
+ ```
+
+ Then, run browserify transform on the command line:
+ ``` bash
+ $ browserify -t browserify-css app.js > bundle.js
+ ```
+
+### 2. How do I load font and image files from node_modules?
+
+Assume that you have the following directory structure:
+``` bash
+package.json
+dist/
+src/
+ index.js
+ index.css
+node_modules/
+ bootstrap/
+ dist/
+ css/
+ bootstrap.css
+```
+
+The `index.css` uses `@import` to import external style sheets:
+``` css
+@import url("../node_modules/bootstrap/dist/css/bootstrap.css");
+```
+
+All output files, including the generated `bundle.js`, are created under the `dist` directory:
+``` bash
+dist/
+ bundle.js
+ vendor/
+ bootstrap/
+ dist/
+ css/
+ bootstrap.css
+```
+
+Suppose that the `dist` directory is your web root, you might want to copy external font and images files from `../node_modules/` to `dist/vendor/`.
+
+For example, the `@font-face` rules in `node_modules/bootstrap/dist/css/bootstrap.css`:
+``` css
+@font-face {
+ font-family: 'Glyphicons Halflings';
+ src: url('../fonts/glyphicons-halflings-regular.eot');
+ src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'),
+ url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'),
+ url('../fonts/glyphicons-halflings-regular.woff') format('woff'),
+ url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'),
+ url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
+}
+```
+
+The example below illustrates the use of the `processRelativeUrl` option:
+``` javascript
+var gulp = require('gulp');
+var gutil = require('gulp-util');
+var path = require('path');
+var browserify = require('browserify');
+var sourceStream = require('vinyl-source-stream');
+var fse = require('fs-extra');
+
+var bundleStream = browserify()
+ .add('src/index.js')
+ .transform(require('browserify-css'), {
+ rootDir: 'src',
+ processRelativeUrl: function(relativeUrl) {
+ var stripQueryStringAndHashFromPath = function(url) {
+ return url.split('?')[0].split('#')[0];
+ };
+ var rootDir = path.resolve(process.cwd(), 'src');
+ var relativePath = stripQueryStringAndHashFromPath(relativeUrl);
+ var queryStringAndHash = relativeUrl.substring(relativePath.length);
+
+ //
+ // Copying files from '../node_modules/bootstrap/' to 'dist/vendor/bootstrap/'
+ //
+ var prefix = '../node_modules/';
+ if (_.startsWith(relativePath, prefix)) {
+ var vendorPath = 'vendor/' + relativePath.substring(prefix.length);
+ var source = path.join(rootDir, relativePath);
+ var target = path.join(rootDir, vendorPath);
+
+ gutil.log('Copying file from ' + JSON.stringify(source) + ' to ' + JSON.stringify(target));
+ fse.copySync(source, target);
+
+ // Returns a new path string with original query string and hash fragments
+ return vendorPath + queryStringAndHash;
+ }
+
+ return relativeUrl;
+ }
+ })
+ .bundle();
+
+bundleStream
+ .pipe(sourceStream(bundleFile))
+ .pipe(gulp.dest(browserifyConfig.dest));
+
+```
+
+## Acknowledgements
+
+Test Images:
+- [test/fixtures/background.png](test/fixtures/background.png)
+Originally by [W3C](http://www.w3.org/html/logo/) under terms of [CC-BY-3.0](http://creativecommons.org/licenses/by/3.0), via [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:Class-header-css3.jpg)
+
+- [test/fixtures/background-600.png](test/fixtures/background-600.png)
+Originally by Rudloff under terms of [CC-BY-3.0](http://creativecommons.org/licenses/by/3.0), via [Wikimedia Commons](https://commons.wikimedia.org/wiki/File:CSS3_logo_and_wordmark.svg)
+
+
+## License
+
+MIT
diff --git a/chatto/src/main/javascript/node_modules/browserify-css/browser.js b/chatto/src/main/javascript/node_modules/browserify-css/browser.js
new file mode 100644
index 0000000..7c3e356
--- /dev/null
+++ b/chatto/src/main/javascript/node_modules/browserify-css/browser.js
@@ -0,0 +1,75 @@
+'use strict';
+// For more information about browser field, check out the browser field at https://github.com/substack/browserify-handbook#browser-field.
+
+var styleElementsInsertedAtTop = [];
+
+var insertStyleElement = function(styleElement, options) {
+ var head = document.head || document.getElementsByTagName('head')[0];
+ var lastStyleElementInsertedAtTop = styleElementsInsertedAtTop[styleElementsInsertedAtTop.length - 1];
+
+ options = options || {};
+ options.insertAt = options.insertAt || 'bottom';
+
+ if (options.insertAt === 'top') {
+ if (!lastStyleElementInsertedAtTop) {
+ head.insertBefore(styleElement, head.firstChild);
+ } else if (lastStyleElementInsertedAtTop.nextSibling) {
+ head.insertBefore(styleElement, lastStyleElementInsertedAtTop.nextSibling);
+ } else {
+ head.appendChild(styleElement);
+ }
+ styleElementsInsertedAtTop.push(styleElement);
+ } else if (options.insertAt === 'bottom') {
+ head.appendChild(styleElement);
+ } else {
+ throw new Error('Invalid value for parameter \'insertAt\'. Must be \'top\' or \'bottom\'.');
+ }
+};
+
+module.exports = {
+ // Create a tag with optional data attributes
+ createLink: function(href, attributes) {
+ var head = document.head || document.getElementsByTagName('head')[0];
+ var link = document.createElement('link');
+
+ link.href = href;
+ link.rel = 'stylesheet';
+
+ for (var key in attributes) {
+ if ( ! attributes.hasOwnProperty(key)) {
+ continue;
+ }
+ var value = attributes[key];
+ link.setAttribute('data-' + key, value);
+ }
+
+ head.appendChild(link);
+ },
+ // Create a