<commit_before>/** File: core.js
 * Candy - Chats are not dead yet.
 *
 * Authors:
 *   - Patrick Stadler <patrick.stadler@gmail.com>
 *   - Michael Weibel <michael.weibel@gmail.com>
 *
 * Copyright:
 *   (c) 2011 Amiado Group AG. All rights reserved.
 *   (c) 2012-2014 Patrick Stadler & Michael Weibel. All rights reserved.
 */
'use strict';

/* global Candy, window, Strophe, jQuery */

/** Class: Candy.Core
 * Candy Chat Core
 *
 * Parameters:
 *   (Candy.Core) self - itself
 *   (Strophe) Strophe - Strophe JS
 *   (jQuery) $ - jQuery
 */
Candy.Core = (function(self, Strophe, $) {
		/** PrivateVariable: _connection
		 * Strophe connection
		 */
	var _connection = null,
		/** PrivateVariable: _service
		 * URL of BOSH service
		 */
		_service = null,
		/** PrivateVariable: _user
		 * Current user (me)
		 */
		_user = null,
		/** PrivateVariable: _rooms
		 * Opened rooms, containing instances of Candy.Core.ChatRooms
		 */
		_rooms = {},
		/** PrivateVariable: _anonymousConnection
		 * Set in <Candy.Core.connect> when jidOrHost doesn't contain a @-char.
		 */
		_anonymousConnection = false,
		/** PrivateVariable: _status
		 * Current Strophe connection state
		 */
		_status,
		/** PrivateVariable: _options
		 * Options:
		 *   (Boolean) debug - Debug (Default: false)
		 *   (Array|Boolean) autojoin - Autojoin these channels. When boolean true, do not autojoin, wait if the server sends something.
		 */
		_options = {
			/** Boolean: autojoin
			 * If set to `true` try to get the bookmarks and autojoin the rooms (supported by Openfire).
			 * You may want to define an array of rooms to autojoin: `['room1@conference.host.tld', 'room2...]` (ejabberd, Openfire, ...)
			 */
			autojoin: undefined,
			debug: false,
			disableWindowUnload: false,
			/** Integer: presencePriority
			 * Default priority for presence messages in order to receive messages across different resources
			 */
			presencePriority: 1
		},

		/** PrivateFunction: _addNamespace
		 * Adds a namespace.
		 *
		 * Parameters:
		 *   (String) name - namespace name (will become a constant living in Strophe.NS.*)
		 *   (String) value - XML Namespace
		 */
		_addNamespace = function(name, value) {
			Strophe.addNamespace(name, value);
		},

		/** PrivateFunction: _addNamespaces
		 * Adds namespaces needed by Candy.
		 */
		_addNamespaces = function() {
			_addNamespace('PRIVATE', 'jabber:iq:private');
			_addNamespace('BOOKMARKS', 'storage:bookmarks');
			_addNamespace('PRIVACY', 'jabber:iq:privacy');
			_addNamespace('DELAY', 'jabber:x:delay');
		},

		_getEscapedJidFromJid = function(jid) {
			var node = Strophe.getNodeFromJid(jid),
				domain = Strophe.getDomainFromJid(jid);
			return node ? Strophe.escapeNode(node) + '@' + domain : domain;
		};

	/** Function: init
	 * Initialize Core.
	 *
	 * Parameters:
	 *   (String) service - URL of BOSH/Websocket service
	 *   (Object) options - Options for candy
	 */
	self.init = function(service, options) {
		_service = service;
		// Apply options
		$.extend(true, _options, options);

		// Enable debug logging
		if(_options.debug) {
			self.log = function(str) {
				try { // prevent erroring
					if(typeof window.console !== undefined && typeof window.console.log !== undefined) {
						console.log(str);
					}
				} catch(e) {}
			};
			self.log('[Init] Debugging enabled');
		}

		_addNamespaces();

		// Connect to BOSH/Websocket service
		_connection = new Strophe.Connection(_service);
		_connection.rawInput = self.rawInput.bind(self);
		_connection.rawOutput = self.rawOutput.bind(self);

		// set caps node
		_connection.caps.node = 'https://candy-chat.github.io/candy/';

		// Window unload handler... works on all browsers but Opera. There is NO workaround.
		// Opera clients getting disconnected 1-2 minutes delayed.
		if (!_options.disableWindowUnload) {
			window.onbeforeunload = self.onWindowUnload;
		}
	};

	/** Function: registerEventHandlers
	 * Adds listening handlers to the connection.
	 *
	 * Use with caution from outside of Candy.
	 */
	self.registerEventHandlers = function() {
		self.addHandler(self.Event.Jabber.Version, Strophe.NS.VERSION, 'iq');
		self.addHandler(self.Event.Jabber.Presence, null, 'presence');
		self.addHandler(self.Event.Jabber.Message, null, 'message');
		self.addHandler(self.Event.Jabber.Bookmarks, Strophe.NS.PRIVATE, 'iq');
		self.addHandler(self.Event.Jabber.Room.Disco, Strophe.NS.DISCO_INFO, 'iq', 'result');

		self.addHandler(_connection.disco._onDiscoInfo.bind(_connection.disco), Strophe.NS.DISCO_INFO, 'iq', 'get');
		self.addHandler(_connection.disco._onDiscoItems.bind(_connection.disco), Strophe.NS.DISCO_ITEMS, 'iq', 'get');
		self.addHandler(_connection.caps._delegateCapabilities.bind(_connection.caps), Strophe.NS.CAPS);
	};

	/** Function: connect
	 * Connect to the jabber host.
	 *
	 * There are four different procedures to login:
	 *   connect('JID', 'password') - Connect a registered user
	 *   connect('domain') - Connect anonymously to the domain. The user should receive a random JID.
	 *   connect('domain', null, 'nick') - Connect anonymously to the domain. The user should receive a random JID but with a nick set.
	 *   connect('JID') - Show login form and prompt for password. JID input is hidden.
	 *   connect() - Show login form and prompt for JID and password.
	 *
	 * See:
	 *   <Candy.Core.attach()> for attaching an already established session.
	 *
	 * Parameters:
	 *   (String) jidOrHost - JID or Host
	 *   (String) password  - Password of the user
	 *   (String) nick      - Nick of the user. Set one if you want to anonymously connect but preset a nick. If jidOrHost is a domain
	 *                        and this param is not set, Candy will prompt for a nick.
	 */
	self.connect = function(jidOrHost, password, nick) {
		// Reset before every connection attempt to make sure reconnections work after authfail, alltabsclosed, ...
		_connection.reset();
		self.registerEventHandlers();
		/** Event: candy:core.before-connect
		 * Triggered before a connection attempt is made.
		 *
		 * Plugins should register their stanza handlers using this event
		 * to ensure that they are set.
		 *
		 * See also <#84 at https://github.com/candy-chat/candy/issues/84>.
		 *
		 * Parameters:
		 *   (Strophe.Connection) conncetion - Strophe connection
		 */
		$(Candy).triggerHandler('candy:core.before-connect', {
			connection: _connection
		});

		_anonymousConnection = !_anonymousConnection ? jidOrHost && jidOrHost.indexOf("@") < 0 : true;

		if(jidOrHost && password) {
			// authentication
			_connection.connect(_getEscapedJidFromJid(jidOrHost) + '/' + Candy.about.name, password, Candy.Core.Event.Strophe.Connect);
			if (nick) {
				_user = new self.ChatUser(jidOrHost, nick);
			} else {
				_user = new self.ChatUser(jidOrHost, Strophe.getNodeFromJid(jidOrHost));
			}
		} else if(jidOrHost && nick) {
			// anonymous connect
			_connection.connect(_getEscapedJidFromJid(jidOrHost) + '/' + Candy.about.name, null, Candy.Core.Event.Strophe.Connect);
			_user = new self.ChatUser(null, nick); // set jid to null because we'll later receive it
		} else if(jidOrHost) {
			Candy.Core.Event.Login(jidOrHost);
		} else {
			// display login modal
			Candy.Core.Event.Login();
		}
	};

	/** Function: attach
	 * Attach an already binded & connected session to the server
	 *
	 * _See_ Strophe.Connection.attach
	 *
	 * Parameters:
	 *   (String) jid - Jabber ID
	 *   (Integer) sid - Session ID
	 *   (Integer) rid - rid
	 */
	self.attach = function(jid, sid, rid) {
		_user = new self.ChatUser(jid, Strophe.getNodeFromJid(jid));
		self.registerEventHandlers();
		_connection.attach(jid, sid, rid, Candy.Core.Event.Strophe.Connect);
	};

	/** Function: disconnect
	 * Leave all rooms and disconnect
	 */
	self.disconnect = function() {
		if(_connection.connected) {
			$.each(self.getRooms(), function() {
				Candy.Core.Action.Jabber.Room.Leave(this.getJid());
			});
			_connection.disconnect();
		}
	};

	/** Function: addHandler
	 * Wrapper for Strophe.Connection.addHandler() to add a stanza handler for the connection.
	 *
	 * Parameters:
	 *   (Function) handler - The user callback.
	 *   (String) ns - The namespace to match.
	 *   (String) name - The stanza name to match.
	 *   (String) type - The stanza type attribute to match.
	 *   (String) id - The stanza id attribute to match.
	 *   (String) from - The stanza from attribute to match.
	 *   (String) options - The handler options
	 *
	 * Returns:
	 *   A reference to the handler that can be used to remove it.
	 */
	self.addHandler = function(handler, ns, name, type, id, from, options) {
		return _connection.addHandler(handler, ns, name, type, id, from, options);
	};

	/** Function: getUser
	 * Gets current user
	 *
	 * Returns:
	 *   Instance of Candy.Core.ChatUser
	 */
	self.getUser = function() {
		return _user;
	};

	/** Function: setUser
	 * Set current user. Needed when anonymous login is used, as jid gets retrieved later.
	 *
	 * Parameters:
	 *   (Candy.Core.ChatUser) user - User instance
	 */
	self.setUser = function(user) {
		_user = user;
	};

	/** Function: getConnection
	 * Gets Strophe connection
	 *
	 * Returns:
	 *   Instance of Strophe.Connection
	 */
	self.getConnection = function() {
		return _connection;
	};

	/** Function: removeRoom
	 * Removes a room from the rooms list
	 *
	 * Parameters:
	 *   (String) roomJid - roomJid
	 */
	self.removeRoom = function(roomJid) {
		delete _rooms[roomJid];
	};

	/** Function: getRooms
	 * Gets all joined rooms
	 *
	 * Returns:
	 *   Object containing instances of Candy.Core.ChatRoom
	 */
	self.getRooms = function() {
		return _rooms;
	};

	/** Function: getStropheStatus
	 * Get the status set by Strophe.
	 *
	 * Returns:
	 *   (Strophe.Status.*) - one of Strophe's statuses
	 */
	self.getStropheStatus = function() {
		return _status;
	};

	/** Function: setStropheStatus
	 * Set the strophe status
	 *
	 * Called by:
	 *   Candy.Core.Event.Strophe.Connect
	 *
	 * Parameters:
	 *   (Strophe.Status.*) status - Strophe's status
	 */
	self.setStropheStatus = function(status) {
		_status = status;
	};

	/** Function: isAnonymousConnection
	 * Returns true if <Candy.Core.connect> was first called with a domain instead of a jid as the first param.
	 *
	 * Returns:
	 *   (Boolean)
	 */
	self.isAnonymousConnection = function() {
		return _anonymousConnection;
	};

	/** Function: getOptions
	 * Gets options
	 *
	 * Returns:
	 *   Object
	 */
	self.getOptions = function() {
		return _options;
	};

    /** Function: getRoom
	 * Gets a specific room
	 *
	 * Parameters:
	 *   (String) roomJid - JID of the room
	 *
	 * Returns:
	 *   If the room is joined, instance of Candy.Core.ChatRoom, otherwise null.
	 */
	self.getRoom = function(roomJid) {
		if (_rooms[roomJid]) {
			return _rooms[roomJid];
		}
		return null;
	};

	/** Function: onWindowUnload
	 * window.onbeforeunload event which disconnects the client from the Jabber server.
	 */
	self.onWindowUnload = function() {
		// Enable synchronous requests because Safari doesn't send asynchronous requests within unbeforeunload events.
		// Only works properly when following patch is applied to strophejs: https://github.com/metajack/strophejs/issues/16/#issuecomment-600266
		_connection.options.sync = true;
		self.disconnect();
		_connection.flush();
	};

	/** Function: rawInput
	 * (Overridden from Strophe.Connection.rawInput)
	 *
	 * Logs all raw input if debug is set to true.
	 */
	self.rawInput = function(data) {
		this.log('RECV: ' + data);
	};

	/** Function rawOutput
	 * (Overridden from Strophe.Connection.rawOutput)
	 *
	 * Logs all raw output if debug is set to true.
	 */
	self.rawOutput = function(data) {
		this.log('SENT: ' + data);
	};

	/** Function: log
	 * Overridden to do something useful if debug is set to true.
	 *
	 * See: Candy.Core#init
	 */
	self.log = function() {};

	return self;
}(Candy.Core || {}, Strophe, jQuery));
<commit_msg>Allow the JID resource to be set using an intuitive parameter name<commit_after>/** File: core.js
 * Candy - Chats are not dead yet.
 *
 * Authors:
 *   - Patrick Stadler <patrick.stadler@gmail.com>
 *   - Michael Weibel <michael.weibel@gmail.com>
 *
 * Copyright:
 *   (c) 2011 Amiado Group AG. All rights reserved.
 *   (c) 2012-2014 Patrick Stadler & Michael Weibel. All rights reserved.
 */
'use strict';

/* global Candy, window, Strophe, jQuery */

/** Class: Candy.Core
 * Candy Chat Core
 *
 * Parameters:
 *   (Candy.Core) self - itself
 *   (Strophe) Strophe - Strophe JS
 *   (jQuery) $ - jQuery
 */
Candy.Core = (function(self, Strophe, $) {
		/** PrivateVariable: _connection
		 * Strophe connection
		 */
	var _connection = null,
		/** PrivateVariable: _service
		 * URL of BOSH service
		 */
		_service = null,
		/** PrivateVariable: _user
		 * Current user (me)
		 */
		_user = null,
		/** PrivateVariable: _rooms
		 * Opened rooms, containing instances of Candy.Core.ChatRooms
		 */
		_rooms = {},
		/** PrivateVariable: _anonymousConnection
		 * Set in <Candy.Core.connect> when jidOrHost doesn't contain a @-char.
		 */
		_anonymousConnection = false,
		/** PrivateVariable: _status
		 * Current Strophe connection state
		 */
		_status,
		/** PrivateVariable: _options
		 * Options:
		 *   (Boolean) debug - Debug (Default: false)
		 *   (Array|Boolean) autojoin - Autojoin these channels. When boolean true, do not autojoin, wait if the server sends something.
		 */
		_options = {
			/** Boolean: autojoin
			 * If set to `true` try to get the bookmarks and autojoin the rooms (supported by Openfire).
			 * You may want to define an array of rooms to autojoin: `['room1@conference.host.tld', 'room2...]` (ejabberd, Openfire, ...)
			 */
			autojoin: undefined,
			debug: false,
			disableWindowUnload: false,
			/** Integer: presencePriority
			 * Default priority for presence messages in order to receive messages across different resources
			 */
			presencePriority: 1,
			/** String: resource
			 * JID resource to use when connecting to the server.
			 * Specify `''` (an empty string) to request a random resource.
			 */
			resource: Candy.about.name
		},

		/** PrivateFunction: _addNamespace
		 * Adds a namespace.
		 *
		 * Parameters:
		 *   (String) name - namespace name (will become a constant living in Strophe.NS.*)
		 *   (String) value - XML Namespace
		 */
		_addNamespace = function(name, value) {
			Strophe.addNamespace(name, value);
		},

		/** PrivateFunction: _addNamespaces
		 * Adds namespaces needed by Candy.
		 */
		_addNamespaces = function() {
			_addNamespace('PRIVATE', 'jabber:iq:private');
			_addNamespace('BOOKMARKS', 'storage:bookmarks');
			_addNamespace('PRIVACY', 'jabber:iq:privacy');
			_addNamespace('DELAY', 'jabber:x:delay');
		},

		_getEscapedJidFromJid = function(jid) {
			var node = Strophe.getNodeFromJid(jid),
				domain = Strophe.getDomainFromJid(jid);
			return node ? Strophe.escapeNode(node) + '@' + domain : domain;
		};

	/** Function: init
	 * Initialize Core.
	 *
	 * Parameters:
	 *   (String) service - URL of BOSH/Websocket service
	 *   (Object) options - Options for candy
	 */
	self.init = function(service, options) {
		_service = service;
		// Apply options
		$.extend(true, _options, options);

		// Enable debug logging
		if(_options.debug) {
			self.log = function(str) {
				try { // prevent erroring
					if(typeof window.console !== undefined && typeof window.console.log !== undefined) {
						console.log(str);
					}
				} catch(e) {}
			};
			self.log('[Init] Debugging enabled');
		}

		_addNamespaces();

		// Connect to BOSH/Websocket service
		_connection = new Strophe.Connection(_service);
		_connection.rawInput = self.rawInput.bind(self);
		_connection.rawOutput = self.rawOutput.bind(self);

		// set caps node
		_connection.caps.node = 'https://candy-chat.github.io/candy/';

		// Window unload handler... works on all browsers but Opera. There is NO workaround.
		// Opera clients getting disconnected 1-2 minutes delayed.
		if (!_options.disableWindowUnload) {
			window.onbeforeunload = self.onWindowUnload;
		}
	};

	/** Function: registerEventHandlers
	 * Adds listening handlers to the connection.
	 *
	 * Use with caution from outside of Candy.
	 */
	self.registerEventHandlers = function() {
		self.addHandler(self.Event.Jabber.Version, Strophe.NS.VERSION, 'iq');
		self.addHandler(self.Event.Jabber.Presence, null, 'presence');
		self.addHandler(self.Event.Jabber.Message, null, 'message');
		self.addHandler(self.Event.Jabber.Bookmarks, Strophe.NS.PRIVATE, 'iq');
		self.addHandler(self.Event.Jabber.Room.Disco, Strophe.NS.DISCO_INFO, 'iq', 'result');

		self.addHandler(_connection.disco._onDiscoInfo.bind(_connection.disco), Strophe.NS.DISCO_INFO, 'iq', 'get');
		self.addHandler(_connection.disco._onDiscoItems.bind(_connection.disco), Strophe.NS.DISCO_ITEMS, 'iq', 'get');
		self.addHandler(_connection.caps._delegateCapabilities.bind(_connection.caps), Strophe.NS.CAPS);
	};

	/** Function: connect
	 * Connect to the jabber host.
	 *
	 * There are four different procedures to login:
	 *   connect('JID', 'password') - Connect a registered user
	 *   connect('domain') - Connect anonymously to the domain. The user should receive a random JID.
	 *   connect('domain', null, 'nick') - Connect anonymously to the domain. The user should receive a random JID but with a nick set.
	 *   connect('JID') - Show login form and prompt for password. JID input is hidden.
	 *   connect() - Show login form and prompt for JID and password.
	 *
	 * See:
	 *   <Candy.Core.attach()> for attaching an already established session.
	 *
	 * Parameters:
	 *   (String) jidOrHost - JID or Host
	 *   (String) password  - Password of the user
	 *   (String) nick      - Nick of the user. Set one if you want to anonymously connect but preset a nick. If jidOrHost is a domain
	 *                        and this param is not set, Candy will prompt for a nick.
	 */
	self.connect = function(jidOrHost, password, nick) {
		// Reset before every connection attempt to make sure reconnections work after authfail, alltabsclosed, ...
		_connection.reset();
		self.registerEventHandlers();
		/** Event: candy:core.before-connect
		 * Triggered before a connection attempt is made.
		 *
		 * Plugins should register their stanza handlers using this event
		 * to ensure that they are set.
		 *
		 * See also <#84 at https://github.com/candy-chat/candy/issues/84>.
		 *
		 * Parameters:
		 *   (Strophe.Connection) conncetion - Strophe connection
		 */
		$(Candy).triggerHandler('candy:core.before-connect', {
			connection: _connection
		});

		_anonymousConnection = !_anonymousConnection ? jidOrHost && jidOrHost.indexOf("@") < 0 : true;

		if(jidOrHost && password) {
			// authentication
			_connection.connect(_getEscapedJidFromJid(jidOrHost) + '/' + _options.resource, password, Candy.Core.Event.Strophe.Connect);
			if (nick) {
				_user = new self.ChatUser(jidOrHost, nick);
			} else {
				_user = new self.ChatUser(jidOrHost, Strophe.getNodeFromJid(jidOrHost));
			}
		} else if(jidOrHost && nick) {
			// anonymous connect
			_connection.connect(_getEscapedJidFromJid(jidOrHost) + '/' + _options.resource, null, Candy.Core.Event.Strophe.Connect);
			_user = new self.ChatUser(null, nick); // set jid to null because we'll later receive it
		} else if(jidOrHost) {
			Candy.Core.Event.Login(jidOrHost);
		} else {
			// display login modal
			Candy.Core.Event.Login();
		}
	};

	/** Function: attach
	 * Attach an already binded & connected session to the server
	 *
	 * _See_ Strophe.Connection.attach
	 *
	 * Parameters:
	 *   (String) jid - Jabber ID
	 *   (Integer) sid - Session ID
	 *   (Integer) rid - rid
	 */
	self.attach = function(jid, sid, rid) {
		_user = new self.ChatUser(jid, Strophe.getNodeFromJid(jid));
		self.registerEventHandlers();
		_connection.attach(jid, sid, rid, Candy.Core.Event.Strophe.Connect);
	};

	/** Function: disconnect
	 * Leave all rooms and disconnect
	 */
	self.disconnect = function() {
		if(_connection.connected) {
			$.each(self.getRooms(), function() {
				Candy.Core.Action.Jabber.Room.Leave(this.getJid());
			});
			_connection.disconnect();
		}
	};

	/** Function: addHandler
	 * Wrapper for Strophe.Connection.addHandler() to add a stanza handler for the connection.
	 *
	 * Parameters:
	 *   (Function) handler - The user callback.
	 *   (String) ns - The namespace to match.
	 *   (String) name - The stanza name to match.
	 *   (String) type - The stanza type attribute to match.
	 *   (String) id - The stanza id attribute to match.
	 *   (String) from - The stanza from attribute to match.
	 *   (String) options - The handler options
	 *
	 * Returns:
	 *   A reference to the handler that can be used to remove it.
	 */
	self.addHandler = function(handler, ns, name, type, id, from, options) {
		return _connection.addHandler(handler, ns, name, type, id, from, options);
	};

	/** Function: getUser
	 * Gets current user
	 *
	 * Returns:
	 *   Instance of Candy.Core.ChatUser
	 */
	self.getUser = function() {
		return _user;
	};

	/** Function: setUser
	 * Set current user. Needed when anonymous login is used, as jid gets retrieved later.
	 *
	 * Parameters:
	 *   (Candy.Core.ChatUser) user - User instance
	 */
	self.setUser = function(user) {
		_user = user;
	};

	/** Function: getConnection
	 * Gets Strophe connection
	 *
	 * Returns:
	 *   Instance of Strophe.Connection
	 */
	self.getConnection = function() {
		return _connection;
	};

	/** Function: removeRoom
	 * Removes a room from the rooms list
	 *
	 * Parameters:
	 *   (String) roomJid - roomJid
	 */
	self.removeRoom = function(roomJid) {
		delete _rooms[roomJid];
	};

	/** Function: getRooms
	 * Gets all joined rooms
	 *
	 * Returns:
	 *   Object containing instances of Candy.Core.ChatRoom
	 */
	self.getRooms = function() {
		return _rooms;
	};

	/** Function: getStropheStatus
	 * Get the status set by Strophe.
	 *
	 * Returns:
	 *   (Strophe.Status.*) - one of Strophe's statuses
	 */
	self.getStropheStatus = function() {
		return _status;
	};

	/** Function: setStropheStatus
	 * Set the strophe status
	 *
	 * Called by:
	 *   Candy.Core.Event.Strophe.Connect
	 *
	 * Parameters:
	 *   (Strophe.Status.*) status - Strophe's status
	 */
	self.setStropheStatus = function(status) {
		_status = status;
	};

	/** Function: isAnonymousConnection
	 * Returns true if <Candy.Core.connect> was first called with a domain instead of a jid as the first param.
	 *
	 * Returns:
	 *   (Boolean)
	 */
	self.isAnonymousConnection = function() {
		return _anonymousConnection;
	};

	/** Function: getOptions
	 * Gets options
	 *
	 * Returns:
	 *   Object
	 */
	self.getOptions = function() {
		return _options;
	};

    /** Function: getRoom
	 * Gets a specific room
	 *
	 * Parameters:
	 *   (String) roomJid - JID of the room
	 *
	 * Returns:
	 *   If the room is joined, instance of Candy.Core.ChatRoom, otherwise null.
	 */
	self.getRoom = function(roomJid) {
		if (_rooms[roomJid]) {
			return _rooms[roomJid];
		}
		return null;
	};

	/** Function: onWindowUnload
	 * window.onbeforeunload event which disconnects the client from the Jabber server.
	 */
	self.onWindowUnload = function() {
		// Enable synchronous requests because Safari doesn't send asynchronous requests within unbeforeunload events.
		// Only works properly when following patch is applied to strophejs: https://github.com/metajack/strophejs/issues/16/#issuecomment-600266
		_connection.options.sync = true;
		self.disconnect();
		_connection.flush();
	};

	/** Function: rawInput
	 * (Overridden from Strophe.Connection.rawInput)
	 *
	 * Logs all raw input if debug is set to true.
	 */
	self.rawInput = function(data) {
		this.log('RECV: ' + data);
	};

	/** Function rawOutput
	 * (Overridden from Strophe.Connection.rawOutput)
	 *
	 * Logs all raw output if debug is set to true.
	 */
	self.rawOutput = function(data) {
		this.log('SENT: ' + data);
	};

	/** Function: log
	 * Overridden to do something useful if debug is set to true.
	 *
	 * See: Candy.Core#init
	 */
	self.log = function() {};

	return self;
}(Candy.Core || {}, Strophe, jQuery));
<|endoftext|>


<commit_before>/**
 * Created by janimo on 09.07.2015.
 */
'use strict';

define([
  'states/BaseState'
],function(BaseState){

    function Menu(){
        this.app = null;
        this.filter = null;
        this.filterSrc = [

            "precision mediump float;",

            "uniform float     time;",
            "uniform vec2      resolution;",

            "void main( void ) {",

            "vec2 normalPos = gl_FragCoord.xy / resolution.xy;",
            "float pos = (gl_FragCoord.y / resolution.y);",

            "float c = sin(pos * 400.0) * 0.4 + 0.4;",
            "c = pow(c, 0.2);",
            "c *= 0.2;",

            "float band_pos = fract(time * 0.1) * 3.0 - 1.0;",
            "c += clamp( (1.0 - abs(band_pos - pos) * 10.0), 0.0, 1.0) * 0.1;",

            "gl_FragColor = vec4( 0.0, c, 0.0, 1.0 );",
            "}"
        ];

        this.background = null;
        this.logo = null;
        this.arrowLeft = null;
        this.arrowRight = null;

        this.robot = null;

        this.scanIcon = null;

        this.infoText = null;

        this.content = null;
        this.contentTween = null;
    }

    Menu.prototype = Object.create(BaseState.prototype);
    Menu.prototype.constructor = Menu;

    Menu.prototype.init = function(){
        this.app = require('app');

        this.contentPage = 0;
        this.contentPageMin = -1;
        this.contentPageMax = 1;
        this.lastPointerX = 0;
        this.pointerDown = false;
    };

    Menu.prototype.preload = function(){
        this.load.image('background', 'assets/mainmenu/background.png');
        this.load.image('logo', 'assets/logo.png');
        this.load.image('scan_icon', 'assets/mainmenu/scan.png');
        this.load.spritesheet('arrow_left', 'assets/mainmenu/left.png',32,120);
        this.load.spritesheet('arrow_right', 'assets/mainmenu/right.png',32,120);

    };

    Menu.prototype.create = function(){
        this.filter = new Phaser.Filter(this.app.game, null, this.filterSrc);
        this.filter.setResolution(this.app.width, this.app.height);

        this.background = this.add.sprite();//0,0,'background');
        this.background.width = this.app.width;
        this.background.height = this.app.height;
        this.background.filters = [this.filter];

        this.content = this.game.add.group();

        this.robot = this.app.makeRobot('player');

        this.app.scaleMax(this.robot,this.world.width - 112,this.world.height - 260);
        this.robot.x = this.world.centerX - (this.robot.width/2);
        this.robot.y = this.world.centerY - (this.robot.height/2);
        //this.robot.setAll('tint', 0x76dc51);
        this.robot.alpha = 0.5;
        this.content.add(this.robot);

        this.scanIcon = this.add.sprite(this.world.centerX,this.world.centerY,'scan_icon');
        this.app.scaleMax(this.scanIcon,this.world.width - 180,this.world.height - 260);
        this.scanIcon.x += this.app.width;
        this.scanIcon.anchor.set(0.5);
        this.scanIcon.tint = 0x419001;
        this.scanIcon.alpha = 0.5;

        this.createOnClick(this.scanIcon,function(){
            this.onScanClick();
        });

        this.content.add(this.scanIcon);

        this.infoText = this.add.text(this.world.centerX,this.app.height - 50,'Zusammenstellung',{font: "35px bitwise",fill: '#419001',align: 'center'});
        this.infoText.anchor.set(0.5);

        this.logo = this.add.sprite(this.world.centerX, 80, 'logo');
        this.logo.anchor.set(0.5);

        this.arrowLeft = this.add.button(32, this.world.centerY, 'arrow_left');
        this.arrowLeft.anchor.set(0.5);
        this.arrowRight = this.add.button(this.world.width - 32, this.world.centerY, 'arrow_right');
        this.arrowRight.anchor.set(0.5);

    };

    Menu.prototype.update = function(){
        this.filter.update();

        if(this.input.activePointer.isDown){

            if(!this.pointerDown){
                this.lastPointerX = this.input.activePointer.x;
                this.pointerDown = true;
                if(this.contentTween != null){
                    this.contentTween.stop();
                }
            }

            var move = this.input.activePointer.x - this.lastPointerX;

            this.content.x += move;

            this.lastPointerX = this.input.activePointer.x;
        }

        if(this.input.activePointer.justReleased()){
            if(this.pointerDown){
                this.pointerDown = false;

                var pos = -(this.game.width * this.contentPage);
                var delta = this.content.x - pos;

                if(delta > this.app.width/8 && this.contentPage > this.contentPageMin){
                    this.contentPage--;
                }else if(delta < -(this.app.width/8) && this.contentPage < this.contentPageMax){
                    this.contentPage++;
                }

                this.bouncePage();

            }
        }

    };

    Menu.prototype.bouncePage =function(){
        switch(this.contentPage){
            case -1:
                this.infoText.text = 'Inventar';
                break;
            case 0:
                this.infoText.text = 'Zusammenstellung';
                break;
            case 1:
                this.infoText.text = 'Scannen';
                break;
        }

        var pos = -(this.game.width * this.contentPage);
        this.contentTween = this.add.tween(this.content).to({x:pos},150);
        this.contentTween.start();
    };

    Menu.prototype.render = function(){
        this.app.game.debug.text(''+this.contentPage, 10, 10);

        var y = 20;
        if(this.contentPage == -1){
            for(var i=0; i<this.app.user.inventory.length; i++){
                var item = this.app.user.inventory[i];
                this.app.game.debug.text(item.count + 'x ' + item.robotpart.name, 10, (i+1)*y);
            }
        }
    };

    Menu.prototype.onScanClick = function(){
        var self = this;
        cordova.plugins.barcodeScanner.scan(function(result){
            if(!result.cancelled){
                var code = btoa(result.text) + ';;' + result.format;
                self.app.connection.sendCode(code,self.onScanResult.bind(self));
            }
        },function(error){
            alert('Ein Fehler ist aufgetreten.');
        });
    };

    Menu.prototype.onScanResult = function(data){
        switch(data.status){
            case 'valid':
                alert(this.app.store.get('RobotPart',data.item).name);
                break;
            case 'used':
                alert('Du hast diesen Code heute bereits gescant!');
                break;
            case 'empty':
                alert('Dieser Code war leider leer.');
                break;
            default :
                alert('Der Code konnte nicht gelesen werden');
                break;
        }
    };

    return new Menu();
});<commit_msg>Andere Fehlermeldung bei Scanfehlschlag<commit_after>/**
 * Created by janimo on 09.07.2015.
 */
'use strict';

define([
  'states/BaseState'
],function(BaseState){

    function Menu(){
        this.app = null;
        this.filter = null;
        this.filterSrc = [

            "precision mediump float;",

            "uniform float     time;",
            "uniform vec2      resolution;",

            "void main( void ) {",

            "vec2 normalPos = gl_FragCoord.xy / resolution.xy;",
            "float pos = (gl_FragCoord.y / resolution.y);",

            "float c = sin(pos * 400.0) * 0.4 + 0.4;",
            "c = pow(c, 0.2);",
            "c *= 0.2;",

            "float band_pos = fract(time * 0.1) * 3.0 - 1.0;",
            "c += clamp( (1.0 - abs(band_pos - pos) * 10.0), 0.0, 1.0) * 0.1;",

            "gl_FragColor = vec4( 0.0, c, 0.0, 1.0 );",
            "}"
        ];

        this.background = null;
        this.logo = null;
        this.arrowLeft = null;
        this.arrowRight = null;

        this.robot = null;

        this.scanIcon = null;

        this.infoText = null;

        this.content = null;
        this.contentTween = null;
    }

    Menu.prototype = Object.create(BaseState.prototype);
    Menu.prototype.constructor = Menu;

    Menu.prototype.init = function(){
        this.app = require('app');

        this.contentPage = 0;
        this.contentPageMin = -1;
        this.contentPageMax = 1;
        this.lastPointerX = 0;
        this.pointerDown = false;
    };

    Menu.prototype.preload = function(){
        this.load.image('background', 'assets/mainmenu/background.png');
        this.load.image('logo', 'assets/logo.png');
        this.load.image('scan_icon', 'assets/mainmenu/scan.png');
        this.load.spritesheet('arrow_left', 'assets/mainmenu/left.png',32,120);
        this.load.spritesheet('arrow_right', 'assets/mainmenu/right.png',32,120);

    };

    Menu.prototype.create = function(){
        this.filter = new Phaser.Filter(this.app.game, null, this.filterSrc);
        this.filter.setResolution(this.app.width, this.app.height);

        this.background = this.add.sprite();//0,0,'background');
        this.background.width = this.app.width;
        this.background.height = this.app.height;
        this.background.filters = [this.filter];

        this.content = this.game.add.group();

        this.robot = this.app.makeRobot('player');

        this.app.scaleMax(this.robot,this.world.width - 112,this.world.height - 260);
        this.robot.x = this.world.centerX - (this.robot.width/2);
        this.robot.y = this.world.centerY - (this.robot.height/2);
        //this.robot.setAll('tint', 0x76dc51);
        this.robot.alpha = 0.5;
        this.content.add(this.robot);

        this.scanIcon = this.add.sprite(this.world.centerX,this.world.centerY,'scan_icon');
        this.app.scaleMax(this.scanIcon,this.world.width - 180,this.world.height - 260);
        this.scanIcon.x += this.app.width;
        this.scanIcon.anchor.set(0.5);
        this.scanIcon.tint = 0x419001;
        this.scanIcon.alpha = 0.5;

        this.createOnClick(this.scanIcon,function(){
            this.onScanClick();
        });

        this.content.add(this.scanIcon);

        this.infoText = this.add.text(this.world.centerX,this.app.height - 50,'Zusammenstellung',{font: "35px bitwise",fill: '#419001',align: 'center'});
        this.infoText.anchor.set(0.5);

        this.logo = this.add.sprite(this.world.centerX, 80, 'logo');
        this.logo.anchor.set(0.5);

        this.arrowLeft = this.add.button(32, this.world.centerY, 'arrow_left');
        this.arrowLeft.anchor.set(0.5);
        this.arrowRight = this.add.button(this.world.width - 32, this.world.centerY, 'arrow_right');
        this.arrowRight.anchor.set(0.5);

    };

    Menu.prototype.update = function(){
        this.filter.update();

        if(this.input.activePointer.isDown){

            if(!this.pointerDown){
                this.lastPointerX = this.input.activePointer.x;
                this.pointerDown = true;
                if(this.contentTween != null){
                    this.contentTween.stop();
                }
            }

            var move = this.input.activePointer.x - this.lastPointerX;

            this.content.x += move;

            this.lastPointerX = this.input.activePointer.x;
        }

        if(this.input.activePointer.justReleased()){
            if(this.pointerDown){
                this.pointerDown = false;

                var pos = -(this.game.width * this.contentPage);
                var delta = this.content.x - pos;

                if(delta > this.app.width/8 && this.contentPage > this.contentPageMin){
                    this.contentPage--;
                }else if(delta < -(this.app.width/8) && this.contentPage < this.contentPageMax){
                    this.contentPage++;
                }

                this.bouncePage();

            }
        }

    };

    Menu.prototype.bouncePage =function(){
        switch(this.contentPage){
            case -1:
                this.infoText.text = 'Inventar';
                break;
            case 0:
                this.infoText.text = 'Zusammenstellung';
                break;
            case 1:
                this.infoText.text = 'Scannen';
                break;
        }

        var pos = -(this.game.width * this.contentPage);
        this.contentTween = this.add.tween(this.content).to({x:pos},150);
        this.contentTween.start();
    };

    Menu.prototype.render = function(){
        this.app.game.debug.text(''+this.contentPage, 10, 10);

        var y = 20;
        if(this.contentPage == -1){
            for(var i=0; i<this.app.user.inventory.length; i++){
                var item = this.app.user.inventory[i];
                this.app.game.debug.text(item.count + 'x ' + item.robotpart.name, 10, (i+1)*y);
            }
        }
    };

    Menu.prototype.onScanClick = function(){
        var self = this;
        cordova.plugins.barcodeScanner.scan(function(result){
            if(!result.cancelled){
                var code = btoa(result.text) + ';;' + result.format;
                self.app.connection.sendCode(code,self.onScanResult.bind(self));
            }
        },function(error){
            alert('Du benötigst eine Kamera um Codes zu scannen.');
        });
    };

    Menu.prototype.onScanResult = function(data){
        switch(data.status){
            case 'valid':
                alert(this.app.store.get('RobotPart',data.item).name);
                break;
            case 'used':
                alert('Du hast diesen Code heute bereits gescant!');
                break;
            case 'empty':
                alert('Dieser Code war leider leer.');
                break;
            default :
                alert('Der Code konnte nicht gelesen werden');
                break;
        }
    };

    return new Menu();
});<|endoftext|>


<commit_before>var document   = window.document
  , _tire      = window.tire
  , _$         = window.$
  , idExp      = /^#/
  , simpleExp  = /^#?([\w-]+)$/
  , classExp   = /^\./
  , tagExp     = /<([\w:]+)/
  , slice      = [].slice;

// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
  var rest = this.slice((to || from) + 1 || this.length);
  this.length = from < 0 ? this.length + from : from;
  return this.push.apply(this, rest);
};

// If slice is not available we provide a backup
try {
  slice.call(document.documentElement.childNodes, 0)[0].nodeType;
} catch(e) {
  slice = function (i) {
    i = i || 0;
    var elem, results = [];
    for (; (elem = this[i]); i++ ) {
      results.push( elem );
    }
    return results;
  };
}

var tire = function (selector, context) {
  return new tire.fn.find(selector, context);
};

tire.fn = tire.prototype = {

  /**
   * Default length is zero
   */

  length: 0,

  /**
   * Extend `tire.fn`
   *
   * @param {Object} o
   */

  extend: function (o) {
    for (var k in o) {
      this[k] = o[k];
    }
  },

  /**
   * Find elements by selector
   *
   * @param {String|Object|Function|Array} selector
   * @param {Object} context
   *
   * @return {Object}
   */

  find: function (selector, context) {
    var elms = [];

    if (!selector) {
      return this;
    }

    if (tire.isFun(selector)) {
      tire.ready(selector);
    }

    if (selector.nodeType) {
      this.selector = '';
      this.context = selector;
      return this.set([selector]);
    }

    if (selector.length === 1 && selector[0].nodeType) {
      this.selector = selector.selector;
      this.context = selector[0];
      return this.set(selector);
    }

    this.context = context = (context || document);

    if (tire.isStr(selector)) {
      this.selector = selector;
      if (simpleExp.test(selector)) {
        elms = slice.call(idExp.test(selector) ? [document.getElementById(selector.substr(1))] : document.getElementsByTagName(selector), 0);
        if (elms[0] === null) {
          elms = [];
        }
      } else if (classExp.test(selector) && document.getElementsByClassName !== undefined) {
        elms = slice.call(document.getElementsByClassName(selector.substr(1)), 0);
        if (elms[0] === null) {
          elms = [];
        }
      } else if (tagExp.test(selector)) {
        var tmp = document.createElement('div');
        tmp.innerHTML = selector;
        this.each.call(slice.call(tmp.childNodes, 0), function () {
          elms.push(this);
        });
      } else {
        elms = document.querySelectorAll(selector);
      }
    } else if (selector.nodeName || selector === window) {
      elms = [selector];
    } else if (tire.isArr(selector)) {
      elms = selector;
    }

    return this.set(elms);
  },

  /**
   * Fetch property from elements
   *
   * @param {String} prop
   * @return {Array}
   */

  pluck: function (prop) {
    var result = [];
    this.each(function () {
      if (this[prop]) result.push(this[prop]);
    });
    return result;
  },

  /**
   * Run callback for each element in the collection
   *
   * @param {Function} callback
   * @return {Object}
   */

  each: function(target, callback) {
    var i, key;

    if (tire.isFun(target)) {
      callback = target;
      target = this;
    }

    if (target === this || tire.isArr(target)) {
      for (i = 0; i < target.length; ++i) {
        if (callback.call(target[i], target[i], i, target) === false) break;
      }
    } else {
      for (key in target) {
        if (target.hasOwnProperty(key) && callback.call(target[key], key, target[key]) === false) break;
      }
    }

    return target;
  },

  /**
   * Set elements to tire object before returning `this`
   *
   * @param {Array} elements
   * @return {Object}
   */

  set: function (elements) {
    var i = 0;
    for (; i < elements.length; i++) {
      this[i] = elements[i];
    }
    this.length = i;
    return this;
  }
};

/**
 * Extend `tire` with arguments, if the arguments length is one the extend target is `tire`
 */

tire.extend = function () {
  var options
    , i = 1
    , target = arguments[0] || {};

  if (typeof target !== 'object' && typeof target !== 'function') {
    target = {};
  }

  if (arguments.length === 1) {
    target = this;
    i = 0;
  }

  for (; i < arguments.length; i++) {
    if ((options = arguments[i]) !== null) {
      for (var key in options) {
        if (target[key] === options[key]) {
          continue;
        } else {
          target[key] = options[key];
        }
      }
    }
  }

  return target;
};

tire.fn.find.prototype = tire.fn;

tire.extend({

  // We sould be able to use slice outside
  slice: slice,

  // We sould be able to use each outside
  each: tire.fn.each,

  /**
   * Trim string
   *
   * @param {String} str
   * @return {String}
   */

  trim: function (str) {
    return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
  },

  /**
   * Check if the element matches the selector
   *
   * @param {Object} element
   * @param {String} selector
   * @return {Boolean}
   */

  matches: function (element, selector) {
    if (!element || element.nodeType !== 1) return;

    // Trying to use matchesSelector if it is available
    var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.matchesSelector;
    if (matchesSelector) {
      return matchesSelector.call(element, selector);
    }

    // querySelectorAll fallback
    if (document.querySelectorAll !== undefined) {
      var nodes = element.parentNode.querySelectorAll(selector);

      for (var i = 0; i < nodes.length; i++) {
        if (nodes[i] === element) return true;
      }

      return false;
    }
  },

  /**
   * Check if the object is a function
   *
   * @param {Object} obj
   * @return {Boolean}
   */

  isFun: function (obj) {
    return typeof obj === 'function';
  },

  /**
   * Check if the object is a array
   *
   * @param {Object} obj
   * @return {Boolean}
   */

  isArr: function (obj) {
    return obj instanceof Array;
  },

  /**
   * Check if the object is a string
   *
   * @param {Object} obj
   * @return {Boolean}
   */

  isStr: function (obj) {
    return typeof obj === 'string';
  },

  /**
   * Check if the object is a number
   *
   * @param {Object} obj
   * @return {Boolean}
   */

  isNum: function (obj) {
    return typeof obj === 'number';
  },

  /**
   * Check if the object is a object
   *
   * @param {Object} obj
   * @return {Boolean}
   */

  isObj: function (obj) {
    return obj instanceof Object;
  },

  /**
   * Parse JSON string to object.
   *
   * @param {String} str
   * @return {Object|null)
   */

  parseJSON: function (str) {
    if (!this.isStr(str) || !str) {
      return null;
    }

    str = this.trim(str);

    if (window.JSON && window.JSON.parse) {
      return window.JSON.parse(str);
    }

    // Solution to fix JSON parse support for older browser. Not so nice but it works.
    try { return (new Function('return ' + str))(); }
    catch (e) { return null; }
  },

  /**
   * Calling .noConflict will restore the window.$` to its previous value.
   *
   * @param {Boolean} name Restore `tire` to it's previous value.
   * @return {Object}
   */

  noConflict: function (name) {
    if (name) {
      window.tire = _tire;
    }

    window.$ = _$;
    return tire;
  }
});<commit_msg>matches should return false wen element is wrong or not match is found.<commit_after>var document   = window.document
  , _tire      = window.tire
  , _$         = window.$
  , idExp      = /^#/
  , simpleExp  = /^#?([\w-]+)$/
  , classExp   = /^\./
  , tagExp     = /<([\w:]+)/
  , slice      = [].slice;

// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
  var rest = this.slice((to || from) + 1 || this.length);
  this.length = from < 0 ? this.length + from : from;
  return this.push.apply(this, rest);
};

// If slice is not available we provide a backup
try {
  slice.call(document.documentElement.childNodes, 0)[0].nodeType;
} catch(e) {
  slice = function (i) {
    i = i || 0;
    var elem, results = [];
    for (; (elem = this[i]); i++ ) {
      results.push( elem );
    }
    return results;
  };
}

var tire = function (selector, context) {
  return new tire.fn.find(selector, context);
};

tire.fn = tire.prototype = {

  /**
   * Default length is zero
   */

  length: 0,

  /**
   * Extend `tire.fn`
   *
   * @param {Object} o
   */

  extend: function (o) {
    for (var k in o) {
      this[k] = o[k];
    }
  },

  /**
   * Find elements by selector
   *
   * @param {String|Object|Function|Array} selector
   * @param {Object} context
   *
   * @return {Object}
   */

  find: function (selector, context) {
    var elms = [];

    if (!selector) {
      return this;
    }

    if (tire.isFun(selector)) {
      tire.ready(selector);
    }

    if (selector.nodeType) {
      this.selector = '';
      this.context = selector;
      return this.set([selector]);
    }

    if (selector.length === 1 && selector[0].nodeType) {
      this.selector = selector.selector;
      this.context = selector[0];
      return this.set(selector);
    }

    this.context = context = (context || document);

    if (tire.isStr(selector)) {
      this.selector = selector;
      if (simpleExp.test(selector)) {
        elms = slice.call(idExp.test(selector) ? [document.getElementById(selector.substr(1))] : document.getElementsByTagName(selector), 0);
        if (elms[0] === null) {
          elms = [];
        }
      } else if (classExp.test(selector) && document.getElementsByClassName !== undefined) {
        elms = slice.call(document.getElementsByClassName(selector.substr(1)), 0);
        if (elms[0] === null) {
          elms = [];
        }
      } else if (tagExp.test(selector)) {
        var tmp = document.createElement('div');
        tmp.innerHTML = selector;
        this.each.call(slice.call(tmp.childNodes, 0), function () {
          elms.push(this);
        });
      } else {
        elms = document.querySelectorAll(selector);
      }
    } else if (selector.nodeName || selector === window) {
      elms = [selector];
    } else if (tire.isArr(selector)) {
      elms = selector;
    }

    return this.set(elms);
  },

  /**
   * Fetch property from elements
   *
   * @param {String} prop
   * @return {Array}
   */

  pluck: function (prop) {
    var result = [];
    this.each(function () {
      if (this[prop]) result.push(this[prop]);
    });
    return result;
  },

  /**
   * Run callback for each element in the collection
   *
   * @param {Function} callback
   * @return {Object}
   */

  each: function(target, callback) {
    var i, key;

    if (tire.isFun(target)) {
      callback = target;
      target = this;
    }

    if (target === this || tire.isArr(target)) {
      for (i = 0; i < target.length; ++i) {
        if (callback.call(target[i], target[i], i, target) === false) break;
      }
    } else {
      for (key in target) {
        if (target.hasOwnProperty(key) && callback.call(target[key], key, target[key]) === false) break;
      }
    }

    return target;
  },

  /**
   * Set elements to tire object before returning `this`
   *
   * @param {Array} elements
   * @return {Object}
   */

  set: function (elements) {
    var i = 0;
    for (; i < elements.length; i++) {
      this[i] = elements[i];
    }
    this.length = i;
    return this;
  }
};

/**
 * Extend `tire` with arguments, if the arguments length is one the extend target is `tire`
 */

tire.extend = function () {
  var options
    , i = 1
    , target = arguments[0] || {};

  if (typeof target !== 'object' && typeof target !== 'function') {
    target = {};
  }

  if (arguments.length === 1) {
    target = this;
    i = 0;
  }

  for (; i < arguments.length; i++) {
    if ((options = arguments[i]) !== null) {
      for (var key in options) {
        if (target[key] === options[key]) {
          continue;
        } else {
          target[key] = options[key];
        }
      }
    }
  }

  return target;
};

tire.fn.find.prototype = tire.fn;

tire.extend({

  // We sould be able to use slice outside
  slice: slice,

  // We sould be able to use each outside
  each: tire.fn.each,

  /**
   * Trim string
   *
   * @param {String} str
   * @return {String}
   */

  trim: function (str) {
    return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, '');
  },

  /**
   * Check if the element matches the selector
   *
   * @param {Object} element
   * @param {String} selector
   * @return {Boolean}
   */

  matches: function (element, selector) {
    if (!element || element.nodeType !== 1) return false;

    // Trying to use matchesSelector if it is available
    var matchesSelector = element.webkitMatchesSelector || element.mozMatchesSelector || element.oMatchesSelector || element.matchesSelector;
    if (matchesSelector) {
      return matchesSelector.call(element, selector);
    }

    // querySelectorAll fallback
    if (document.querySelectorAll !== undefined) {
      var nodes = element.parentNode.querySelectorAll(selector);

      for (var i = 0; i < nodes.length; i++) {
        if (nodes[i] === element) return true;
      }
    }

    return false;
  },

  /**
   * Check if the object is a function
   *
   * @param {Object} obj
   * @return {Boolean}
   */

  isFun: function (obj) {
    return typeof obj === 'function';
  },

  /**
   * Check if the object is a array
   *
   * @param {Object} obj
   * @return {Boolean}
   */

  isArr: function (obj) {
    return obj instanceof Array;
  },

  /**
   * Check if the object is a string
   *
   * @param {Object} obj
   * @return {Boolean}
   */

  isStr: function (obj) {
    return typeof obj === 'string';
  },

  /**
   * Check if the object is a number
   *
   * @param {Object} obj
   * @return {Boolean}
   */

  isNum: function (obj) {
    return typeof obj === 'number';
  },

  /**
   * Check if the object is a object
   *
   * @param {Object} obj
   * @return {Boolean}
   */

  isObj: function (obj) {
    return obj instanceof Object;
  },

  /**
   * Parse JSON string to object.
   *
   * @param {String} str
   * @return {Object|null)
   */

  parseJSON: function (str) {
    if (!this.isStr(str) || !str) {
      return null;
    }

    str = this.trim(str);

    if (window.JSON && window.JSON.parse) {
      return window.JSON.parse(str);
    }

    // Solution to fix JSON parse support for older browser. Not so nice but it works.
    try { return (new Function('return ' + str))(); }
    catch (e) { return null; }
  },

  /**
   * Calling .noConflict will restore the window.$` to its previous value.
   *
   * @param {Boolean} name Restore `tire` to it's previous value.
   * @return {Object}
   */

  noConflict: function (name) {
    if (name) {
      window.tire = _tire;
    }

    window.$ = _$;
    return tire;
  }
});<|endoftext|>


<commit_before>/**
 * Created by janimo on 09.07.2015.
 */
'use strict';

define(function(){

    function Menu(){
        this.app = null;
        this.filter = null;
        this.filterSrc = [

            "precision mediump float;",

            "uniform float     time;",
            "uniform vec2      resolution;",

            "void main( void ) {",

            "vec2 normalPos = gl_FragCoord.xy / resolution.xy;",
            "float pos = (gl_FragCoord.y / resolution.y);",

            "float c = sin(pos * 400.0) * 0.4 + 0.4;",
            "c = pow(c, 0.2);",
            "c *= 0.2;",

            "float band_pos = fract(time * 0.1) * 3.0 - 1.0;",
            "c += clamp( (1.0 - abs(band_pos - pos) * 10.0), 0.0, 1.0) * 0.1;",

            "gl_FragColor = vec4( 0.0, c, 0.0, 1.0 );",
            "}"
        ];

        this.background = null;
        this.logo = null;
        this.arrowLeft = null;
        this.arrowRight = null;

        this.robot = null;

        this.scanIcon = null;

        this.infoText = null;

        this.content = null;
        this.contentPage = 0;
        this.contentPageMin = -1;
        this.contentPageMax = 1;
        this.lastPointerX = 0;
        this.pointerDown = false;
    }

    Menu.prototype = new Phaser.State();

    Menu.prototype.init = function(){
        this.app = require('app');
    };

    Menu.prototype.preload = function(){
        this.load.image('background', 'assets/mainmenu/background.png');
        this.load.image('logo', 'assets/logo.png');
        this.load.image('scan_icon', 'assets/mainmenu/scan.png');
        this.load.spritesheet('arrow_left', 'assets/mainmenu/left.png',32,120);
        this.load.spritesheet('arrow_right', 'assets/mainmenu/right.png',32,120);

    };

    Menu.prototype.create = function(){
        this.filter = new Phaser.Filter(this.app.game, null, this.filterSrc);
        this.filter.setResolution(this.app.width, this.app.height);

        this.background = this.add.sprite();//0,0,'background');
        this.background.width = this.app.width;
        this.background.height = this.app.height;
        this.background.filters = [this.filter];

        this.content = this.game.add.group();

        this.robot = this.app.makeRobot();
        this.app.scaleMax(this.robot,this.world.width - 112,this.world.height - 260);
        this.robot.x = this.world.centerX - (this.robot.width/2);
        this.robot.y = this.world.centerY - (this.robot.height/2);
        this.content.add(this.robot);

        this.scanIcon = this.add.sprite(this.world.centerX,this.world.centerY,'scan_icon');
        this.scanIcon.x += this.app.width;
        this.scanIcon.anchor.set(0.5);
        this.content.add(this.scanIcon);

        this.infoText = this.add.text(this.world.centerX,this.app.height - 50,'Zusammenstellung',{font: "35px bitwise",fill: '#419001',align: 'center'});
        this.infoText.anchor.set(0.5);

        this.logo = this.add.sprite(this.world.centerX, 80, 'logo');
        this.logo.anchor.set(0.5);

        this.arrowLeft = this.add.button(32, this.world.centerY, 'arrow_left');
        this.arrowLeft.anchor.set(0.5);
        this.arrowRight = this.add.button(this.world.width - 32, this.world.centerY, 'arrow_right');
        this.arrowRight.anchor.set(0.5);

    };

    Menu.prototype.update = function(){
        this.filter.update();

        if(this.input.activePointer.isDown){

            if(!this.pointerDown){
                this.lastPointerX = this.input.activePointer.x;
                this.pointerDown = true;
            }

            var move = this.input.activePointer.x - this.lastPointerX;

            this.content.x += move;

            this.lastPointerX = this.input.activePointer.x;
        }

        if(this.input.activePointer.justReleased()){
            if(this.pointerDown){
                this.pointerDown = false;

                var pos = -(this.game.width * this.contentPage);
                var delta = this.content.x - pos;

                if(delta > this.app.width/8 && this.contentPage > this.contentPageMin){
                    this.contentPage--;
                }else if(delta < -(this.app.width/8) && this.contentPage < this.contentPageMax){
                    this.contentPage++;
                }

                switch(this.contentPage){
                    case -1:
                        this.infoText.text = 'Inventar';
                        break;
                    case 0:
                        this.infoText.text = 'Zusammenstellung';
                        break;
                    case 1:
                        this.infoText.text = 'Scannen';
                        break;
                }

                this.content.x = -(this.game.width * this.contentPage);

            }
        }

    };

    Menu.prototype.render = function(){
        this.app.game.debug.text(''+this.contentPage, 10, 10);
    };

    return new Menu();
});<commit_msg>Swipe Animation mit Tweens<commit_after>/**
 * Created by janimo on 09.07.2015.
 */
'use strict';

define(function(){

    function Menu(){
        this.app = null;
        this.filter = null;
        this.filterSrc = [

            "precision mediump float;",

            "uniform float     time;",
            "uniform vec2      resolution;",

            "void main( void ) {",

            "vec2 normalPos = gl_FragCoord.xy / resolution.xy;",
            "float pos = (gl_FragCoord.y / resolution.y);",

            "float c = sin(pos * 400.0) * 0.4 + 0.4;",
            "c = pow(c, 0.2);",
            "c *= 0.2;",

            "float band_pos = fract(time * 0.1) * 3.0 - 1.0;",
            "c += clamp( (1.0 - abs(band_pos - pos) * 10.0), 0.0, 1.0) * 0.1;",

            "gl_FragColor = vec4( 0.0, c, 0.0, 1.0 );",
            "}"
        ];

        this.background = null;
        this.logo = null;
        this.arrowLeft = null;
        this.arrowRight = null;

        this.robot = null;

        this.scanIcon = null;

        this.infoText = null;

        this.content = null;
        this.contentPage = 0;
        this.contentPageMin = -1;
        this.contentPageMax = 1;
        this.lastPointerX = 0;
        this.pointerDown = false;
        this.contentTween = null;
    }

    Menu.prototype = new Phaser.State();

    Menu.prototype.init = function(){
        this.app = require('app');
    };

    Menu.prototype.preload = function(){
        this.load.image('background', 'assets/mainmenu/background.png');
        this.load.image('logo', 'assets/logo.png');
        this.load.image('scan_icon', 'assets/mainmenu/scan.png');
        this.load.spritesheet('arrow_left', 'assets/mainmenu/left.png',32,120);
        this.load.spritesheet('arrow_right', 'assets/mainmenu/right.png',32,120);

    };

    Menu.prototype.create = function(){
        this.filter = new Phaser.Filter(this.app.game, null, this.filterSrc);
        this.filter.setResolution(this.app.width, this.app.height);

        this.background = this.add.sprite();//0,0,'background');
        this.background.width = this.app.width;
        this.background.height = this.app.height;
        this.background.filters = [this.filter];

        this.content = this.game.add.group();

        this.robot = this.app.makeRobot();
        this.app.scaleMax(this.robot,this.world.width - 112,this.world.height - 260);
        this.robot.x = this.world.centerX - (this.robot.width/2);
        this.robot.y = this.world.centerY - (this.robot.height/2);
        this.content.add(this.robot);

        this.scanIcon = this.add.sprite(this.world.centerX,this.world.centerY,'scan_icon');
        this.scanIcon.x += this.app.width;
        this.scanIcon.anchor.set(0.5);
        this.content.add(this.scanIcon);

        this.infoText = this.add.text(this.world.centerX,this.app.height - 50,'Zusammenstellung',{font: "35px bitwise",fill: '#419001',align: 'center'});
        this.infoText.anchor.set(0.5);

        this.logo = this.add.sprite(this.world.centerX, 80, 'logo');
        this.logo.anchor.set(0.5);

        this.arrowLeft = this.add.button(32, this.world.centerY, 'arrow_left');
        this.arrowLeft.anchor.set(0.5);
        this.arrowRight = this.add.button(this.world.width - 32, this.world.centerY, 'arrow_right');
        this.arrowRight.anchor.set(0.5);

    };

    Menu.prototype.update = function(){
        this.filter.update();

        if(this.input.activePointer.isDown){

            if(!this.pointerDown){
                this.lastPointerX = this.input.activePointer.x;
                this.pointerDown = true;
                if(this.contentTween != null){
                    this.contentTween.stop();
                }
            }

            var move = this.input.activePointer.x - this.lastPointerX;

            this.content.x += move;

            this.lastPointerX = this.input.activePointer.x;
        }

        if(this.input.activePointer.justReleased()){
            if(this.pointerDown){
                this.pointerDown = false;

                var pos = -(this.game.width * this.contentPage);
                var delta = this.content.x - pos;

                if(delta > this.app.width/8 && this.contentPage > this.contentPageMin){
                    this.contentPage--;
                }else if(delta < -(this.app.width/8) && this.contentPage < this.contentPageMax){
                    this.contentPage++;
                }

                switch(this.contentPage){
                    case -1:
                        this.infoText.text = 'Inventar';
                        break;
                    case 0:
                        this.infoText.text = 'Zusammenstellung';
                        break;
                    case 1:
                        this.infoText.text = 'Scannen';
                        break;
                }

                pos = -(this.game.width * this.contentPage);
                this.contentTween = this.add.tween(this.content).to({x:pos},150);
                this.contentTween.start();

            }
        }

    };

    Menu.prototype.render = function(){
        this.app.game.debug.text(''+this.contentPage, 10, 10);
    };

    return new Menu();
});<|endoftext|>


<commit_before>(function(RemoteDialog, $, undefined) {
    var modalId = '#remote-dialog-modal';

    RemoteDialog.defaults = {
        identifier: null,
        url: null,
        parser: defaultParser
    };

    // jshint multistr:true
    RemoteDialog.template = '\

 \

 \

 \

 \

 \

 ';

    function init() {
        var dialog = $(modalId);

        //Create the message box if it doesn't exist
        if (dialog.length === 0) {
            dialog = $(RemoteDialog.template).appendTo(document.body);
        }

        dialog.modal({
            backdrop: 'static',
            keyboard: false,
            show: false
        });
    }

    function eventHandlers() {
        $('body').on('click', '[data-toggle="remote-dialog"]', function(event) {
            var element = $(event.target);

            RemoteDialog.Show({
                identifier: element.attr('data-identifier'),
                url: element.attr('data-url')
            });
        });

        //Show/Hide events
        $('body').on('shown.bs.modal', modalId, triggerDialogEvent.bind(null, 'shown'));
        $('body').on('hidden.bs.modal', modalId, triggerDialogEvent.bind(null, 'hidden'));
    }

    RemoteDialog.Show = function(options) {
        var modal = $(modalId);
        var content = modal.find('.modal-content');

        var settings = $.extend(RemoteDialog.defaults, options);

        if (!settings.url || !settings.identifier) {
            throw "Url and Identifier must be specified";
        }

        //Set the identifier
        modal.attr('data-identifier', settings.identifier);

        //Load the conent
        $.ajax({
            type: 'GET',
            url: settings.url,
            success: function(result) {
                content.html(settings.parser(result));
                modal.modal('show');
            },
            error: function(xhr, ajaxOptions, thrownError) {
                triggerDialogEvent('error', [xhr, ajaxOptions, thrownError]);
            }
        });
    };

    function defaultParser(data) {
        return data;
    }

    function triggerDialogEvent(eventName, parameters) {
        var modal = $(modalId);
        var identifier = modal.attr('data-identifier');

        modal.trigger(eventName + '.dialog.' + identifier, parameters);
    }

    $(function() {
        init();
        eventHandlers();
    });

})(window.RemoteDialog = window.RemoteDialog || {}, jQuery);
<commit_msg>-Added code to handle button options -Added code to handle confirm button options<commit_after>(function(RemoteDialog, $, undefined) {
    var modalId = '#remote-dialog-modal';

    RemoteDialog.handlers = {
        load: defaultLoadHandler,
        loadSuccess: defaultLoadSuccessHandler,
        loadError: defaultLoadErrorHandler,
        submit: defaultSubmitHandler,
        submitSuccess: defaultSubmitSuccessHandler,
        submitError: defaultSubmitErrorHandler
    };

    RemoteDialog.defaults = {
        identifier: null,
        url: null
    };

    // jshint multistr:true
    RemoteDialog.modalTemplate = '\

 \

 \

 \

 \

 \

 ';

    // jshint multistr:true
    RemoteDialog.confirmMessageTemplate = '\

 \

 \

 \

 \

 ';

    // jshint multistr:true
    RemoteDialog.confirmButtonsTemplate = '\

 \
    	<button type="button" class="btn" data-confirm-accept="true"></button> \
        <button type="button" class="btn btn-danger" data-confirm-cancel="true"></button> \

 ';

    function init() {
        var dialog = $(modalId);

        //Create the message box if it doesn't exist
        if (dialog.length === 0) {
            dialog = $(RemoteDialog.modalTemplate).appendTo(document.body);
        }

        dialog.modal({
            backdrop: 'static',
            keyboard: false,
            show: false
        });
    }

    function eventHandlers() {
        $('body').on('click', '[data-toggle="remote-dialog"]', function(event) {
            var element = $(event.target);

            RemoteDialog.Show({
                identifier: element.attr('data-identifier'),
                url: element.attr('data-url')
            });
        });

		$('body').on('click', modalId + ' .modal-footer button', handleButtonClickEvent);

        //Show/Hide events
        $('body').on('shown.bs.modal', modalId, triggerDialogEvent.bind(null, 'shown'));
        $('body').on('hidden.bs.modal', modalId, triggerDialogEvent.bind(null, 'hidden'));
    }

    RemoteDialog.Show = function(options) {
        var modal = $(modalId);
        var content = modal.find('.modal-content');
        var settings = $.extend(RemoteDialog.defaults, options);

        if (!settings.url || !settings.identifier) {
            throw "Url and Identifier must be specified";
        }

        //Set the identifier
        modal.attr('data-identifier', settings.identifier);
        content.attr('id', settings.identifier);

        //Load the conent
        RemoteDialog.handlers.load(settings.url);
    };

    function handleButtonClickEvent(event) {
        var modal = $(modalId);
        var buttonOptions = parseButtonAttributes($(event.target));

        if(buttonOptions.confirm){
        	showConfirmation(buttonOptions, modal);
        } else if(buttonOptions.confirmAccept){
        	hideConfirmation(buttonOptions, modal);
        } else if(buttonOptions.confirmCancel){
        	hideConfirmation(buttonOptions, modal);
        }
    }

	function hideConfirmation(buttonOptions, modal){
    	var body = modal.find('.modal-body');
        var footer = modal.find('.modal-footer');

        body.find('#confirmation-container').remove();
        footer.find('#confirmation-buttons').remove();
        body.find('*').show();
        footer.find('*').show();
    }

	function showConfirmation(buttonOptions, modal){
    	var body = modal.find('.modal-body');
        var footer = modal.find('.modal-footer');

		var confirmationContainer = body.find('#confirmation-message');
        var confirmationButtons = footer.find('#confirmation-buttons');

        if(confirmationContainer.length ===0){
        	body.find('*').hide();
            confirmationContainer = $(RemoteDialog.confirmMessageTemplate).prependTo(body);
        }

        if(confirmationButtons.length ===0){
        	footer.find('*').hide();
        	confirmationButtons = $(RemoteDialog.confirmButtonsTemplate).appendTo(footer);
        }

        confirmationContainer.find('#confirmation-message').html(buttonOptions.confirmMessage);
        confirmationButtons.find('button[data-confirm-cancel]').html(buttonOptions.confirmCancelText);
        confirmationButtons.find('button[data-confirm-accept]').html(buttonOptions.confirmAcceptText);

        //If the original button had a reload or submit add them to the accept button
        if(buttonOptions.submit || buttonOptions.reload){
        	confirmationButtons
            .find('button[data-confirm-accept]')
            .attr('data-submit-url', buttonOptions.submitUrl)
            .attr('data-reload-url', buttonOptions.reloadUrl)
        }
    }

    function parseButtonAttributes(button) {
        var options = {
            confirm: button.attr('data-confirm'),
            confirmMessage: button.attr('data-confirm-message'),
            confirmAccept: button.attr('data-confirm-accept'),
            confirmAcceptText: button.attr('data-confirm-accept-text'),
            confirmCancel: button.attr('data-confirm-cancel'),
            confirmCancelText: button.attr('data-confirm-cancel-text'),
            reload: button.attr('data-reload'),
            reloadUrl: button.attr('data-reload-url'),
            submit: button.attr('data-submit'),
            submitUrl: button.attr('data-submit-url')
        };

        options.confirm = (options.confirm === undefined ? true : options.confirm) && options.confirmMessage !== undefined;
        options.reload = (options.reload === undefined ? true : options.reload) && options.confirmMessage !== undefined;
        options.submit = (options.submit === undefined ? true : options.submit) && options.confirmMessage !== undefined;

        return options;
    }

    function defaultLoadHandler(url) {
        $.ajax({
            type: 'GET',
            url: url,
            success: RemoteDialog.handlers.loadSuccess,
            error: RemoteDialog.handlers.loadError
        });
    }

    function defaultLoadSuccessHandler(data) {
        var modal = $(modalId);
        var content = modal.find('.modal-content');

        content.html(data);
        triggerDialogEvent('load');
        $(modalId).modal('show');
    }

    function defaultLoadErrorHandler(jqXHR, textStatus, errorThrown) {
        triggerDialogEvent('load-error', [xhr, ajaxOptions, thrownError]);
    }

    function defaultSubmitHandler(formData, url) {
        $.ajax({
            url: url,
            data: formData,
            method: 'POST',
            success: RemoteDialog.handlers.submitSuccess,
            error: RemoteDialog.handlers.submitError
        });
    }

    function defaultSubmitSuccessHandler(data) {
        var modal = $(modalId);

        triggerDialogEvent('submit');
        modal.modal('hide');
    }

    function defaultSubmitErrorHandler(jqXHR, textStatus, errorThrown) {
        triggerDialogEvent('submit-error', [xhr, ajaxOptions, thrownError]);
    }

    function triggerDialogEvent(eventName, parameters) {
        var modal = $(modalId);
        var target = modal.find('.modal-content');
        var identifier = modal.attr('data-identifier');

        target.trigger(eventName + '.dialog', parameters);
        target.trigger(eventName + '.' + identifier, parameters);
    }

    $(function() {
        init();
        eventHandlers();
    });

})(window.RemoteDialog = window.RemoteDialog || {}, jQuery);
<|endoftext|>