diff --git a/public/css/default.css b/public/css/default.css
index cd90c6d..b105b58 100644
--- a/public/css/default.css
+++ b/public/css/default.css
@@ -24,6 +24,12 @@ body, html {
color: #00DD00;
}
+.good { color: #99FF99; }
+.bad { color: #ff7777; }
+.info { color:#99FFFF; }
+.neutral { color: #eeeeee; }
+
+
/*------------------------------------*\
CHAT
\*------------------------------------*/
@@ -58,7 +64,7 @@ body, html {
#chat i.fatal { color: #ff7777; }
/*------------------------------------*\
- INPUT
+ INPUT & LOADER
\*------------------------------------*/
#input_wrapper {
right:0;
@@ -69,7 +75,8 @@ body, html {
height:30px;
}
-#input {
+#input,
+#loader {
top: 0;
bottom: 0;
width: 100%;
@@ -78,9 +85,28 @@ body, html {
border: 0;
outline: 0;
+
+
padding: 5px 5px 5px 15px;
color: #FFFFFF;
background-color:#141414;
height:30px;
+}
+
+#input { z-index: 1; }
+#loader { z-index: 0; line-height: 20px; font-size: 14px; font-weight: 100; font-family: tahoma;}
+
+/*------------------------------------*\
+ SPINNER
+\*------------------------------------*/
+.loading #loader { z-index: 2; }
+.loading #loader span {
+ margin-left:-2px;
+ -webkit-animation: rotation 1s infinite linear;
+}
+
+@-webkit-keyframes rotation {
+ from {-webkit-transform: rotate(0deg);}
+ to {-webkit-transform: rotate(359deg);}
}
\ No newline at end of file
diff --git a/public/index.html b/public/index.html
index 79e1424..91b49d9 100644
--- a/public/index.html
+++ b/public/index.html
@@ -15,10 +15,11 @@
-
+
-
+
diff --git a/public/js/bootstrap.js b/public/js/bootstrap.js
index d0e23d7..577db78 100644
--- a/public/js/bootstrap.js
+++ b/public/js/bootstrap.js
@@ -5,6 +5,9 @@ fandango.defaults({
baseUrl: 'js/cryptalk_modules/',
paths: {
websocket: 'https://cdnjs.cloudflare.com/ajax/libs/socket.io/0.9.16/socket.io.min.js',
+ // Newer version:
+ // We'll have to fix the Access Control issue first though (https://github.com/Automattic/socket.io-client/issues/641).
+ // websocket: 'https://cdn.socket.io/socket.io-1.1.0.js',
aes: 'https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js',
domReady: 'https://cdnjs.cloudflare.com/ajax/libs/require-domReady/2.0.1/domReady.min.js'
},
diff --git a/public/js/cryptalk_modules/$.js b/public/js/cryptalk_modules/$.js
index 5ec45a5..22f071b 100644
--- a/public/js/cryptalk_modules/$.js
+++ b/public/js/cryptalk_modules/$.js
@@ -9,7 +9,16 @@ define(['fandango', 'websocket', 'aes'], function (fandango, websocket, aes) {
utils = exports.utilities,
proto = exports.prototype,
- each = fandango.each;
+ each = fandango.each,
+
+ /**
+ * Regex for matching NaN.
+ *
+ * @property reNaN
+ * @type {Regex}
+ * @private
+ */
+ reDigits = /^\d+$/;
// The DOM selector engine
exports.selector = function (selector) {
@@ -61,6 +70,20 @@ define(['fandango', 'websocket', 'aes'], function (fandango, websocket, aes) {
try { return document.activeElement; } catch (e) {}
}
+ /**
+ * Removes all characters but 0 - 9 from given string.
+ *
+ * @method digits
+ * @param {String} str The string to sanitize
+ * @return {String} The sanitized string
+ * @example
+ * $.digits('foo8bar'); // `8`
+ * $.digits('->#5*duckM4N!!!111'); // `54111`
+ */
+ utils.isDigits = function(value) {
+ return reDigits.test(value);
+ };
+
/**
* A very simple templating function.
* @param {} str [description]
@@ -73,6 +96,28 @@ define(['fandango', 'websocket', 'aes'], function (fandango, websocket, aes) {
});
};
+ utils.getJSON = function (path, onSuccess, onError) {
+ var data, request = new XMLHttpRequest();
+ request.open('GET', path, true);
+
+ request.onreadystatechange = function() {
+ if (this.readyState === 4) {
+ if (this.status >= 200 && this.status < 400) {
+ try {
+ onSuccess && onSuccess(JSON.parse(this.responseText));
+ } catch (e) {
+ onError && onError();
+ }
+ } else {
+ onError && onError();
+ }
+ }
+ };
+
+ request.send();
+ request = null;
+ };
+
// Part of this is originating from mustasche.js
// Code: https://github.com/janl/mustache.js/blob/master/mustache.js#L43
// License: https://github.com/janl/mustache.js/blob/master/LICENSE
diff --git a/public/js/cryptalk_modules/cryptalk.js b/public/js/cryptalk_modules/cryptalk.js
index d444563..621a012 100644
--- a/public/js/cryptalk_modules/cryptalk.js
+++ b/public/js/cryptalk_modules/cryptalk.js
@@ -1,7 +1,7 @@
// Main cryptalk module
define({
compiles: ['$'],
- requires: ['settings', 'templates', 'sound', 'fandango']
+ requires: ['hosts', 'templates', 'sound', 'fandango']
}, function ($, requires, data) {
var socket,
key,
@@ -9,8 +9,9 @@ define({
room,
hash,
nick,
+ mute,
- mute = false,
+ settings = {},
history = [],
history_pos = -1,
@@ -20,15 +21,27 @@ define({
// Collection of DOM components
components = {
chat: $('#chat'),
- input: $('#input')
+ input: $('#input'),
+ inputWrapper: $('#input_wrapper')
},
// Shortcut
- settings = requires.settings;
+ hosts = requires.hosts.hosts;
fandango = requires.fandango;
templates = requires.templates;
sound = requires.sound;
+ lockInput = function () {
+ components.input[0].setAttribute('disabled', 'disabled');
+ components.inputWrapper[0].className = 'loading';
+ },
+
+ unlockInput = function () {
+ components.input[0].removeAttribute('disabled');
+ components.inputWrapper[0].className = '';
+ components.input.focus();
+ },
+
// Adds a new message to the DOM
post = function (type, text, clearChat, clearBuffer, nick) {
var tpl = templates.post[type],
@@ -53,8 +66,205 @@ define({
// Chat related commands
commands = {
- help: function () {
+ help: function (payload, done) {
post('motd', templates.help);
+ done();
+ },
+
+ hosts: function (force, done) {
+ var i = 0,
+ left = hosts.length,
+ host,
+ strhosts = '\n',
+ callback = function (host, index, isUp) {
+ return function (hostSettings) {
+ host.settings = (isUp ? hostSettings : 0);
+
+ strhosts += $.template(templates.messages[(isUp ? 'host_available' : 'host_unavailable')], {
+ name: host.name,
+ path: host.path,
+ index: index
+ });
+
+ if (!--left) {
+ post('info', strhosts);
+ done();
+ }
+ }
+ };
+
+ //
+ force = (force && force.toLowerCase() === 'force');
+
+ // Loop through all the hosts
+ while (host = hosts[i]) {
+ if (!force && host.settings !== undefined) {
+ if (host.settings) {
+ callback(host, i, 1)();
+ } else {
+ callback(host, i, 0)();
+ }
+ } else {
+ require([host.path], callback(host, i, 1), callback(host, i, 0));
+ }
+
+ i++;
+ }
+ },
+
+ connect: function (toHost, done) {
+ var request;
+
+ if (host && host.connected) {
+ done();
+ return post('error', $.template(templates.messages.already_connected, {
+ host: host.name || 'localhost'
+ }));
+ }
+
+ if ($.isDigits(toHost)) {
+ if (host = hosts[+toHost]) {
+ if (host.settings) {
+ settings = host.settings;
+ } else {
+ request = host.path;
+ }
+ } else {
+ return post('error', 'Undefined host index: ' + toHost);
+ }
+ } else if (fandango.is(toHost, 'untyped')) {
+ settings = toHost.settings;
+ } else { // Assume string
+ request = toHost;
+ }
+
+ if (request) {
+ return require([request], function (settings) {
+ host.settings = settings;
+ commands.connect(toHost, done);
+ }, function () {
+ return post('error', 'Could not fetch host settings: ' + request);
+ });
+ }
+
+ // Push 'Connecting...' message
+ post('info', $.template(templates.messages.connecting, {
+ host: host.name || 'localhost'
+ }));
+
+ // The one and only socket
+ socket = $.Websocket.connect(host.host, {
+ forceNew: true,
+ 'force new connection': true
+ });
+
+ // Bind socket events
+ socket
+ .on('room:generated', function (data) {
+ var sanitized = $.escapeHtml(data);
+ post('server', $.template(templates.server.room_generated, { payload: sanitized }));
+ socket.emit('room:join', sanitized);
+ })
+
+ .on('room:joined', function (data) {
+ room = data;
+ post('info', $.template(templates.messages.joined_room, { roomName: room }));
+
+ // Automatically count persons on join
+ socket.emit('room:count');
+ })
+
+ .on('room:left', function () {
+ post('info', $.template(templates.messages.left_room, { roomName: room }));
+
+ // Clear history on leaving room
+ clearHistory();
+
+ room = false;
+ })
+
+ .on('message:send', function (data) {
+ var decrypted = $.AES.decrypt(data.msg, room + key),
+ sanitized = $.escapeHtml(decrypted),
+ nick = (data.nick == undefined || !data.nick ) ? templates.default_nick : $.escapeHtml($.AES.decrypt(data.nick, room + key));
+
+ if (!decrypted) {
+ post('error', templates.messages.unable_to_decrypt);
+ } else {
+ post('message', sanitized, false, false, nick);
+ if( !mute ) sound.playTones(sound.messages.message);
+ }
+ })
+
+ .on('message:server', function (data) {
+ if( data.msg ) {
+ var sanitized = $.escapeHtml(data.msg);
+ if( templates.server[sanitized] ) {
+ if( data.payload !== undefined ) {
+ var sanitized_payload = $.escapeHtml(data.payload);
+ post('server', $.template(templates.server[sanitized], { payload: sanitized_payload }));
+ } else {
+ post('server', templates.server[sanitized]);
+ }
+
+ // Play sound
+ if (sound.messages[sanitized] !== undefined && !mute ) sound.playTones(sound.messages[sanitized]);
+
+ } else {
+ post('error', templates.server.bogus);
+ }
+ } else {
+ post('error', templates.server.bogus);
+ }
+ })
+
+ .on('connect', function () {
+ // Tell the user that the chat is ready to interact with
+ post('info', $.template(templates.messages.connected, {
+ host: host.name || 'localhost'
+ }));
+
+ host.connected = 1;
+
+ done();
+ })
+
+ .on('disconnect', function () {
+ room = 0;
+ key = 0;
+ host.connected = 0;
+
+ // Tell the user that the chat is ready to interact with
+ post('info', $.template(templates.messages.disconnected, {
+ host: host.name || 'localhost'
+ }));
+ })
+
+ .on('error', function () {
+ room = 0;
+ key = 0;
+ host.connected = 0;
+ post('error', templates.messages.socket_error);
+ done();
+ });
+ },
+
+ reconnect: function (foo, done) {
+ if (host) {
+ if (host.connected) {
+ commands.disconnect()
+ commands.connect(host, done);
+ } else {
+ commands.connect(host, done);
+ }
+ } else {
+ done();
+ return post('error', templates.messages.reconnect_no_host);
+ }
+ },
+
+ disconnect: function () {
+ socket.disconnect();
},
clear: function () {
@@ -81,6 +291,10 @@ define({
},
key: function (payload) {
+ if (!host) {
+ return post('error', templates.messages.key_no_host);
+ }
+
// Make sure the key meets the length requirements
if (payload.length > settings.key_maxLen) {
return post('error', templates.messages.key_to_long);
@@ -119,6 +333,10 @@ define({
},
join: function (payload) {
+ if (!host) {
+ return post('error', templates.messages.join_no_host);
+ }
+
return (
room
? post('error', templates.messages.already_in_room)
@@ -169,7 +387,7 @@ define({
// The Document object is bound to this element.
// If the active element is not the input, focus on it and exit the function.
// Ignore this when ctrl and/or alt is pressed!
- if (components.input[0] !== $.activeElement() && !e.ctrlKey && !e.altKey) {
+ if (!e.ctrlKey && !e.altKey && components.input[0] !== $.activeElement()) {
return components.input.focus();
}
@@ -178,7 +396,7 @@ define({
history_timer = setTimeout(clearHistory, 60000);
// Check for escape key, this does nothing but clear the input buffer and reset history position
- if ( e.keyCode == 27 ) {
+ if (e.keyCode == 27) {
history_pos = -1;
clearInput();
@@ -186,7 +404,7 @@ define({
}
// Check for up or down-keys, they handle the history position
- if( e.keyCode == 38 || e.keyCode == 40) {
+ if (e.keyCode == 38 || e.keyCode == 40) {
if (e.keyCode == 38 ) { history_pos = (history_pos > history.length - 2) ? -1 : history_pos = history_pos + 1; }
else { history_pos = (history_pos <= 0) ? -1 : history_pos = history_pos - 1; }
@@ -220,8 +438,18 @@ define({
return post('error', $.template(templates.messages.unrecognized_command, { commandName: command }));
}
- // Execute command handler
- commands[command](payload);
+ // Some commands are asynchrounous;
+ // If the command expects more than one argument, the second argument is a callback that is called when the command is done.
+ if (commands[command].length > 1) {
+ // Lock the input from further interaction
+ lockInput();
+
+ // Execute command handler with callback function.
+ commands[command](payload, unlockInput);
+ } else {
+ // Execute normally.
+ commands[command](payload);
+ }
// Clear input field
clearInput();
@@ -257,103 +485,24 @@ define({
}
};
- host = settings.host;
+ // Bind the necessary DOM events
+ $(document).on('keydown', onKeyDown);
+
+ // Put focus on the message input
+ components.input.focus();
// Post the help/welcome message
post('motd', templates.motd, true);
- // Push 'Connecting...' message
- post('info', $.template(templates.messages.connecting, {
- host: host || 'localhost'
- }));
+ unlockInput();
- // The one and only socket
- socket = $.Websocket.connect(host);
+ // It's possible to provide room and key using the hashtag.
+ // The room and key is then seperated by semicolon (room:key).
+ // If there is no semicolon present, the complete hash will be treated as the room name and the key has to be set manually.
+ if (host && (hash = window.location.hash)) {
+ parts = hash.slice(1).split(':');
- // Bind socket events
- socket
- .on('room:generated', function (data) {
- var sanitized = $.escapeHtml(data);
- post('server', $.template(templates.server.room_generated, { payload: sanitized }));
- socket.emit('room:join', sanitized);
- })
-
- .on('room:joined', function (data) {
- room = data;
- post('info', $.template(templates.messages.joined_room, { roomName: room }));
-
- // Automatically count persons on join
- socket.emit('room:count');
- })
-
- .on('room:left', function () {
- post('info', $.template(templates.messages.left_room, { roomName: room }));
-
- // Clear history on leaving room
- clearHistory();
-
- room = false;
- })
-
- .on('message:send', function (data) {
- var decrypted = $.AES.decrypt(data.msg, room + key),
- sanitized = $.escapeHtml(decrypted),
- nick = (data.nick == undefined || !data.nick ) ? templates.default_nick : $.escapeHtml($.AES.decrypt(data.nick, room + key));
-
- if (!decrypted) {
- post('error', templates.messages.unable_to_decrypt);
- } else {
- post('message', sanitized, false, false, nick);
- if( !mute ) sound.playTones(sound.messages.message);
- }
- })
-
- .on('message:server', function (data) {
- if( data.msg ) {
- var sanitized = $.escapeHtml(data.msg);
- if( templates.server[sanitized] ) {
- if( data.payload !== undefined ) {
- var sanitized_payload = $.escapeHtml(data.payload);
- post('server', $.template(templates.server[sanitized], { payload: sanitized_payload }));
- } else {
- post('server', templates.server[sanitized]);
- }
-
- // Play sound
- if (sound.messages[sanitized] !== undefined && !mute ) sound.playTones(sound.messages[sanitized]);
-
- } else {
- post('error', templates.server.bogus);
- }
- } else {
- post('error', templates.server.bogus);
- }
- })
-
- .on('connect', function () {
- // Bind the necessary DOM events
- $(document).on('keydown', onKeyDown);
-
- // Put focus on the message input
- components.input.focus();
-
- // Tell the user that the chat is ready to interact with
- post('info', $.template(templates.messages.connected, {
- host: host || 'localhost'
- }));
-
- // It's possible to provide room and key using the hashtag.
- // The room and key is then seperated by semicolon (room:key).
- // If there is no semicolon present, the complete hash will be treated as the room name and the key has to be set manually.
- if (hash = window.location.hash) {
- parts = hash.slice(1).split(':');
-
- parts[0] && commands.join(parts[0]);
- parts[1] && commands.key(parts[1]);
- }
- })
-
- .on('error', function () {
- post('error', templates.messages.socket_error);
- });
+ parts[0] && commands.join(parts[0]);
+ parts[1] && commands.key(parts[1]);
+ }
});
\ No newline at end of file
diff --git a/public/js/cryptalk_modules/hosts.js b/public/js/cryptalk_modules/hosts.js
new file mode 100644
index 0000000..ea61b1a
--- /dev/null
+++ b/public/js/cryptalk_modules/hosts.js
@@ -0,0 +1,15 @@
+define({
+ // Used to autoconnect to specific host.
+ // Points to a specific index in the 'hosts' array.
+ // Use -1 to not autoconnect.
+ autoconnect: 0,
+
+ // A collection of hosts to choose from
+ hosts: [
+ {
+ name: 'localhost',
+ host: 'http://localhost:8080',
+ path: 'http://localhost:8080/js/cryptalk_modules/settings.js'
+ }
+ ]
+});
\ No newline at end of file
diff --git a/public/js/cryptalk_modules/settings.js b/public/js/cryptalk_modules/settings.js
index 7510604..a54b96d 100644
--- a/public/js/cryptalk_modules/settings.js
+++ b/public/js/cryptalk_modules/settings.js
@@ -1,7 +1,4 @@
define({
- // If no host is given it will default to localhost.
- host: '',
-
nick_maxLen: 20,
nick_minLen: 3,
diff --git a/public/js/cryptalk_modules/templates.js b/public/js/cryptalk_modules/templates.js
index df50fe5..7e61386 100644
--- a/public/js/cryptalk_modules/templates.js
+++ b/public/js/cryptalk_modules/templates.js
@@ -65,6 +65,9 @@ define({
key_to_long: 'Man that\'s a long key. Make it a tad short, \'kay?',
key_ok_ready: 'Key set, you can now start communicating.',
key_ok_but_no_room: 'Key set, you can now join a room and start communicating.',
+ key_no_host: 'You have to connect to a host before setting the key.',
+
+ join_no_host: 'You have to connect to a host before joining a room.',
nick_to_short: 'Nickname is too short, it has to be at least {nick_minLen} characters long. Try again.',
nick_to_long: 'Nickname is too long, it can be at most {nick_maxLen} characters long. Try again.',
@@ -90,7 +93,13 @@ define({
socket_error: 'A network error has occurred. A restart may be required to bring back full functionality.
Examine the logs for more details.',
connecting: 'Connecting to host {host}...',
- connected: 'A connection to the server has been established. Happy chatting!'
+ connected: 'A connection to the server has been established. Happy chatting!',
+ disconnected: 'Disconnected from host {host}.',
+ already_connected: 'You have to disconnect from {host} before joining another.',
+ reconnect_no_host: 'There is no host to reconnect with.',
+
+ host_available: '
{index} [AVAILABLE] {name}\n',
+ host_unavailable: '
{index} [UNAVAILABLE] {name}\n'
},
server: {
diff --git a/public/js/vendor/fandango.v20140921.min.js b/public/js/vendor/fandango.v20140921.min.js
index d284fa5..50e73c8 100644
--- a/public/js/vendor/fandango.v20140921.min.js
+++ b/public/js/vendor/fandango.v20140921.min.js
@@ -1,20 +1,20 @@
-(function(r){function A(f){this.name="TimeoutError";this.message=f||""}function B(f){this.name="RejectedError";this.message=f||""}function F(f,c,e,g){var d,a,b=[],m,p,k,h=function(a){f.length===b.push(l[a].exports)&&(g&&clearTimeout(m),c(b))};g=g||v.timeout;for(k=0;p=f[k++];)if((d=l[p])&&1!==d.state)if(2===d.state)if(a=new B('Could resolve all dependencies; dependency "'+p+'" has been rejected.'),e)e(a);else throw a;else 3===d.state&&h(p);else(u[p]=u[p]||[]).push(h);g&&(m=setTimeout(function(){for(var c,
-d,m=0,p=0;c=f[m++];)for(p=0;d=b[p++];)c===d&&(b.splice(--p,1),f.splice(--m,1));a=new A("Load timeout of "+g+'ms exceeded for module(s) "'+f.join('", "')+'".');if(e)e(a);else throw a;},g))}function y(){var f=!1,c,e,g,d,a,b,m;if(arguments[0]!==C){for(c=0;(e=arguments[c++])&&(_type=(typeof e)[0]);)if("s"===_type?d?g=1:d=e:"f"===_type?b?m?g=1:m=e:b=e:"o"===_type&&(h.is(e,"array")?(a={requires:e},f=!0):a?b=e:a=e),g)throw new TypeError("define called with unrecognized signature; `"+Array.prototype.join.call(arguments,
-", ")+"`.");a=a||{};d=d||a.UID;b=b||a.factory;m=m||a.onRejected}else if(c=D.pop())d=arguments[1],a=c[0],b=c[1],m=c[2],f=c[3];else throw Error("Inconsistent naming queue");if(!b)if(a)b=a,a={};else throw Error('Missing factory for module "'+d+'"');if(l[d])throw Error('Duplicate entry for module "'+d+'"');d?(c=l[d]=a,c.UID=d,c.amdStyle=f,c.factory=b,c.state=1,c.requires=h.is(c.requires,"array")&&c.requires,c.inherits=h.is(c.inherits,"array")&&c.inherits,c.compiles=h.is(c.compiles,"array")&&c.compiles,
-c.instances||(c.instances={instance1:{}})):D.unshift([a,b,m,f])}var l={},v={deepCopy:!0,baseUrl:"",namespace:"default",timeout:1E3,paths:{},shim:{}},h={},C={a:1},u={},D=[],z=Array.prototype.push;A.prototype=Error.prototype;B.prototype=Error.prototype;h.is=function(){var f=Object.prototype.hasOwnProperty,c=Object.prototype.toString,e={array:Array.isArray||function(a){return"[object Array]"==c.call(a)},arraylike:function(a){if(!a||!a.length&&0!==a.length||e.window(a))return!1;var b=a.length;return 1===
+(function(s){function A(f){this.name="TimeoutError";this.message=f||""}function B(f){this.name="RejectedError";this.message=f||""}function F(f,c,e,g){var d,a,b=[],n,m,k,h=function(a){f.length===b.push(l[a].exports)&&(g&&clearTimeout(n),c(b))};g=g||v.timeout;for(k=0;m=f[k++];)if((d=l[m])&&1!==d.state)if(2===d.state)if(a=new B('Could resolve all dependencies; dependency "'+m+'" has been rejected.'),e)e(a);else throw a;else 3===d.state&&h(m);else(u[m]=u[m]||[]).push(h);g&&(n=setTimeout(function(){for(var d,
+c,n=0,m=0;d=f[n++];)for(m=0;c=b[m++];)d===c&&(b.splice(--m,1),f.splice(--n,1));a=new A("Load timeout of "+g+'ms exceeded for module(s) "'+f.join('", "')+'".');if(e)e(a);else throw a;},g))}function y(){var f=!1,c,e,g,d,a,b,n;if(arguments[0]!==C){for(c=0;(e=arguments[c++])&&(_type=(typeof e)[0]);)if("s"===_type?d?g=1:d=e:"f"===_type?b?n?g=1:n=e:b=e:"o"===_type&&(h.is(e,"array")?(a={requires:e},f=!0):a?b=e:a=e),g)throw new TypeError("define called with unrecognized signature; `"+Array.prototype.join.call(arguments,
+", ")+"`.");a=a||{};d=d||a.UID;b=b||a.factory;n=n||a.onRejected}else if(c=D.pop())d=arguments[1],a=c[0],b=c[1],n=c[2],f=c[3];else throw Error("Inconsistent naming queue");if(!b)if(a)b=a,a={};else throw Error('Missing factory for module "'+d+'"');if(l[d])throw Error('Duplicate entry for module "'+d+'"');d?(c=l[d]=a,c.UID=d,c.amdStyle=f,c.factory=b,c.state=1,c.requires=h.is(c.requires,"array")&&c.requires,c.inherits=h.is(c.inherits,"array")&&c.inherits,c.compiles=h.is(c.compiles,"array")&&c.compiles,
+c.instances||(c.instances={instance1:{}})):D.unshift([a,b,n,f])}var l={},v={deepCopy:!0,baseUrl:"",namespace:"default",timeout:1E3,paths:{},shim:{}},h={},C={a:1},u={},D=[],z=Array.prototype.push;A.prototype=Error.prototype;B.prototype=Error.prototype;h.is=function(){var f=Object.prototype.hasOwnProperty,c=Object.prototype.toString,e={array:Array.isArray||function(a){return"[object Array]"==c.call(a)},arraylike:function(a){if(!a||!a.length&&0!==a.length||e.window(a))return!1;var b=a.length;return 1===
a.nodeType||e.array(a)||!e["function"](a)&&(0===b||"number"===typeof b&&0
a&&Math.floor(a)===a},iterable:function(a){try{1 in obj}catch(b){return!1}return!0},nan:function(a){return e.number(a)&&
a!=+a},number:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},object:function(a){return a===Object(a)},primitive:function(a){return!0===a||!1===a||null==a||!!{string:1,number:1}[typeof a]},string:function(a){return"string"==typeof a||a instanceof String},undefined:function(a){return void 0===a},untyped:function(a){if(!a||a.nodeType||"[object Object]"!==c.call(a)||e.window(a))return!1;try{if(a.constructor&&!f.call(a,"constructor")&&!f.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(b){return!1}for(var d in a);
return void 0===d||f.call(a,d)},window:function(a){return null!=a&&a==a.window},empty:function(a){if(a){if(h.is(a,"array"))return 0===a.length;for(var b in a)if(f.call(a,b))return!1}return!0}},g=0,d=["Arguments","Date","Function","RegExp"];for(;4>g;g++)e[d[g].toLowerCase()]=function(a){a="[object "+a+"]";return function(b){return c.call(b)==a}}(d[g]);e.args=e.arguments;e.bool=e["boolean"];e.plain=e.untyped;return function(a,b){if(e["function"](b))return a===b(a);if(!e.string(b))return a===b;if((b=
-b.toLowerCase())&&e[b])return e[b](a);throw'Unknown type "'+b+'"';}}();h.each=function(){var f=Array.prototype.some;return function(c,e,g){var d,a;if(void 0===c)return obj;g=g||h;if(f&&c.some===f)return c.some(e,g),c;if(h.is(c,"array")||h.is(c,"arraylike")){d=0;for(a=c.length;dt.length&&t.push(d.data?h.merge(d.deepCopy,{},d.data,b.data):b.data),p>t.length&&t.push(h.merge(d.deepCopy,{},d,b))),d.exports=m?b.factory.apply(d.context,t)||{}:b.factory,void 0===b.exports&&(b.exports=g?d.exports?h.merge(!0,{},g,d.exports):g:d.exports);b.state=3;if(u[c])for(;u[c].length;)u[c].pop()(c)}return function e(g,
-d){var a,b=l[g],m=h.is(b.factory,"function")?b.factory.length:0,p=b.amdStyle,k=[],t,s,r;a=[];var q,n;if(!d&&(b.requires||b.compiles||b.inherits)){b.requires&&z.apply(a,b.requires);b.inherits&&z.apply(a,b.inherits);b.compiles&&z.apply(a,b.compiles);for(q=0;n=a[q++];)if(l[n])if(3===l[n].state)a.splice(--q,1);else if(2===l[n].state)throw Error('Could not instantiate "'+UID+'"; dependency "'+dependency+'" has been rejected.');if(0t.length&&t.push(d.data?h.merge(d.deepCopy,{},d.data,b.data):b.data),m>t.length&&t.push(h.merge(d.deepCopy,{},d,b))),d.exports=n?b.factory.apply(d.context,t)||{}:b.factory,void 0===b.exports&&(b.exports=g?d.exports?h.merge(!0,{},g,d.exports):g:d.exports);b.state=3;if(u[c])for(;u[c].length;)u[c].pop()(c)}return function e(g,
+d){var a,b=l[g],n=h.is(b.factory,"function")?b.factory.length:0,m=b.amdStyle,k=[],t,q,s;a=[];var r,p;if(!d&&(b.requires||b.compiles||b.inherits)){b.requires&&z.apply(a,b.requires);b.inherits&&z.apply(a,b.inherits);b.compiles&&z.apply(a,b.compiles);for(r=0;p=a[r++];)if(l[p])if(3===l[p].state)a.splice(--r,1);else if(2===l[p].state)throw Error('Could not instantiate "'+UID+'"; dependency "'+dependency+'" has been rejected.');if(0