c3d2-web/content/static/candy/src/core.js

416 lines
12 KiB
JavaScript

/** 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 ejabberd, 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');
_addNamespace('PUBSUB', 'http://jabber.org/protocol/pubsub');
},
_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) {
if(typeof window.console !== undefined && typeof window.console.log !== undefined) {
// Strophe has a polyfill for bind which doesn't work in IE8.
if(Function.prototype.bind && Candy.Util.getIeVersion() > 8) {
self.log = Function.prototype.bind.call(console.log, console);
} else {
self.log = function() {
Function.prototype.apply.call(console.log, console, arguments);
};
}
}
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));