Mercurial > eldonilo > lightstring
changeset 106:c06ec02217ee
many changes
author | Sonny Piers <sonny@fastmail.net> |
---|---|
date | Tue, 26 Jun 2012 12:02:14 +0200 |
parents | fb50311997b5 |
children | 704ce44c1a22 |
files | README console/console.css console/console.js console/console.xhtml jid.js lib/EventEmitter.js lib/bind.js lib/md5.js lib/vkbeautify.0.97.00.beta.js lightstring.js plugins/ANONYMOUS.js plugins/DIGEST-MD5.js plugins/PLAIN.js plugins/dataforms.js plugins/disco.js plugins/im.js plugins/ping.js plugins/presence.js plugins/pubsub.js plugins/roster.js plugins/vcard.js stanza.js transports/bosh.js transports/websocket.js |
diffstat | 24 files changed, 739 insertions(+), 104 deletions(-) [+] |
line wrap: on
line diff
new file mode 100755 --- /dev/null +++ b/console/console.css @@ -0,0 +1,44 @@ +html, body { + width: 100%; + height: 100%; +} +body { + margin: 0; + /*FIXME: delete me*/ + overflow: hidden; +} +.entry { + width: 100%; + margin-bottom: 15px; +} +.entry pre { + margin: 0; +} +#entries { + overflow: auto; + height: -webkit-calc(100% - (30px + 100px)); + height: -moz-calc(100% - (30px + 100px)); +} +#toolbar { + width: 100%; + height: 30px; +} +#input { + height: 100px; + width: 100%; +} +#input textarea { + /*border: none;*/ + padding: 0; + resize: none; + width: -webkit-calc(100% - 80px); + width: -moz-calc(100% - 80px); +} +#input input { + width: 65px; +} +#input > * { + display: inline-block; + height: 100%; + vertical-align: top; +} \ No newline at end of file
new file mode 100755 --- /dev/null +++ b/console/console.js @@ -0,0 +1,100 @@ +'use strict'; + +var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver; + +if(!Lightstring) + var Lightstring = {}; + +Lightstring.console = { + invertedScroll: true, + log: function(aLog) { + var stanza = vkbeautify.xml(aLog.data, 2, ' '); + var entry = document.createElement('div'); + entry.classList.add('entry'); + entry.classList.add(aLog.dir); + var header = document.createElement('header'); + //FIXME date? should come from the origin? + header.textContent = aLog.dir + ': '; + entry.appendChild(header) + var pre = document.createElement('pre'); + pre.textContent = stanza; + entry.appendChild(pre); + var entriesEl = document.querySelector('#entries') + entriesEl.appendChild(entry); + }, + filter: function(aFilter) { + var entries = document.querySelectorAll('.entry pre'); + for (var i = 0, length = entries.length; i < length; i++) { + var entry = entries[i]; + if (!entry.textContent.match(aFilter)) + entry.parentNode.hidden = true; + else + entry.parentNode.hidden = false; + + //FIXME use the Mutation Observer? get back to the previous scroll state? + this.scrollToBottom(); + } + }, + send: function(aData) { + Lightstring.console.source.postMessage({ + 'send': aData}, document.location.protocol + '//' + document.location.host); + }, + scrollToBottom: function() { + this.entriesEl.scrollTop = (this.entriesEl.scrollHeight - this.entriesEl.clientHeight); + } +}; + +(function() { + document.addEventListener('DOMContentLoaded', function() { + + var entriesEl = document.getElementById('entries'); + entriesEl.addEventListener('scroll', function(e) { + if (entriesEl.scrollTop === (entriesEl.scrollHeight - entriesEl.clientHeight)) + Lightstring.console.invertedScroll = true; + else + Lightstring.console.invertedScroll = false; + }) + + new MutationObserver(function(mutations) { + if(Lightstring.console.invertedScroll === true) + Lightstring.console.scrollToBottom(); + }).observe(entriesEl, { + childList: true, + // attributes: false, + // characterData: false + }); + + Lightstring.console.entriesEl = entriesEl; + if (Lightstring.console.invertedScroll) + Lightstring.console.scrollToBottom(); + + window.addEventListener("message", function(e) { + if(!Lightstring.console.source) + Lightstring.console.source = e.source; + + Lightstring.console.log(e.data); + }, false); + + document.getElementById('input').addEventListener('submit', function(e) { + e.preventDefault(); + Lightstring.console.send(this.elements['field'].value) + }); + document.getElementById('clear').addEventListener('click', function(e) { + Lightstring.console.entriesEl.innerHTML = ''; + }); + //FIXME allow xpath, xquery, E4X, whatever XML query syntax + document.getElementById('filter').addEventListener('input', function(e) { + Lightstring.console.filter(this.value); + }); + document.getElementById('input').addEventListener('keypress', function(e) { + if (e.keyCode === 13) { + if (e.shiftKey) { + e.preventDefault(); + var event = document.createEvent('Event'); + event.initEvent('submit', true, true); + this.dispatchEvent(event); + } + } + }); + }); +})(); \ No newline at end of file
new file mode 100755 --- /dev/null +++ b/console/console.xhtml @@ -0,0 +1,21 @@ +<!DOCTYPE html> +<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> + <head> + <title>Lightstring Console</title> + <meta charset="UTF-8"/> + <script type="application/javascript" src="console.js"/> + <script type="application/javascript" src="../lib/vkbeautify.0.97.00.beta.js"/> + <link type="text/css" rel="stylesheet" href="console.css"/> + </head> + <body> + <div id="entries"/> + <div id="toolbar"> + <button id="clear">Clear</button> + <input type="search" id="filter" placeholder="Filter"/> + </div> + <form id="input"> + <textarea name="field" spellcheck="false"/> + <input type="submit" value="send"/> + </form> + </body> +</html> \ No newline at end of file
old mode 100644 new mode 100755 --- a/jid.js +++ b/jid.js @@ -28,7 +28,7 @@ Lightstring.JID = function(aJID) { this.resource = null; if (aJID) - this.full = aJID; + this.full = aJID.toString(); //TODO: use a stringprep library to validate the input. }; @@ -91,6 +91,7 @@ Lightstring.JID.prototype = { }, set full(aJID) { + if (!aJID) return;
new file mode 100755 --- /dev/null +++ b/lib/vkbeautify.0.97.00.beta.js @@ -0,0 +1,376 @@ +/** +* vkBeautify - javascript plugin to pretty-print or minify text in XML, JSON and CSS formats. +* +* Version - 0.97.00.beta +* Copyright (c) 2012 Vadim Kiryukhin +* vkiryukhin @ gmail.com +* http://www.eslinstructor.net/vkbeautify/ +* +* Dual licensed under the MIT and GPL licenses: +* http://www.opensource.org/licenses/mit-license.php +* http://www.gnu.org/licenses/gpl.html +* +* Pretty print +* +* vkbeautify.xml(text [,indent_pattern]); +* vkbeautify.json(text [,indent_pattern]); +* vkbeautify.css(text [,indent_pattern]); +* vkbeautify.sql(text [,indent_pattern]); +* +* @text - String; text to beatufy; +* @indent_pattern - Integer | String; +* Integer: number of white spaces; +* String: character string to visualize indentation ( can also be a set of white spaces ) +* Minify +* +* vkbeautify.xmlmin(text [,preserve_comments]); +* vkbeautify.jsonmin(text); +* vkbeautify.cssmin(text [,preserve_comments]); +* vkbeautify.sqlmin(text); +* +* @text - String; text to minify; +* @preserve_comments - Bool; [optional]; +* Set this flag to true to prevent removing comments from @text ( minxml and mincss functions only. ) +* +* Examples: +* vkbeautify.xml(text); // pretty print XML +* vkbeautify.json(text, 4 ); // pretty print JSON +* vkbeautify.css(text, '. . . .'); // pretty print CSS +* vkbeautify.sql(text, '----'); // pretty print SQL +* +* vkbeautify.xmlmin(text, true);// minify XML, preserve comments +* vkbeautify.jsonmin(text);// minify JSON +* vkbeautify.cssmin(text);// minify CSS, remove comments ( default ) +* vkbeautify.sqlmin(text);// minify SQL +* +*/ + +(function() { + +function createShiftArr(step) { + + var space = ' '; + + if ( isNaN(parseInt(step)) ) { // argument is string + space = step; + } else { // argument is integer + switch(step) { + case 1: space = ' '; break; + case 2: space = ' '; break; + case 3: space = ' '; break; + case 4: space = ' '; break; + case 5: space = ' '; break; + case 6: space = ' '; break; + case 7: space = ' '; break; + case 8: space = ' '; break; + case 9: space = ' '; break; + case 10: space = ' '; break; + case 11: space = ' '; break; + case 12: space = ' '; break; + } + } + + var shift = ['\n']; // array of shifts + for(ix=0;ix<100;ix++){ + shift.push(shift[ix]+space); + } + return shift; +} + +function vkbeautify(){ + this.step = ' '; // 4 spaces + this.shift = createShiftArr(this.step); +}; + +vkbeautify.prototype.xml = function(text,step) { + + var ar = text.replace(/>\s{0,}</g,"><").replace(/</g,"~::~<").split('~::~'), + len = ar.length, + inComment = false, + deep = 0, + str = '', + ix = 0, + shift = step ? createShiftArr(step) : this.shift; + + for(ix=0;ix<len;ix++) { + // start comment or <![CDATA[...]]> or <!DOCTYPE // + if(ar[ix].search(/<!/) > -1) { + str += shift[deep]+ar[ix]; + inComment = true; + // end comment or <![CDATA[...]]> // + if(ar[ix].search(/-->/) > -1 || ar[ix].search(/\]>/) > -1 || ar[ix].search(/!DOCTYPE/) > -1 ) { + inComment = false; + } + } else + // end comment or <![CDATA[...]]> // + if(ar[ix].search(/-->/) > -1 || ar[ix].search(/\]>/) > -1) { + str += ar[ix]; + inComment = false; + } else + // <elm></elm> // + if( /^<\w/.exec(ar[ix-1]) && /^<\/\w/.exec(ar[ix]) && + /^<[\w:\-\.\,]+/.exec(ar[ix-1]) == /^<\/[\w:\-\.\,]+/.exec(ar[ix])[0].replace('/','')) { + str += ar[ix]; + if(!inComment) deep--; + } else + // <elm> // + if(ar[ix].search(/<\w/) > -1 && ar[ix].search(/<\//) == -1 && ar[ix].search(/\/>/) == -1 ) { + str = !inComment ? str += shift[deep++]+ar[ix] : str += ar[ix]; + } else + // <elm>...</elm> // + if(ar[ix].search(/<\w/) > -1 && ar[ix].search(/<\//) > -1) { + str = !inComment ? str += shift[deep]+ar[ix] : str += ar[ix]; + } else + // </elm> // + if(ar[ix].search(/<\//) > -1) { + str = !inComment ? str += shift[--deep]+ar[ix] : str += ar[ix]; + } else + // <elm/> // + if(ar[ix].search(/\/>/) > -1 ) { + str = !inComment ? str += shift[deep]+ar[ix] : str += ar[ix]; + } else + // <? xml ... ?> // + if(ar[ix].search(/<\?/) > -1) { + str += shift[deep]+ar[ix]; + } else { + str += ar[ix]; + } + } + + return (str[0] == '\n') ? str.slice(1) : str; +} + +vkbeautify.prototype.json = function(text,step) { + + var ar = this.jsonmin(text).replace(/\{/g,"~::~{~::~") + .replace(/\[/g,"[~::~") + .replace(/\}/g,"~::~}") + .replace(/\]/g,"~::~]") + .replace(/\"\,/g,'",~::~') + .replace(/\,\"/g,',~::~"') + .replace(/\]\,/g,'],~::~') + .replace(/~::~\s{0,}~::~/g,"~::~") + .split('~::~'), + + len = ar.length, + deep = 0, + str = '', + ix = 0, + shift = step ? createShiftArr(step) : this.shift;; + + for(ix=0;ix<len;ix++) { + if( /\{/.exec(ar[ix])) { + str += shift[deep++]+ar[ix]; + } else + if( /\[/.exec(ar[ix])) { + str += shift[deep++]+ar[ix]; + } else + if( /\]/.exec(ar[ix])) { + str += shift[--deep]+ar[ix]; + } else + if( /\}/.exec(ar[ix])) { + str += shift[--deep]+ar[ix]; + } else { + str += shift[deep]+ar[ix]; + } + } + return str.replace(/^\n{1,}/,''); +} + +vkbeautify.prototype.css = function(text, step) { + + var ar = text.replace(/\s{1,}/g,' ') + .replace(/\{/g,"{~::~") + .replace(/\}/g,"~::~}~::~") + .replace(/\;/g,";~::~") + .replace(/\/\*/g,"~::~/*") + .replace(/\*\//g,"*/~::~") + .replace(/~::~\s{0,}~::~/g,"~::~") + .split('~::~'), + len = ar.length, + deep = 0, + str = '', + ix = 0, + shift = step ? createShiftArr(step) : this.shift; + + for(ix=0;ix<len;ix++) { + + if( /\{/.exec(ar[ix])) { + str += shift[deep++]+ar[ix]; + } else + if( /\}/.exec(ar[ix])) { + str += shift[--deep]+ar[ix]; + } else + if( /\*\\/.exec(ar[ix])) { + str += shift[deep]+ar[ix]; + } + else { + str += shift[deep]+ar[ix]; + } + } + return str.replace(/^\n{1,}/,''); +} + +//---------------------------------------------------------------------------- + +function isSubquery(str, parenthesisLevel) { + return parenthesisLevel - (str.replace(/\(/g,'').length - str.replace(/\)/g,'').length ) +} + +function split_sql(str, tab) { + + return str.replace(/\s{1,}/g," ") + + .replace(/ AND /ig,"~::~"+tab+tab+"AND ") + .replace(/ BETWEEN /ig,"~::~"+tab+"BETWEEN ") + .replace(/ CASE /ig,"~::~"+tab+"CASE ") + .replace(/ ELSE /ig,"~::~"+tab+"ELSE ") + .replace(/ END /ig,"~::~"+tab+"END ") + .replace(/ FROM /ig,"~::~FROM ") + .replace(/ GROUP\s{1,}BY/ig,"~::~GROUP BY ") + .replace(/ HAVING /ig,"~::~HAVING ") + //.replace(/ IN /ig,"~::~"+tab+"IN ") + .replace(/ IN /ig," IN ") + + .replace(/ JOIN /ig,"~::~JOIN ") + .replace(/ CROSS~::~{1,}JOIN /ig,"~::~CROSS JOIN ") + .replace(/ INNER~::~{1,}JOIN /ig,"~::~INNER JOIN ") + .replace(/ LEFT~::~{1,}JOIN /ig,"~::~LEFT JOIN ") + .replace(/ RIGHT~::~{1,}JOIN /ig,"~::~RIGHT JOIN ") + + .replace(/ ON /ig,"~::~"+tab+"ON ") + .replace(/ OR /ig,"~::~"+tab+tab+"OR ") + .replace(/ ORDER\s{1,}BY/ig,"~::~ORDER BY ") + .replace(/ OVER /ig,"~::~"+tab+"OVER ") + + .replace(/\(\s{0,}SELECT /ig,"~::~(SELECT ") + .replace(/\)\s{0,}SELECT /ig,")~::~SELECT ") + + .replace(/ THEN /ig," THEN~::~"+tab+"") + .replace(/ UNION /ig,"~::~UNION~::~") + .replace(/ USING /ig,"~::~USING ") + .replace(/ WHEN /ig,"~::~"+tab+"WHEN ") + .replace(/ WHERE /ig,"~::~WHERE ") + .replace(/ WITH /ig,"~::~WITH ") + + //.replace(/\,\s{0,}\(/ig,",~::~( ") + //.replace(/\,/ig,",~::~"+tab+tab+"") + + .replace(/ ALL /ig," ALL ") + .replace(/ AS /ig," AS ") + .replace(/ ASC /ig," ASC ") + .replace(/ DESC /ig," DESC ") + .replace(/ DISTINCT /ig," DISTINCT ") + .replace(/ EXISTS /ig," EXISTS ") + .replace(/ NOT /ig," NOT ") + .replace(/ NULL /ig," NULL ") + .replace(/ LIKE /ig," LIKE ") + .replace(/\s{0,}SELECT /ig,"SELECT ") + + .replace(/~::~{1,}/g,"~::~") + .split('~::~'); +} + +vkbeautify.prototype.sql = function(text,step) { + + var ar_by_quote = text.replace(/\s{1,}/g," ") + .replace(/\'/ig,"~::~\'") + .split('~::~'), + len = ar_by_quote.length, + ar = [], + deep = 0, + tab = this.step,//+this.step, + inComment = true, + inQuote = false, + parenthesisLevel = 0, + str = '', + ix = 0, + shift = step ? createShiftArr(step) : this.shift;; + + for(ix=0;ix<len;ix++) { + if(ix%2) { + ar = ar.concat(ar_by_quote[ix]); + } else { + ar = ar.concat(split_sql(ar_by_quote[ix], tab) ); + } + } + + len = ar.length; + for(ix=0;ix<len;ix++) { + + parenthesisLevel = isSubquery(ar[ix], parenthesisLevel); + + if( /\s{0,}\s{0,}SELECT\s{0,}/.exec(ar[ix])) { + ar[ix] = ar[ix].replace(/\,/g,",\n"+tab+tab+"") + } + + if( /\s{0,}\(\s{0,}SELECT\s{0,}/.exec(ar[ix])) { + deep++; + str += shift[deep]+ar[ix]; + } else + if( /\'/.exec(ar[ix]) ) { + if(parenthesisLevel<1 && deep) { + deep--; + } + str += ar[ix]; + } + else { + str += shift[deep]+ar[ix]; + if(parenthesisLevel<1 && deep) { + deep--; + } + } + var junk = 0; + } + + str = str.replace(/^\n{1,}/,'').replace(/\n{1,}/g,"\n"); + return str; +} + + +vkbeautify.prototype.xmlmin = function(text, preserveComments) { + + var str = preserveComments ? text + : text.replace(/\<![ \r\n\t]*(--([^\-]|[\r\n]|-[^\-])*--[ \r\n\t]*)\>/g,""); + return str.replace(/>\s{0,}</g,"><"); +} + +vkbeautify.prototype.jsonmin = function(text) { + + return text.replace(/\s{0,}\{\s{0,}/g,"{") + .replace(/\s{0,}\[$/g,"[") + .replace(/\[\s{0,}/g,"[") + .replace(/:\s{0,}\[/g,':[') + .replace(/\s{0,}\}\s{0,}/g,"}") + .replace(/\s{0,}\]\s{0,}/g,"]") + .replace(/\"\s{0,}\,/g,'",') + .replace(/\,\s{0,}\"/g,',"') + .replace(/\"\s{0,}:/g,'":') + .replace(/:\s{0,}\"/g,':"') + .replace(/:\s{0,}\[/g,':[') + .replace(/\,\s{0,}\[/g,',[') + .replace(/\,\s{2,}/g,', ') + .replace(/\]\s{0,},\s{0,}\[/g,'],['); +} + +vkbeautify.prototype.cssmin = function(text, preserveComments) { + + var str = preserveComments ? text + : text.replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\//g,"") ; + + return str.replace(/\s{1,}/g,' ') + .replace(/\{\s{1,}/g,"{") + .replace(/\}\s{1,}/g,"}") + .replace(/\;\s{1,}/g,";") + .replace(/\/\*\s{1,}/g,"/*") + .replace(/\*\/\s{1,}/g,"*/"); +} + +vkbeautify.prototype.sqlmin = function(text) { + return text.replace(/\s{1,}/g," ").replace(/\s{1,}\(/,"(").replace(/\s{1,}\)/,")"); +} + +window.vkbeautify = new vkbeautify(); + +})(); +
old mode 100644 new mode 100755 --- a/lightstring.js +++ b/lightstring.js @@ -72,16 +72,17 @@ var Lightstring = { * @param {String} aString XML string. * @return {Object} Domified XML. */ - XML2DOM: function(aString) { - var DOM = null; + parse: function(aString) { + var el = null; + //FIXME webkit doesn't throw an error when the parsing fails try { - DOM = this.parser.parseFromString(aString, 'text/xml').documentElement; + el = this.parser.parseFromString(aString, 'text/xml').documentElement; } catch (e) { //TODO: error } finally { - return DOM; + return el; }; }, /** @@ -89,16 +90,16 @@ var Lightstring = { * @param {Object} aString DOM object. * @return {String} Stringified DOM. */ - DOM2XML: function(aElement) { - var XML = null; + serialize: function(aElement) { + var string = null; try { - XML = this.serializer.serializeToString(aElement); + string = this.serializer.serializeToString(aElement); } catch (e) { //TODO: error } finally { - return XML; + return string; }; }, /** @@ -122,6 +123,10 @@ var Lightstring = { * @memberOf Lightstring */ Lightstring.Connection = function(aService) { + var that = this; + window.addEventListener('message', function(e) { + that.send(e.data.send) + }); if (aService) this.service = aService; /** @@ -158,9 +163,9 @@ Lightstring.Connection.prototype.connect var protocol = getProtocol(this.service); if (protocol.match('http')) - this.connection = new Lightstring.BOSHConnection(this.service); + this.connection = new Lightstring.BOSHConnection(this.service, this.jid); else if (protocol.match('ws')) - this.connection = new Lightstring.WebSocketConnection(this.service); + this.connection = new Lightstring.WebSocketConnection(this.service, this.jid); this.connection.open(); @@ -173,54 +178,56 @@ Lightstring.Connection.prototype.connect that.emit('out', stanza); }); this.connection.on('in', function(stanza) { - var stanza = new Lightstring.Stanza(stanza); - //FIXME: node-xmpp-bosh sends a self-closing stream:stream tag; it is wrong! that.emit('stanza', stanza); - if (!stanza.DOM) + if (!stanza.el) return; - var name = stanza.DOM.localName; + var el = stanza.el; //Authentication //FIXME SASL mechanisms and XMPP features can be both in a stream:features - if (name === 'features') { - //SASL mechanisms - if (stanza.DOM.firstChild.localName === 'mechanisms') { - stanza.mechanisms = []; - var nodes = stanza.DOM.getElementsByTagName('mechanism'); - for (var i = 0; i < nodes.length; i++) - stanza.mechanisms.push(nodes[i].textContent); - that.emit('mechanisms', stanza); + 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 { + // else { //TODO: stanza.features that.emit('features', stanza); - } + // } } - else if (name === 'challenge') { + else if (el.localName === 'challenge') { that.emit('challenge', stanza); } - else if (name === 'failure') { + else if (el.localName === 'failure') { that.emit('failure', stanza); } - else if (name === 'success') { + else if (el.localName === 'success') { that.emit('success', stanza); } //Iq callbacks - else if (name === 'iq') { - var payload = stanza.DOM.firstChild; + else if (el.localName === 'iq') { + var payload = el.firstChild; if (payload) that.emit('iq/' + payload.namespaceURI + ':' + payload.localName, stanza); - var id = stanza.DOM.getAttribute('id'); + var id = el.getAttribute('id'); if (!(id && id in that.callbacks)) return; - var type = stanza.DOM.getAttribute('type'); + var type = el.getAttribute('type'); if (type !== 'result' && type !== 'error') return; //TODO: warning @@ -233,7 +240,7 @@ Lightstring.Connection.prototype.connect delete that.callbacks[id]; } - else if (name === 'presence' || name === 'message') { + else if (el.localName === 'presence' || el.localName === 'message') { that.emit(name, stanza); } }); @@ -252,17 +259,17 @@ Lightstring.Connection.prototype.send = if (!stanza) return; - if (stanza.DOM.tagName === 'iq') { - var type = stanza.DOM.getAttribute('type'); + if (stanza.el.tagName === 'iq') { + var type = stanza.el.getAttribute('type'); if (type !== 'get' || type !== 'set') ; //TODO: error var callback = {success: aSuccess, error: aError}; - var id = stanza.DOM.getAttribute('id'); + var id = stanza.el.getAttribute('id'); if (!id) { var id = Lightstring.newId('sendiq:'); - stanza.DOM.setAttribute('id', id); + stanza.el.setAttribute('id', id); } this.callbacks[id] = callback; @@ -271,12 +278,7 @@ Lightstring.Connection.prototype.send = else if (aSuccess || aError) ; //TODO: warning (no callback without iq) - - //FIXME this.socket.send(stanza.XML); (need some work on Lightstring.Stanza) - var fixme = Lightstring.DOM2XML(stanza.DOM); - stanza.XML = fixme; - this.connection.send(fixme); - this.emit('output', stanza); + this.connection.send(stanza.toString()); }; /** * @function Closes the XMPP stream and the socket. @@ -285,7 +287,7 @@ Lightstring.Connection.prototype.disconn this.emit('disconnecting'); var stream = Lightstring.stanzas.stream.close(); this.socket.send(stream); - this.emit('XMLOutput', stream); + this.emit('out', stream); this.socket.close(); }; Lightstring.Connection.prototype.load = function() {
old mode 100644 new mode 100755 --- a/plugins/PLAIN.js +++ b/plugins/PLAIN.js @@ -66,7 +66,7 @@ Lightstring.plugins['PLAIN'] = { //Success function(stanza) { //Session http://xmpp.org/rfcs/rfc3921.html#session - Conn.jid = new Lightstring.JID(stanza.DOM.textContent); + Conn.jid = new Lightstring.JID(stanza.el.textContent); Conn.send( "<iq type='set' id='"+Lightstring.newId('sendiq:')+"'>" + "<session xmlns='urn:ietf:params:xml:ns:xmpp-session'/>" +
old mode 100644 new mode 100755 --- a/plugins/im.js +++ b/plugins/im.js @@ -19,13 +19,47 @@ ////// //IM// ////// -Lightstring.plugins['message'] = { +Lightstring.plugins['im'] = { stanzas: { - normalMessage: function(aTo, aSubject, aText) { - return "<message type='normal' to='" + aTo + "'><subject>" + aSubject + "</subject><body>" + aText + "</body></message>"; + normal: function(aTo, aSubject, aText) { + return( + "<message type='normal' to='" + aTo + "'>" + + "<subject>" + aSubject + "</subject>" + + "<body>" + aText + "</body>" + + "</message>" + ); }, - chatMessage: function(aTo, aText) { - return "<message type='chat' to='" + aTo + "'><body>" + aText + "</body></message>"; - } + chat: function(aTo, aText, aReceipt) { + var message = Lightstring.parse( + "<message type='chat' to='" + aTo + "'>" + + "<body>" + aText + "</body>" + + "</message>" + ); + + if (aReceipt) { + var receipt = document.createElement('request'); + receipt.setAttribute('xmlns', 'urn:xmpp:receipts'); + message.appendChild(receipt); + message.setAttribute('id', Lightstring.newId()); + } + + return message; + }, + received: function(aTo, aId) { + var message = Lightstring.parse( + "<message to='" + aTo + "'>" + + "<received xmlns='urn:xmpp:receipts' id='" + aId + "'/>" + + "</message>" + ); + return message; + }, + read: function(aTo, aId) { + var message = Lightstring.parse( + "<message to='" + aTo + "'>" + + "<read xmlns='urn:xmpp:receipts' id='" + aId + "'/>" + + "</message>" + ); + return message; + }, } };
new file mode 100644 --- /dev/null +++ b/plugins/ping.js @@ -0,0 +1,51 @@ +'use strict'; + +/** + Copyright (c) 2012, Sonny Piers <sonny at fastmail dot net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/* +References: + http://xmpp.org/extensions/xep-0199.html +*/ + +Lightstring.plugins['ping'] = { + namespaces: { + ping: 'urn:xmpp:ping' + }, + stanzas: { + ping: function (aTo){ + return( + "<iq to='" + aTo + "' type='get'>" + + "<ping xmlns='" + Lightstring.ns.ping + "'/>" + + "</iq>" + ); + }, + pong: function (aTo){ + return "<iq to='" + aTo + "' type='result'/>" + }, + }, + handlers: { + 'iq': function (stanza) { + if (stanza.el.firstChild.localName !== 'ping') + return; + + var id = stanza.el.getAttribute('id'); + var from = stanza.el.getAttribute('from'); + var stanza = Lightstring.stanzas.pong(from); + stanza.setAttribute('id', id); + } + } +}
old mode 100644 new mode 100755 --- a/plugins/presence.js +++ b/plugins/presence.js @@ -21,7 +21,15 @@ //Presence// http://xmpp.org/rfcs/rfc6121.html#presence //////////// (function() { - var legal_types = ['error', 'probe', 'subscribe', 'subscribed', 'unavailable', 'unsubscribe', 'unsubscribed']; + var legal_types = [ + 'error', + 'probe', + 'subscribe', + 'subscribed', + 'unavailable', + 'unsubscribe', + 'unsubscribed' + ]; Lightstring.plugins['presence'] = { stanzas: {
old mode 100644 new mode 100755 --- a/plugins/pubsub.js +++ b/plugins/pubsub.js @@ -44,7 +44,13 @@ return stanza + "</items></pubsub></iq>"; }, affiliations: function(aTo, aNode) { - return "<iq type='get' to='" + aTo + "'><pubsub xmlns='" + Lightstring.ns.pubsub_owner + "'><affiliations node='" + aNode + "'/></pubsub></iq>"; + return( + "<iq type='get' to='" + aTo + "'>" + + "<pubsub xmlns='" + Lightstring.ns.pubsub_owner + "'>" + + "<affiliations node='" + aNode + "'/>" + + "</pubsub>" + + "</iq>" + ); }, publish: function(aTo, aNode, aItem, aId) { return "<iq type='set' to='" + aTo + "'><pubsub xmlns='" + Lightstring.ns.pubsub + "'><publish node='" + aNode + "'><item id='" + aId + "'>" + aItem + "</item></publish></pubsub></iq>";
old mode 100644 new mode 100755 --- a/plugins/roster.js +++ b/plugins/roster.js @@ -51,11 +51,11 @@ Lightstring.plugins['roster'] = { } }, methods: { - 'get': function(aSuccess, aError) { + 'get': function(aOnSuccess, aOnError) { this.send(Lightstring.stanzas.roster.get(), function(stanza) { var contacts = []; - var items = stanza.DOM.getElementsByTagName('item'); + var items = stanza.el.getElementsByTagName('item'); for (var i = 0; i < items.length; i++) { var item = items[i]; @@ -87,9 +87,9 @@ Lightstring.plugins['roster'] = { contacts: contacts }; - if (aSuccess) - aSuccess(stanza); - }, aError); + if (aOnSuccess) + aOnSuccess(stanza); + }, aOnError); } } };
old mode 100644 new mode 100755 --- a/stanza.js +++ b/stanza.js @@ -23,16 +23,13 @@ * @memberOf Lightstring */ Lightstring.Stanza = function(aStanza) { - if (typeof aStanza === 'string') { - this.XML = aStanza; - this.DOM = Lightstring.XML2DOM(this.XML); - } - else if (aStanza instanceof Element) { - this.DOM = aStanza; - this.XML = Lightstring.DOM2XML(this.DOM); - } - //ToDo error ? - else { - return null; - } + if (typeof aStanza === 'string') + this.el = Lightstring.parse(aStanza); + else if (aStanza instanceof Element) + this.el = aStanza; + else + this.el = null;//TODO error }; +Lightstring.Stanza.prototype.toString = function() { + return Lightstring.serialize(this.el); +}; \ No newline at end of file
old mode 100644 new mode 100755 --- a/transports/bosh.js +++ b/transports/bosh.js @@ -54,20 +54,19 @@ // } // } - var body = '<body rid="' + this.rid++ + '" xmlns="http://jabber.org/protocol/httpbind"/>'; - var body = Lightstring.XML2DOM(body); + var body = new Lightstring.Stanza('<body rid="' + this.rid++ + '" xmlns="http://jabber.org/protocol/httpbind"/>'); //sid if (this.sid) - body.setAttribute('sid', this.sid); + body.el.setAttribute('sid', this.sid); //attributes on body for (var i in attrs) - body.setAttribute(i, attrs[i]); + body.el.setAttribute(i, attrs[i]); //children for (var i in children) - body.appendChild(children[i]); + body.el.appendChild(children[i]); @@ -99,7 +98,7 @@ var body = this.response; that.emit('rawin', body); - var bodyEl = Lightstring.XML2DOM(body); + var bodyEl = Lightstring.parse(body); that.processResponse(bodyEl) if (aOnSuccess) aOnSuccess(bodyEl); @@ -117,14 +116,15 @@ // } // }); // this.emit('rawout', body.toString()); + if (body.children) { + for(var i = 0; i < body.children.length; i++) { + var child = body.children[i]; + that.emit('out', child); + } + } + this.emit('rawout', body); - for(var i = 0; i < body.children.length; i++) { - var child = body.children[i]; - that.emit('out', child); - } - this.emit('rawout', Lightstring.DOM2XML(body)) - - req.send(Lightstring.DOM2XML(body)); + req.send(Lightstring.serialize(body)); this.currentRequests++; }; Lightstring.BOSHConnection.prototype.send = function(aData) { @@ -134,7 +134,7 @@ else if(typeof aData == 'string') { try { - var el = Lightstring.XML2DOM(aData); + var el = Lightstring.parse(aData); } catch(e) { console.log(e);
old mode 100644 new mode 100755 --- a/transports/websocket.js +++ b/transports/websocket.js @@ -1,24 +1,18 @@ 'use strict'; (function() { - Lightstring.WebSocketConnection = function(aService) { + Lightstring.WebSocket = WebSocket || MozWebSocket || undefined; + Lightstring.WebSocketConnection = function(aService, aJid) { this.service = aService; + this.jid = aJid; }; Lightstring.WebSocketConnection.prototype = new EventEmitter(); Lightstring.WebSocketConnection.prototype.open = function() { - // Standard - if (typeof(WebSocket) === 'function') - this.socket = new WebSocket(this.service, 'xmpp'); - // Safari - else if (typeof(WebSocket) === 'object') - this.socket = new WebSocket(this.service, 'xmpp'); - // Old Gecko - else if (typeof(MozWebSocket) === 'function') - this.socket = new MozWebSocket(this.service, 'xmpp'); - // No WebSocket support - else + if(!Lightstring.WebSocket) return; //TODO: error + this.socket = new WebSocket(this.service, 'xmpp'); + var that = this; this.socket.addEventListener('open', function() { //FIXME: Opera/Safari WebSocket implementation doesn't support sub-protocol mechanism. @@ -27,11 +21,11 @@ that.emit('open'); var stream = Lightstring.stanzas.stream.open(that.jid.domain); - this.socket.send(stream); - var stanza = { - XML: stream - }; - that.emit('out', stanza); + var stanza = new Lightstring.Stanza(); + stanza.toString = function() { + return stream; + } + that.send(stanza); }); this.socket.addEventListener('error', function(e) { that.emit('disconnecting', e.data); @@ -41,11 +35,12 @@ that.emit('disconnected', e.data); }); this.socket.addEventListener('message', function(e) { - that.emit('in', e.data); + var stanza = new Lightstring.Stanza(e.data); + that.emit('in', stanza); }); }; Lightstring.WebSocketConnection.prototype.send = function(aStanza) { - this.socket.send(aStanza); - that.emit('out', aStanza); + this.emit('out', aStanza); + this.socket.send(aStanza.toString()); }; })(); \ No newline at end of file