Mercurial > eldonilo > lightstring
diff lightstring.js @ 108:5cb4733c5189
many api changes
author | Sonny Piers <sonny@fastmail.net> |
---|---|
date | Fri, 13 Jul 2012 15:26:18 +0200 |
parents | c06ec02217ee |
children |
line wrap: on
line diff
--- a/lightstring.js +++ b/lightstring.js @@ -16,307 +16,276 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +(function() { + define(['./jid', './stanza', './transports/websocket.js', './transports/bosh.js'], function(JID, Stanza, WebSocketTransport, BOSHTransport) { + Lightstring.JID = JID; + Lightstring.Stanza = Stanza.stanza; + Lightstring.IQ = Stanza.iq; + Lightstring.doc = Stanza.doc; + Lightstring.Presence = Stanza.presence; + Lightstring.Message = Stanza.message; + Lightstring.BOSHTransport = BOSHTransport; + Lightstring.WebSocketTransport = WebSocketTransport; + return Lightstring; + }); -var Lightstring = { - /** - * @namespace Holds XMPP namespaces. - * @description http://xmpp.org/xmpp-protocols/protocol-namespaces - */ - ns: { - streams: 'http://etherx.jabber.org/streams', - jabber_client: 'jabber:client', - xmpp_stanzas: 'urn:ietf:params:xml:ns:xmpp-stanzas' - }, - /** - * @namespace Holds XMPP stanza builders. - */ - stanzas: { - stream: { - open: function(aService) { - return "<stream:stream to='" + aService + "'" + - " xmlns='" + Lightstring.ns['jabber_client'] + "'" + - " xmlns:stream='" + Lightstring.ns['streams'] + "'" + - " version='1.0'>"; + var Lightstring = { + /* + * @namespace Holds XMPP namespaces. + * @description http://xmpp.org/xmpp-protocols/protocol-namespaces + */ + ns: { + streams: 'http://etherx.jabber.org/streams', + jabber_client: 'jabber:client', + xmpp_stanzas: 'urn:ietf:params:xml:ns:xmpp-stanzas' + }, + /** + * @namespace Holds XMPP stanza builders. + */ + stanzas: { + stream: { + open: function(aService) { + return "<stream:stream to='" + aService + "'" + + " xmlns='" + Lightstring.ns['jabber_client'] + "'" + + " xmlns:stream='" + Lightstring.ns['streams'] + "'" + + " version='1.0'>"; + }, + close: function() { + return "</stream:stream>"; + } }, - close: function() { - return "</stream:stream>"; + errors: { + iq: function(from, id, type, error) { + return "<iq to='" + from + "'" + + " id='" + id + "'" + + " type='error'>" + + "<error type='" + type + "'>" + + "<" + error + " xmlns='" + Lightstring.ns['xmpp_stanzas'] + "'/>" + //TODO: allow text content. + //TODO: allow text and payload. + "</error>" + + "</iq>"; + } } }, - errors: { - iq: function(from, id, type, error) { - return "<iq to='" + from + "'" + - " id='" + id + "'" + - " type='error'>" + - "<error type='" + type + "'>" + - "<" + error + " xmlns='" + Lightstring.ns['xmpp_stanzas'] + "'/>" + //TODO: allow text content. - //TODO: allow text and payload. - "</error>" + - "</iq>"; - } + /** + * @namespace Holds Lightstring plugins + */ + plugins: {}, + /** + * @private Holds the connections + */ + connections: [], + /** + * @function Returns a new unique identifier. + * @param {String} [aPrefix] Prefix to put before the identifier. + * @return {String} Identifier. + */ + id: function(aPrefix) { + return (aPrefix || '') + Date.now(); } - }, - /** - * @namespace Holds Lightstring plugins - */ - plugins: {}, - /** - * @private - */ - parser: new DOMParser(), - /** - * @private - */ - serializer: new XMLSerializer(), + }; + /** - * @function Transforms a XML string to a DOM object. - * @param {String} aString XML string. - * @return {Object} Domified XML. - */ - parse: function(aString) { - var el = null; - //FIXME webkit doesn't throw an error when the parsing fails - try { - el = this.parser.parseFromString(aString, 'text/xml').documentElement; - } - catch (e) { - //TODO: error - } - finally { - return el; - }; - }, - /** - * @function Transforms a DOM object to a XML string. - * @param {Object} aString DOM object. - * @return {String} Stringified DOM. - */ - serialize: function(aElement) { - var string = null; - try { - string = this.serializer.serializeToString(aElement); - } - catch (e) { - //TODO: error - } - finally { - return string; - }; - }, - /** - * @function Get an unique identifier. - * @param {String} [aString] Prefix to put before the identifier. - * @return {String} Identifier. + * @constructor Creates a new Lightstring connection + * @param {String} [aService] The connection manager URL. + * @memberOf Lightstring */ - newId: (function() { - var id = 1024; - return function(prefix) { - if (typeof prefix === 'string') - return prefix + id++; - return '' + id++; - }; - })() -}; + Lightstring.Connection = function(aService) { + if (aService) + this.service = aService; + /** + * @namespace Holds connection events handlers + */ + this.handlers = {}; + /** + * @namespace Holds connection iq callbacks + */ + this.callbacks = {}; + + Lightstring.connections.push(this); + }; + Lightstring.Connection.prototype = new EventEmitter(); + Lightstring.Connection.prototype.onTransportLoaded = function() { + this.transport.open(); + + var that = this; + + this.transport.once('open', function() { + that.emit('open'); + }); + this.transport.on('out', function(stanza) { + setTimeout(function() { + that.emit('out', stanza); + }, 0); + }); + this.transport.on('in', function(stanza) { + //FIXME: node-xmpp-bosh sends a self-closing stream:stream tag; it is wrong! + that.emit('stanza', stanza); + + if (!stanza.el) + return; + + var el = stanza.el; -/** - * @constructor Creates a new Lightstring connection - * @param {String} [aService] The connection manager URL. - * @memberOf Lightstring - */ -Lightstring.Connection = function(aService) { - var that = this; - window.addEventListener('message', function(e) { - that.send(e.data.send) - }); - if (aService) - this.service = aService; + //Authentication + //FIXME SASL mechanisms and XMPP features can be both in a stream:features + if (el.localName === 'features') { + var children = el.childNodes; + for (var i = 0, length = children.length; i < length; i++) { + //SASL mechanisms + if(children[i].localName === 'mechanisms') { + stanza.mechanisms = []; + var nodes = el.getElementsByTagName('mechanism'); + for (var i = 0; i < nodes.length; i++) + stanza.mechanisms.push(nodes[i].textContent); + that.emit('mechanisms', stanza); + return; + } + } + //XMPP features + // else { + //TODO: stanza.features + that.emit('features', stanza); + // } + } + else if (el.localName === 'challenge') { + that.emit('challenge', stanza); + } + else if (el.localName === 'failure') { + that.emit('failure', stanza); + } + else if (el.localName === 'success') { + that.emit('success', stanza); + } + + //Iq callbacks + else if (el.localName === 'iq') { + var payload = el.firstChild; + if (payload) + that.emit('iq/' + payload.namespaceURI + ':' + payload.localName, stanza); + + var id = el.getAttribute('id'); + if (!(id && id in that.callbacks)) + return; + + var type = el.getAttribute('type'); + if (type !== 'result' && type !== 'error') + return; //TODO: warning + + var callback = that.callbacks[id]; + if (type === 'result' && callback.success) + callback.success.call(that, stanza); + else if (type === 'error' && callback.error) + callback.error.call(that, stanza); + + delete that.callbacks[id]; + } + + else if (el.localName === 'presence' || el.localName === 'message') { + that.emit(name, stanza); + } + }); + }; /** - * @namespace Holds connection events handlers - */ - this.handlers = {}; - /** - * @namespace Holds connection iq callbacks + * @function Create and open a websocket then go though the XMPP authentification process. + * @param {String} [aJid] The JID (Jabber id) to use. + * @param {String} [aPassword] The associated password. */ - this.callbacks = {}; -}; -Lightstring.Connection.prototype = new EventEmitter(); -/** - * @function Create and open a websocket then go though the XMPP authentification process. - * @param {String} [aJid] The JID (Jabber id) to use. - * @param {String} [aPassword] The associated password. - */ -Lightstring.Connection.prototype.connect = function(aJid, aPassword) { - this.emit('connecting'); - this.jid = new Lightstring.JID(aJid); - if (aPassword) - this.password = aPassword; + Lightstring.Connection.prototype.connect = function(aJid, aPassword) { + this.emit('connecting'); + this.jid = new Lightstring.JID(aJid); + if (aPassword) + this.password = aPassword; - if (!this.jid.bare) - return; //TODO: error - if (!this.service) - return; //TODO: error + if (!this.jid.bare) + return; //TODO: error + if (!this.service) + return; //TODO: error + + function getProtocol(aURL) { + var a = document.createElement('a'); + a.href = aURL; + return a.protocol.replace(':', ''); + } + var protocol = getProtocol(this.service); - function getProtocol(aURL) { - var a = document.createElement('a'); - a.href = aURL; - return a.protocol.replace(':', ''); - } - var protocol = getProtocol(this.service); - - if (protocol.match('http')) - this.connection = new Lightstring.BOSHConnection(this.service, this.jid); - else if (protocol.match('ws')) - this.connection = new Lightstring.WebSocketConnection(this.service, this.jid); - - this.connection.open(); + if (protocol.match('http')) + this.transport = new Lightstring.BOSHTransport(this.service, this.jid); + else if (protocol.match('ws')) + this.transport = new Lightstring.WebSocketTransport(this.service, this.jid); - var that = this; + this.onTransportLoaded(); + }; + /** + * @function Send a message. + * @param {String|Object} aStanza The message to send. + * @param {Function} [aCallback] Executed on answer. (stanza must be iq) + */ + Lightstring.Connection.prototype.send = function(aStanza, aOnSuccess, aOnError) { + if (!(aStanza instanceof Lightstring.Stanza)) + var stanza = new Lightstring.Stanza(aStanza); + else + var stanza = aStanza; - this.connection.once('open', function() { - that.emit('open'); - }); - this.connection.on('out', function(stanza) { - that.emit('out', stanza); - }); - this.connection.on('in', function(stanza) { - //FIXME: node-xmpp-bosh sends a self-closing stream:stream tag; it is wrong! - that.emit('stanza', stanza); - - if (!stanza.el) + if (!stanza) return; - var el = stanza.el; + if (stanza.name === 'iq') { + var type = stanza.type; + if (type !== 'get' || type !== 'set') + ; //TODO: error - //Authentication - //FIXME SASL mechanisms and XMPP features can be both in a stream:features - if (el.localName === 'features') { - var children = el.childNodes; - for (var i = 0, length = children.length; i < length; i++) { - //SASL mechanisms - if(children[i].localName === 'mechanisms') { - stanza.mechanisms = []; - var nodes = el.getElementsByTagName('mechanism'); - for (var i = 0; i < nodes.length; i++) - stanza.mechanisms.push(nodes[i].textContent); - that.emit('mechanisms', stanza); - return; - } - } - //XMPP features - // else { - //TODO: stanza.features - that.emit('features', stanza); - // } - } - else if (el.localName === 'challenge') { - that.emit('challenge', stanza); - } - else if (el.localName === 'failure') { - that.emit('failure', stanza); - } - else if (el.localName === 'success') { - that.emit('success', stanza); - } + var callback = {success: aOnSuccess, error: aOnError}; - //Iq callbacks - else if (el.localName === 'iq') { - var payload = el.firstChild; - if (payload) - that.emit('iq/' + payload.namespaceURI + ':' + payload.localName, stanza); - - var id = el.getAttribute('id'); - if (!(id && id in that.callbacks)) - return; + var id = stanza.id; + if (!id) + stanza.id = Lightstring.id(); - var type = el.getAttribute('type'); - if (type !== 'result' && type !== 'error') - return; //TODO: warning - - var callback = that.callbacks[id]; - if (type === 'result' && callback.success) - callback.success.call(that, stanza); - else if (type === 'error' && callback.error) - callback.error.call(that, stanza); - - delete that.callbacks[id]; - } - - else if (el.localName === 'presence' || el.localName === 'message') { - that.emit(name, stanza); + this.callbacks[stanza.id] = callback; } - }); -}; -/** - * @function Send a message. - * @param {String|Object} aStanza The message to send. - * @param {Function} [aCallback] Executed on answer. (stanza must be iq) - */ -Lightstring.Connection.prototype.send = function(aStanza, aSuccess, aError) { - if (!(aStanza instanceof Lightstring.Stanza)) - var stanza = new Lightstring.Stanza(aStanza); - else - var stanza = aStanza; - - if (!stanza) - return; + else if (aOnSuccess || aOnError) + ; //TODO: warning (no callback without iq) - if (stanza.el.tagName === 'iq') { - var type = stanza.el.getAttribute('type'); - if (type !== 'get' || type !== 'set') - ; //TODO: error - - var callback = {success: aSuccess, error: aError}; + this.transport.send(stanza.toString()); + }; + /** + * @function Closes the XMPP stream and the socket. + */ + Lightstring.Connection.prototype.disconnect = function() { + this.emit('disconnecting'); + var stream = Lightstring.stanzas.stream.close(); + this.transport.send(stream); + this.emit('out', stream); + this.transport.close(); + }; + Lightstring.Connection.prototype.load = function() { + for (var i = 0; i < arguments.length; i++) { + var name = arguments[i]; + if (!(name in Lightstring.plugins)) + continue; //TODO: error - var id = stanza.el.getAttribute('id'); - if (!id) { - var id = Lightstring.newId('sendiq:'); - stanza.el.setAttribute('id', id); - } - - this.callbacks[id] = callback; - - } - else if (aSuccess || aError) - ; //TODO: warning (no callback without iq) + var plugin = Lightstring.plugins[name]; - this.connection.send(stanza.toString()); -}; -/** - * @function Closes the XMPP stream and the socket. - */ -Lightstring.Connection.prototype.disconnect = function() { - this.emit('disconnecting'); - var stream = Lightstring.stanzas.stream.close(); - this.socket.send(stream); - this.emit('out', stream); - this.socket.close(); -}; -Lightstring.Connection.prototype.load = function() { - for (var i = 0; i < arguments.length; i++) { - var name = arguments[i]; - if (!(name in Lightstring.plugins)) - continue; //TODO: error + //Namespaces + for (var ns in plugin.namespaces) + Lightstring.ns[ns] = plugin.namespaces[ns]; - var plugin = Lightstring.plugins[name]; + //Stanzas + Lightstring.stanzas[name] = {}; + for (var stanza in plugin.stanzas) + Lightstring.stanzas[name][stanza] = plugin.stanzas[stanza]; - //Namespaces - for (var ns in plugin.namespaces) - Lightstring.ns[ns] = plugin.namespaces[ns]; - - //Stanzas - Lightstring.stanzas[name] = {}; - for (var stanza in plugin.stanzas) - Lightstring.stanzas[name][stanza] = plugin.stanzas[stanza]; + //Handlers + for (var handler in plugin.handlers) + this.on(handler, plugin.handlers[handler]); - //Handlers - for (var handler in plugin.handlers) - this.on(handler, plugin.handlers[handler]); + //Methods + this[name] = {}; + for (var method in plugin.methods) + this[name][method] = plugin.methods[method].bind(this); - //Methods - this[name] = {}; - for (var method in plugin.methods) - this[name][method] = plugin.methods[method].bind(this); - - if (plugin.init) - plugin.init.apply(this); - } -}; \ No newline at end of file + if (plugin.init) + plugin.init.apply(this); + } + }; +})(); \ No newline at end of file