Base for multi-host support
Must do further testing; it's too late for me to continue now.
This commit is contained in:
		
							parent
							
								
									482d352f30
								
							
						
					
					
						commit
						f90869e370
					
				
					 9 changed files with 372 additions and 127 deletions
				
			
		| 
						 | 
				
			
			@ -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);}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -15,10 +15,11 @@
 | 
			
		|||
	</ul>
 | 
			
		||||
 | 
			
		||||
	<!-- Message input -->
 | 
			
		||||
	<div id="input_wrapper">
 | 
			
		||||
	<div id="input_wrapper" class="loading">
 | 
			
		||||
		<div id="loader"><span>|</span></div>
 | 
			
		||||
		<input type="text" id="input" />
 | 
			
		||||
	</div>
 | 
			
		||||
	
 | 
			
		||||
 | 
			
		||||
	<!-- Only include the script needed for loading the app -->
 | 
			
		||||
	<script src="js/vendor/fandango.v20140921.min.js"></script>
 | 
			
		||||
	<script src="js/bootstrap.js"></script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										3
									
								
								public/js/bootstrap.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								public/js/bootstrap.js
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -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'
 | 
			
		||||
	},
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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]);
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
							
								
								
									
										15
									
								
								public/js/cryptalk_modules/hosts.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								public/js/cryptalk_modules/hosts.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -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'
 | 
			
		||||
		}
 | 
			
		||||
	]
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -1,7 +1,4 @@
 | 
			
		|||
define({
 | 
			
		||||
	// If no host is given it will default to localhost.
 | 
			
		||||
	host: '',
 | 
			
		||||
 | 
			
		||||
	nick_maxLen: 20,
 | 
			
		||||
	nick_minLen: 3,
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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.<br>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: 		'<span class="info">{index}</span>	<span class="good">[AVAILABLE]</span>	<span class="neutral">{name}</span>\n',
 | 
			
		||||
		host_unavailable: 		'<span class="info">{index}</span>	<span class="bad">[UNAVAILABLE]</span>	<span class="neutral">{name}</span>\n'
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	server: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										30
									
								
								public/js/vendor/fandango.v20140921.min.js
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										30
									
								
								public/js/vendor/fandango.v20140921.min.js
									
										
									
									
										vendored
									
									
								
							| 
						 | 
				
			
			@ -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<b&&!h.is(a,"primitive")&&b-1 in a)},"boolean":function(a){return!0===a||!1===a},element:function(a){return!(!a||1!==a.nodeType)},finite:function(a){return isFinite(a)&&!isNaN(parseFloat(a))},integer:Number.isInteger||function(a){return"number"===typeof a&&e.finite(a)&&-9007199254740992<a&&9007199254740992>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;d<a&&(void 0===c[d]||!e.call(g,c[d],d,c));d++);return c}for(d in c)if(c.hasOwnProperty(d)&&e.call(g,c[d],d,c))break;return c}}();h.merge=function(f){var c,e,g,d,a;c=arguments.length;var b=!1;f=arguments[0];var m=1,p,k,l=0;if(!0===
 | 
			
		||||
f||!1===f)b=f,f=arguments[m],m++;h.is(arguments[c-1],"function")&&(a=arguments[c-1],c--);if(void 0===f)f={};else if(h.is(f,"primitive"))throw new TypeError("Cannot merge primitive data types: merge("+Array.prototype.join.call(arguments,", ")+")");k=h.is(f,"array");if(m<c)for(;d=arguments[m++];){k=k&&h.is(d,"array");for(g in d)e=f[g],c=d[g],void 0===c||c===f?k&&l--:(b&&c&&((p=h.is(c,"array"))||h.is(c,"untyped"))&&(c=h.merge(b,e&&(p?h.is(e,"array")?e:[]:h.is(e,"untyped")?e:{}),c,a)),a&&a(g,f,d)||(l&&
 | 
			
		||||
(g=+g+l),f[g]=c));l=0}return f};h.subordinate=function(){function f(a){if(!a||a.source===window&&a.data===g){var c=d.shift();a=c[0];var c=c[1],f=c.length;f?1===f?a(c[0]):2===f?a(c[0],c[1]):3===f?a(c[0],c[1],c[2]):4===f?a(c[0],c[1],c[2],c[3]):a.apply(0,c):a()}}var c=Array.prototype.slice,e=window.postMessage,g="__fandango@"+Math.random().toString(36),d=[],a=function(){return e?function(){e(g,"*")}:function(){setTimeout(f)}}();e&&window.addEventListener("message",f,!1);return function(b){a(d.push([arguments[arguments.length-
 | 
			
		||||
1],c.call(arguments).slice(0,-1)])-1)}}();var G=function(){var f={},c=[].slice,e=function(){return[]};return function(g,d){function a(a,c){return new b(a,c)}function b(a,b){var c=0,d=m(a,b),f=d.length;this.context=b||document;this.selector=a;for(this.length=f;c<f;c++)this[c]=d[c];return this}var m,p,k,h;if(f[g])return f[g];for(h=d.length;p=d[--h];){p.selector&&(m=p.selector);if(source=p.prototype)if("function"===typeof source)source.call(b.prototype);else if(b.prototype.prototype)for(k in source)source.hasOwnProperty(k)&&
 | 
			
		||||
(b.prototype[k]=source[k]);else b.prototype=source;if(source=p.utilities)if("function"===typeof source)source.utilities.call(a);else for(k in source)source.hasOwnProperty(k)&&(a[k]=source[k])}b.prototype.length=0;b.prototype.selector="";b.prototype.splice=c;b.prototype.fandango=1;m||(m=e);return f[g]=a}}(),E=function(){function f(c,f,g,d){var a,b=l[c],m=h.is(b.factory,"function"),p=m?b.factory.length:0,k=b.amdStyle,t,s;d&&(a=G(b.compiles+"",d));for(s in b.instances)d=b.instances[s],d.context=void 0===
 | 
			
		||||
d.context?b.context:d.context,d.deepCopy=void 0===d.deepCopy?b.deepCopy:d.deepCopy,d.namespace=void 0===d.namespace?b.namespace:d.namespace,t=[],k?t=f||[]:(a&&t.push(a),f&&t.push(f),p>t.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(0<a.length){h.subordinate(a,function(){e(g,!0)},x);return}}void 0===(a=b.deepCopy)&&
 | 
			
		||||
(a=v.deepCopy);b=l[g]=h.merge(a,{},v,b);if(m&&b.requires)for(s=b.requires&&(p?[]:{}),q=0;n=b.requires[q++];)l[n]?p?s.push(l[n].exports):h.is(l[n].exports,"function")?s[n]=l[n].exports:h.merge(a,s[n]={},l[n].exports):k.push(n);if(b.inherits)for(r=b.inherits&&{},q=0;n=b.inherits[q++];)l[n]?h.merge(a,r,l[n].exports):k.push(n);if(m&&b.compiles)for(t=b.compiles&&[],q=0;n=b.compiles[q++];)l[n]?t.push(l[n].exports):k.push(n);k.length?x(k,function(){},onError):f(g,s,r,t)}}();y.amd={};var x=function(){var f=
 | 
			
		||||
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;d<a&&(void 0===c[d]||!e.call(g,c[d],d,c));d++);return c}for(d in c)if(c.hasOwnProperty(d)&&e.call(g,c[d],d,c))break;return c}}();h.merge=function(f){var c,e,g,d,a,b=arguments.length,n=!1;f=arguments[0];var m=1,k,l,q=0;if(!0===
 | 
			
		||||
f||!1===f)n=f,f=arguments[m],m++;h.is(arguments[b-1],"function")&&(a=arguments[b-1],b--);if(void 0===f)f={};else if(h.is(f,"primitive"))throw new TypeError("Cannot merge primitive data types: merge("+Array.prototype.join.call(arguments,", ")+")");l=h.is(f,"array");if(m<b)for(;m<=b;)if(d=arguments[m++]){l=l&&h.is(d,"array");for(g in d)e=f[g],c=d[g],void 0===c||c===f?l&&q--:(n&&c&&((k=h.is(c,"array"))||h.is(c,"untyped"))&&(c=h.merge(n,e||(k?h.is(e,"array")?e:[]:h.is(e,"untyped")?e:{}),c,a)),a&&a(g,
 | 
			
		||||
f,d)||(q&&(g=+g+q),f[g]=c));q=0}return f};h.subordinate=function(){function f(a){if(!a||a.source===window&&a.data===g){var c=d.shift();a=c[0];var c=c[1],f=c.length;f?1===f?a(c[0]):2===f?a(c[0],c[1]):3===f?a(c[0],c[1],c[2]):4===f?a(c[0],c[1],c[2],c[3]):a.apply(0,c):a()}}var c=Array.prototype.slice,e=window.postMessage,g="__fandango@"+Math.random().toString(36),d=[],a=function(){return e?function(){e(g,"*")}:function(){setTimeout(f)}}();e&&window.addEventListener("message",f,!1);return function(b){a(d.push([arguments[arguments.length-
 | 
			
		||||
1],c.call(arguments).slice(0,-1)])-1)}}();var G=function(){var f={},c=[].slice,e=function(){return[]};return function(g,d){function a(a,c){return new b(a,c)}function b(a,b){var c=0,d=n(a,b),f=d.length;this.context=b||document;this.selector=a;for(this.length=f;c<f;c++)this[c]=d[c];return this}var n,m,k,h;if(f[g])return f[g];for(h=d.length;m=d[--h];){m.selector&&(n=m.selector);if(source=m.prototype)if("function"===typeof source)source.call(b.prototype);else if(b.prototype.prototype)for(k in source)source.hasOwnProperty(k)&&
 | 
			
		||||
(b.prototype[k]=source[k]);else b.prototype=source;if(source=m.utilities)if("function"===typeof source)source.utilities.call(a);else for(k in source)source.hasOwnProperty(k)&&(a[k]=source[k])}b.prototype.length=0;b.prototype.selector="";b.prototype.splice=c;b.prototype.fandango=1;n||(n=e);return f[g]=a}}(),E=function(){function f(c,f,g,d){var a,b=l[c],n=h.is(b.factory,"function"),m=n?b.factory.length:0,k=b.amdStyle,t,q;d&&(a=G(b.compiles+"",d));for(q in b.instances)d=b.instances[q],d.context=void 0===
 | 
			
		||||
d.context?b.context:d.context,d.deepCopy=void 0===d.deepCopy?b.deepCopy:d.deepCopy,d.namespace=void 0===d.namespace?b.namespace:d.namespace,t=[],k?t=f||[]:(a&&t.push(a),f&&t.push(f),m>t.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<a.length){h.subordinate(a,function(){e(g,!0)},x);return}}void 0===(a=b.deepCopy)&&
 | 
			
		||||
(a=v.deepCopy);b=l[g]=h.merge(a,{},v,b);if(n&&b.requires)for(q=b.requires&&(m?[]:{}),r=0;p=b.requires[r++];)l[p]?m?q.push(l[p].exports):h.is(l[p].exports,"function")?q[p]=l[p].exports:h.merge(a,q[p]={},l[p].exports):k.push(p);if(b.inherits)for(s=b.inherits&&{},r=0;p=b.inherits[r++];)l[p]?h.merge(a,s,l[p].exports):k.push(p);if(n&&b.compiles)for(t=b.compiles&&[],r=0;p=b.compiles[r++];)l[p]?t.push(l[p].exports):k.push(p);k.length?x(k,function(){},onError):f(g,q,s,t)}}();y.amd={};var x=function(){var f=
 | 
			
		||||
{},c=document.getElementsByTagName("head")[0],e="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),g=function(a){var b=document.createElement("script");b.setAttribute("id",a);b.type="text/javascript";b.charset="utf-8";b.async=!0;return b},d=!(!((d=g())&&d.attachEvent&&d.attachEvent.toString&&0<d.attachEvent.toString().indexOf("[native code"))||e);buildUrl=function(){var a={"http://":1,"https://":1,"file://":1};return function(b){var c=b.toLowerCase(),d=c[0];"/"===d||"\\"===d||"h"===d&&
 | 
			
		||||
(a[c.slice(0,7)]||a[c.slice(0,8)])||"f"===d&&a[c.slice(0,7)]||(b=v.baseUrl+b);".js"!==b.slice(-3)&&(b+=".js");return b}}();onErrorEvent=function(a,b,c,d){return function(e){f[b]=0;a.error=e||!0;d.parentElement.removeChild(d);if(a.onError)a.onError(e,c,b)}};onLoadEvent=function(a,b,c,d,e){return function(){if(2===f[b]){f[b]=3;if(a.onPartial)a.onPartial(b,c,d);e?d.onreadystatechange=null:(d.removeEventListener("load",a.loadListener),d.removeEventListener("error",a.errorListener));if(a.loaded.push(b)===
 | 
			
		||||
a.paths.length&&a.onDone)a.onDone(a.load)}}};get=function(a){for(var b=0,e,h,k;!a.error&&(e=a.paths[b++]);)h=a.names[b-1],f[e]=2,k=g(e),d?k.attachEvent("onreadystatechange",onLoadEvent(a,e,h,k,!0)):(a.loadListener=onLoadEvent(a,e,h,k),a.errorListener=onErrorEvent(a,e,h,k),k.addEventListener("load",a.loadListener,!1),k.addEventListener("error",a.errorListener,!1)),k.src=e,c.appendChild(k)};return function(a,b,c,d){var e=[],g=[],s={paths:[],names:[],loaded:[],error:null,loadListener:null,errorListener:null},
 | 
			
		||||
u,q,n,w,x;for(x=0;q=a[x++];)if(l[q])if(3===l[q].state)g.push(l[q].exports);else{if(2===l[q].state)throw Error("require(): Rejected dependency. Cannot continue.");E(q);e.push(q)}else f[u]||(u=buildUrl(v.paths[q]||q),s.paths.push(u),s.names.push(q)),e.push(q);b&&!e.length?b.apply(null,g):(s.paths&&(s.onError=function(a,b,d){a instanceof Event&&(a=Error('Could require "'+b+'"; an error occurred while trying to load "'+d+'".'));if(c)c(a);else throw a;},s.onPartial=function(a,b,c){l[b]||((n=v.shim[b])?
 | 
			
		||||
(w=h.is(n.exports,"function")?n.exports:function(){return r[n.exports]},y(b,w)):y(C,b));E(b);d&&d(a,c)},get(s)),F(e,function(a){b.apply(null,a)},s.onError))}}();y("fandango",h);var H=r.fandango,I=r.define,J=r.require,w={fn:h,define:y,require:x,noConflict:function(f){r.define===y&&(r.define=I);r.require===x&&(r.require=J);f&&r.fandango===w&&(r.fandango=H);return w},defaults:function(f){return f?h.merge(v,f):v}};r.define||(r.define=w.define);r.require||(r.require=w.require);r.fandango=w})(this);
 | 
			
		||||
a.paths.length&&a.onDone)a.onDone(a.load)}}};get=function(a){for(var b=0,e,h,k;!a.error&&(e=a.paths[b++]);)h=a.names[b-1],f[e]=2,k=g(e),d?k.attachEvent("onreadystatechange",onLoadEvent(a,e,h,k,!0)):(a.loadListener=onLoadEvent(a,e,h,k),a.errorListener=onErrorEvent(a,e,h,k),k.addEventListener("load",a.loadListener,!1),k.addEventListener("error",a.errorListener,!1)),k.src=e,c.appendChild(k)};return function(a,b,c,d){var e=[],g=[],q={paths:[],names:[],loaded:[],error:null,loadListener:null,errorListener:null},
 | 
			
		||||
u,r,p,w,x;for(x=0;r=a[x++];)if(l[r])if(3===l[r].state)g.push(l[r].exports);else{if(2===l[r].state)throw Error("require(): Rejected dependency. Cannot continue.");E(r);e.push(r)}else f[u]||(u=buildUrl(v.paths[r]||r),q.paths.push(u),q.names.push(r)),e.push(r);b&&!e.length?b.apply(null,g):(q.paths&&(q.onError=function(a,b,d){a instanceof Event&&(a=Error('Could require "'+b+'"; an error occurred while trying to load "'+d+'".'));if(c)c(a);else throw a;},q.onPartial=function(a,b,c){l[b]||((p=v.shim[b])?
 | 
			
		||||
(w=h.is(p.exports,"function")?p.exports:function(){return s[p.exports]},y(b,w)):y(C,b));E(b);d&&d(a,c)},get(q)),F(e,function(a){b.apply(null,a)},q.onError))}}();y("fandango",h);var H=s.fandango,I=s.define,J=s.require,w={fn:h,define:y,require:x,noConflict:function(f){s.define===y&&(s.define=I);s.require===x&&(s.require=J);f&&s.fandango===w&&(s.fandango=H);return w},defaults:function(f){return f?h.merge(v,f):v}};s.define||(s.define=w.define);s.require||(s.require=w.require);s.fandango=w})(this);
 | 
			
		||||
		Loading…
	
	Add table
		
		Reference in a new issue