Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Initial content

  • Loading branch information...
commit e8641028ccabec943898a0f344ae8706401f7191 1 parent 0a4f5ec
@prefiks prefiks authored
View
21 Makefile
@@ -0,0 +1,21 @@
+all: p1pp.js
+
+JS_FILES=\
+ src/p1pp_defs.js \
+ src/flash-websocket/swfobject.js \
+ src/flash-websocket/web_socket.js \
+ src/strophe/strophe.js \
+ src/strophe/strophe.bosh.js \
+ src/strophe/strophe.pubsub.js \
+ src/strophe/strophe.roster.js \
+ src/strophe/strophe.websocket.js \
+ src/p1pp.js
+
+GOOGLE_CC = compiler.jar
+JAVA = java
+
+p1pp.js: $(JS_FILES)
+ @cat $(JS_FILES) > p1pp.js
+
+p1pp.js.min: p1pp.js
+ @$(JAVA) -jar $(GOOGLE_CC) p1pp.js >p1pp.js.min
View
BIN  WebSocketMain.swf
Binary file not shown
View
6,519 p1pp.js
6,519 additions, 0 deletions not shown
View
163 p1pp.js.min
@@ -0,0 +1,163 @@
+var WEB_SOCKET_DEBUG=!1,WEB_SOCKET_DISABLE_AUTO_INITIALIZATION=!0,P1PP=function(a){this.timeout_id=this.params=this.connection=this.password=this.jid=null;this.retries=0;this.closing=!1;this.defaults={flash_location:"WebSocketMain.swf",domain:"p1pp.net",ws_url:"ws://p1pp.net:5280/xmpp",bosh_url:"ws://p1pp.net:5280/http-bind",connect_timeout:15E3,connect_delay:0,connect_retry:10,rebind:!0,debug:!1,num_old:0,on_strophe_event:function(){},on_login_required:null,publish:function(){},retract:function(){},
+on_disconnected:function(){},on_connected:function(){},cookie_opts:{},nodes:[]};this.params=function(a,c){for(var d in c)c.hasOwnProperty(d)&&(a[d]=c[d]);return a}(this.defaults,a);this.params.pubsub_domain||(this.params.pubsub_domain="pubsub."+this.params.domain);a=this.params.nodes;0<a.length&&(this.scope=MD5.hexdigest(a.join("-")));!window.console||!window.console.log||!window.console.error?this.console={log:function(){},error:function(){}}:this.params.debug&&(this.console=window.console);this.params.debug&&
+(window.WEB_SOCKET_DEBUG=!0);window.WEB_SOCKET_SWF_LOCATION=this.params.flash_location;window.WebSocket&&window.WebSocket.__initialize&&window.WebSocket.__initialize();return this};P1PP.connect=function(a){this.push_client?!1==this.push_client.connection.connected&&this.push_client.connect():(this.push_client=new P1PP(a),this.push_client.connect());return this.push_client};P1PP.disconnect=function(){this.push_client.disconnect()};
+P1PP.addChannel=function(a){if(this.push_client){"string"===typeof a&&(a=[a]);var b=this.push_client.params.nodes;this.merge(b,a);0<b.length&&(this.push_client.scope=MD5.hexdigest(b.join("-")));this.push_client.subscribe(a)}};P1PP.removeChannel=function(a){if(this.push_client){"string"===typeof a&&(a=[a]);var b=P1PP.diff(this.push_client.params.nodes,channel);0<b.length&&(this.push_client.scope=MD5.hexdigest(b.join("-")));this.push_client.unsubscribe(a)}};
+P1PP.publish=function(a,b,c,d){return this.push_client?this.push_client._publish(a,b,c,d):null};P1PP.deleteNode=function(a,b){return this.push_client?this.push_client._deleteNode(a,b):null};P1PP.COOKIE="session";P1PP.couldRebind=function(){var a=["WEBSOCKET","BOSH"];for(p in a)return a=P1PP.COOKIE+"_"+a[p],window.sessionStorage?!!sessionStorage[a]:!!this.cookie(a)};
+P1PP.merge=function(a,b){var c=a.length,d=0;if("number"===typeof b.length)for(var e=b.length;d<e;d++)a[c++]=b[d];else for(;void 0!==b[d];)a[c++]=b[d++];a.length=c;return a};P1PP.diff=function(a,b){return a.filter(function(a){return!(-1<b.indexOf(a))})};
+var swfobject=function(){function a(){if(!A){try{var a=m.getElementsByTagName("body")[0].appendChild(m.createElement("span"));a.parentNode.removeChild(a)}catch(b){return}A=!0;for(var a=E.length,c=0;c<a;c++)E[c]()}}function b(a){A?a():E[E.length]=a}function c(a){if(typeof w.addEventListener!=q)w.addEventListener("load",a,!1);else if(typeof m.addEventListener!=q)m.addEventListener("load",a,!1);else if(typeof w.attachEvent!=q)o(w,"onload",a);else if("function"==typeof w.onload){var b=w.onload;w.onload=
+function(){b();a()}}else w.onload=a}function d(){var a=m.getElementsByTagName("body")[0],b=m.createElement(v);b.setAttribute("type",B);var c=a.appendChild(b);if(c){var g=0;(function(){if(typeof c.GetVariable!=q){var d=c.GetVariable("$version");d&&(d=d.split(" ")[1].split(","),l.pv=[parseInt(d[0],10),parseInt(d[1],10),parseInt(d[2],10)])}else if(10>g){g++;setTimeout(arguments.callee,10);return}a.removeChild(b);c=null;e()})()}else e()}function e(){var a=y.length;if(0<a)for(var b=0;b<a;b++){var c=y[b].id,
+d=y[b].callbackFn,e={success:!1,id:c};if(0<l.pv[0]){var o=s(c);if(o)if(F(y[b].swfVersion)&&!(l.wk&&312>l.wk))i(c,!0),d&&(e.success=!0,e.ref=f(c),d(e));else if(y[b].expressInstall&&g()){e={};e.data=y[b].expressInstall;e.width=o.getAttribute("width")||"0";e.height=o.getAttribute("height")||"0";o.getAttribute("class")&&(e.styleclass=o.getAttribute("class"));o.getAttribute("align")&&(e.align=o.getAttribute("align"));for(var t={},o=o.getElementsByTagName("param"),h=o.length,j=0;j<h;j++)"movie"!=o[j].getAttribute("name").toLowerCase()&&
+(t[o[j].getAttribute("name")]=o[j].getAttribute("value"));k(e,t,c,d)}else n(o),d&&d(e)}else if(i(c,!0),d){if((c=f(c))&&typeof c.SetVariable!=q)e.success=!0,e.ref=c;d(e)}}}function f(a){var b=null;if((a=s(a))&&"OBJECT"==a.nodeName)typeof a.SetVariable!=q?b=a:(a=a.getElementsByTagName(v)[0])&&(b=a);return b}function g(){return!G&&F("6.0.65")&&(l.win||l.mac)&&!(l.wk&&312>l.wk)}function k(a,b,c,g){G=!0;J=g||null;L={success:!1,id:c};var d=s(c);if(d){"OBJECT"==d.nodeName?(D=r(d),H=null):(D=d,H=c);a.id=
+j;if(typeof a.width==q||!/%$/.test(a.width)&&310>parseInt(a.width,10))a.width="310";if(typeof a.height==q||!/%$/.test(a.height)&&137>parseInt(a.height,10))a.height="137";m.title=m.title.slice(0,47)+" - Flash Player Installation";g=l.ie&&l.win?"ActiveX":"PlugIn";g="MMredirectURL="+w.location.toString().replace(/&/g,"%26")+"&MMplayerType="+g+"&MMdoctitle="+m.title;b.flashvars=typeof b.flashvars!=q?b.flashvars+("&"+g):g;l.ie&&l.win&&4!=d.readyState&&(g=m.createElement("div"),c+="SWFObjectNew",g.setAttribute("id",
+c),d.parentNode.insertBefore(g,d),d.style.display="none",function(){4==d.readyState?d.parentNode.removeChild(d):setTimeout(arguments.callee,10)}());u(a,b,c)}}function n(a){if(l.ie&&l.win&&4!=a.readyState){var b=m.createElement("div");a.parentNode.insertBefore(b,a);b.parentNode.replaceChild(r(a),b);a.style.display="none";(function(){4==a.readyState?a.parentNode.removeChild(a):setTimeout(arguments.callee,10)})()}else a.parentNode.replaceChild(r(a),a)}function r(a){var b=m.createElement("div");if(l.win&&
+l.ie)b.innerHTML=a.innerHTML;else if(a=a.getElementsByTagName(v)[0])if(a=a.childNodes)for(var c=a.length,g=0;g<c;g++)!(1==a[g].nodeType&&"PARAM"==a[g].nodeName)&&8!=a[g].nodeType&&b.appendChild(a[g].cloneNode(!0));return b}function u(a,b,c){var g,d=s(c);if(l.wk&&312>l.wk)return g;if(d)if(typeof a.id==q&&(a.id=c),l.ie&&l.win){var f="",e;for(e in a)a[e]!=Object.prototype[e]&&("data"==e.toLowerCase()?b.movie=a[e]:"styleclass"==e.toLowerCase()?f+=' class="'+a[e]+'"':"classid"!=e.toLowerCase()&&(f+=" "+
+e+'="'+a[e]+'"'));e="";for(var o in b)b[o]!=Object.prototype[o]&&(e+='<param name="'+o+'" value="'+b[o]+'" />');d.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+f+">"+e+"</object>";I[I.length]=a.id;g=s(a.id)}else{o=m.createElement(v);o.setAttribute("type",B);for(var k in a)a[k]!=Object.prototype[k]&&("styleclass"==k.toLowerCase()?o.setAttribute("class",a[k]):"classid"!=k.toLowerCase()&&o.setAttribute(k,a[k]));for(f in b)b[f]!=Object.prototype[f]&&"movie"!=f.toLowerCase()&&
+(a=o,e=f,k=b[f],c=m.createElement("param"),c.setAttribute("name",e),c.setAttribute("value",k),a.appendChild(c));d.parentNode.replaceChild(o,d);g=o}return g}function t(a){var b=s(a);b&&"OBJECT"==b.nodeName&&(l.ie&&l.win?(b.style.display="none",function(){if(4==b.readyState){var c=s(a);if(c){for(var g in c)"function"==typeof c[g]&&(c[g]=null);c.parentNode.removeChild(c)}}else setTimeout(arguments.callee,10)}()):b.parentNode.removeChild(b))}function s(a){var b=null;try{b=m.getElementById(a)}catch(c){}return b}
+function o(a,b,c){a.attachEvent(b,c);C[C.length]=[a,b,c]}function F(a){var b=l.pv,a=a.split(".");a[0]=parseInt(a[0],10);a[1]=parseInt(a[1],10)||0;a[2]=parseInt(a[2],10)||0;return b[0]>a[0]||b[0]==a[0]&&b[1]>a[1]||b[0]==a[0]&&b[1]==a[1]&&b[2]>=a[2]?!0:!1}function h(a,b,c,g){if(!l.ie||!l.mac){var d=m.getElementsByTagName("head")[0];if(d){c=c&&"string"==typeof c?c:"screen";g&&(K=x=null);if(!x||K!=c)g=m.createElement("style"),g.setAttribute("type","text/css"),g.setAttribute("media",c),x=d.appendChild(g),
+l.ie&&l.win&&typeof m.styleSheets!=q&&0<m.styleSheets.length&&(x=m.styleSheets[m.styleSheets.length-1]),K=c;l.ie&&l.win?x&&typeof x.addRule==v&&x.addRule(a,b):x&&typeof m.createTextNode!=q&&x.appendChild(m.createTextNode(a+" {"+b+"}"))}}}function i(a,b){if(M){var c=b?"visible":"hidden";A&&s(a)?s(a).style.visibility=c:h("#"+a,"visibility:"+c)}}function N(a){return null!=/[\\\"<>\.;]/.exec(a)&&typeof encodeURIComponent!=q?encodeURIComponent(a):a}var q="undefined",v="object",B="application/x-shockwave-flash",
+j="SWFObjectExprInst",w=window,m=document,z=navigator,O=!1,E=[function(){O?d():e()}],y=[],I=[],C=[],D,H,J,L,A=!1,G=!1,x,K,M=!0,l=function(){var a=typeof m.getElementById!=q&&typeof m.getElementsByTagName!=q&&typeof m.createElement!=q,b=z.userAgent.toLowerCase(),c=z.platform.toLowerCase(),g=c?/win/.test(c):/win/.test(b),c=c?/mac/.test(c):/mac/.test(b),b=/webkit/.test(b)?parseFloat(b.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):!1,d=!+"\v1",e=[0,0,0],f=null;if(typeof z.plugins!=q&&typeof z.plugins["Shockwave Flash"]==
+v){if((f=z.plugins["Shockwave Flash"].description)&&!(typeof z.mimeTypes!=q&&z.mimeTypes[B]&&!z.mimeTypes[B].enabledPlugin))O=!0,d=!1,f=f.replace(/^.*\s+(\S+\s+\S+$)/,"$1"),e[0]=parseInt(f.replace(/^(.*)\..*$/,"$1"),10),e[1]=parseInt(f.replace(/^.*\.(.*)\s.*$/,"$1"),10),e[2]=/[a-zA-Z]/.test(f)?parseInt(f.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}else if(typeof w.ActiveXObject!=q)try{var o=new ActiveXObject("ShockwaveFlash.ShockwaveFlash");if(o&&(f=o.GetVariable("$version")))d=!0,f=f.split(" ")[1].split(","),
+e=[parseInt(f[0],10),parseInt(f[1],10),parseInt(f[2],10)]}catch(k){}return{w3:a,pv:e,wk:b,ie:d,win:g,mac:c}}();(function(){l.w3&&((typeof m.readyState!=q&&"complete"==m.readyState||typeof m.readyState==q&&(m.getElementsByTagName("body")[0]||m.body))&&a(),A||(typeof m.addEventListener!=q&&m.addEventListener("DOMContentLoaded",a,!1),l.ie&&l.win&&(m.attachEvent("onreadystatechange",function(){"complete"==m.readyState&&(m.detachEvent("onreadystatechange",arguments.callee),a())}),w==top&&function(){if(!A){try{m.documentElement.doScroll("left")}catch(b){setTimeout(arguments.callee,
+0);return}a()}}()),l.wk&&function(){A||(/loaded|complete/.test(m.readyState)?a():setTimeout(arguments.callee,0))}(),c(a)))})();(function(){l.ie&&l.win&&window.attachEvent("onunload",function(){for(var a=C.length,b=0;b<a;b++)C[b][0].detachEvent(C[b][1],C[b][2]);a=I.length;for(b=0;b<a;b++)t(I[b]);for(var c in l)l[c]=null;l=null;for(var g in swfobject)swfobject[g]=null;swfobject=null})})();return{registerObject:function(a,b,c,g){if(l.w3&&a&&b){var d={};d.id=a;d.swfVersion=b;d.expressInstall=c;d.callbackFn=
+g;y[y.length]=d;i(a,!1)}else g&&g({success:!1,id:a})},getObjectById:function(a){if(l.w3)return f(a)},embedSWF:function(a,c,d,f,e,o,t,h,n,j){var s={success:!1,id:c};l.w3&&!(l.wk&&312>l.wk)&&a&&c&&d&&f&&e?(i(c,!1),b(function(){d+="";f+="";var b={};if(n&&typeof n===v)for(var l in n)b[l]=n[l];b.data=a;b.width=d;b.height=f;l={};if(h&&typeof h===v)for(var r in h)l[r]=h[r];if(t&&typeof t===v)for(var m in t)l.flashvars=typeof l.flashvars!=q?l.flashvars+("&"+m+"="+t[m]):m+"="+t[m];if(F(e))r=u(b,l,c),b.id==
+c&&i(c,!0),s.success=!0,s.ref=r;else{if(o&&g()){b.data=o;k(b,l,c,j);return}i(c,!0)}j&&j(s)})):j&&j(s)},switchOffAutoHideShow:function(){M=!1},ua:l,getFlashPlayerVersion:function(){return{major:l.pv[0],minor:l.pv[1],release:l.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(a,b,c){if(l.w3)return u(a,b,c)},showExpressInstall:function(a,b,c,d){l.w3&&g()&&k(a,b,c,d)},removeSWF:function(a){l.w3&&t(a)},createCSS:function(a,b,c,g){l.w3&&h(a,b,c,g)},addDomLoadEvent:b,addLoadEvent:c,getQueryParamValue:function(a){var b=
+m.location.search||m.location.hash;if(b){/\?/.test(b)&&(b=b.split("?")[1]);if(null==a)return N(b);for(var b=b.split("&"),c=0;c<b.length;c++)if(b[c].substring(0,b[c].indexOf("="))==a)return N(b[c].substring(b[c].indexOf("=")+1))}return""},expressInstallCallback:function(){if(G){var a=s(j);a&&D&&(a.parentNode.replaceChild(D,a),H&&(i(H,!0),l.ie&&l.win&&(D.style.display="block")),J&&J(L));G=!1}}}}();
+(function(){if(!window.WEB_SOCKET_FORCE_FLASH){if(window.WebSocket)return;if(window.MozWebSocket){window.WebSocket=MozWebSocket;return}}var a;a=window.WEB_SOCKET_LOGGER?WEB_SOCKET_LOGGER:window.console&&window.console.log&&window.console.error?window.console:{log:function(){},error:function(){}};9>swfobject.getFlashPlayerVersion().major?a.error("Flash Player >= 9.0.0 is required."):("file:"==location.protocol&&a.error("WARNING: web-socket-js doesn't work in file:///... URL unless you set Flash Security Settings properly. Open the page via Web server i.e. http://..."),
+window.WebSocket=function(a,c,d,e,f){var g=this;g.__id=WebSocket.__nextId++;WebSocket.__instances[g.__id]=g;g.readyState=WebSocket.CONNECTING;g.bufferedAmount=0;g.__events={};c?"string"==typeof c&&(c=[c]):c=[];g.__createTask=setTimeout(function(){WebSocket.__addTask(function(){g.__createTask=null;WebSocket.__flash.create(g.__id,a,c,d||null,e||0,f||null)})},0)},WebSocket.prototype.send=function(a){if(this.readyState==WebSocket.CONNECTING)throw"INVALID_STATE_ERR: Web Socket connection has not been established";
+a=WebSocket.__flash.send(this.__id,encodeURIComponent(a));if(0>a)return!0;this.bufferedAmount+=a;return!1},WebSocket.prototype.close=function(){this.__createTask?(clearTimeout(this.__createTask),this.__createTask=null,this.readyState=WebSocket.CLOSED):this.readyState==WebSocket.CLOSED||this.readyState==WebSocket.CLOSING||(this.readyState=WebSocket.CLOSING,WebSocket.__flash.close(this.__id))},WebSocket.prototype.addEventListener=function(a,c){a in this.__events||(this.__events[a]=[]);this.__events[a].push(c)},
+WebSocket.prototype.removeEventListener=function(a,c){if(a in this.__events)for(var d=this.__events[a],e=d.length-1;0<=e;--e)if(d[e]===c){d.splice(e,1);break}},WebSocket.prototype.dispatchEvent=function(a){for(var c=this.__events[a.type]||[],d=0;d<c.length;++d)c[d](a);(c=this["on"+a.type])&&c.apply(this,[a])},WebSocket.prototype.__handleEvent=function(a){"readyState"in a&&(this.readyState=a.readyState);"protocol"in a&&(this.protocol=a.protocol);var c;if("open"==a.type||"error"==a.type)c=this.__createSimpleEvent(a.type);
+else if("close"==a.type)c=this.__createSimpleEvent("close"),c.wasClean=a.wasClean?!0:!1,c.code=a.code,c.reason=a.reason;else if("message"==a.type)c=this.__createMessageEvent("message",decodeURIComponent(a.message));else throw"unknown event type: "+a.type;this.dispatchEvent(c)},WebSocket.prototype.__createSimpleEvent=function(a){if(document.createEvent&&window.Event){var c=document.createEvent("Event");c.initEvent(a,!1,!1);return c}return{type:a,bubbles:!1,cancelable:!1}},WebSocket.prototype.__createMessageEvent=
+function(a,c){if(document.createEvent&&window.MessageEvent&&!window.opera){var d=document.createEvent("MessageEvent");d.initMessageEvent("message",!1,!1,c,null,null,window,null);return d}return{type:a,data:c,bubbles:!1,cancelable:!1}},WebSocket.CONNECTING=0,WebSocket.OPEN=1,WebSocket.CLOSING=2,WebSocket.CLOSED=3,WebSocket.__initialized=!1,WebSocket.__flash=null,WebSocket.__instances={},WebSocket.__tasks=[],WebSocket.__nextId=0,WebSocket.loadFlashPolicyFile=function(a){WebSocket.__addTask(function(){WebSocket.__flash.loadManualPolicyFile(a)})},
+WebSocket.__initialize=function(){if(!WebSocket.__initialized)if(WebSocket.__initialized=!0,WebSocket.__swfLocation&&(window.WEB_SOCKET_SWF_LOCATION=WebSocket.__swfLocation),window.WEB_SOCKET_SWF_LOCATION){if(!window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR&&!WEB_SOCKET_SWF_LOCATION.match(/(^|\/)WebSocketMainInsecure\.swf(\?.*)?$/)&&WEB_SOCKET_SWF_LOCATION.match(/^\w+:\/\/([^\/]+)/)){var b=RegExp.$1;location.host!=b&&a.error("[WebSocket] You must host HTML and WebSocketMain.swf in the same host ('"+
+location.host+"' != '"+b+"'). See also 'How to host HTML file and SWF file in different domains' section in README.md. If you use WebSocketMainInsecure.swf, you can suppress this message by WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;")}b=document.createElement("div");b.id="webSocketContainer";b.style.position="absolute";WebSocket.__isFlashLite()?(b.style.left="0px",b.style.top="0px"):(b.style.left="-100px",b.style.top="-100px");var c=document.createElement("div");c.id="webSocketFlash";b.appendChild(c);
+document.body.appendChild(b);swfobject.embedSWF(WEB_SOCKET_SWF_LOCATION,"webSocketFlash","1","1","9.0.0",null,null,{hasPriority:!0,swliveconnect:!0,allowScriptAccess:"always"},null,function(b){b.success||a.error("[WebSocket] swfobject.embedSWF failed")})}else a.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf")},WebSocket.__onFlashInitialized=function(){setTimeout(function(){WebSocket.__flash=document.getElementById("webSocketFlash");WebSocket.__flash.setCallerUrl(location.href);
+WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);for(var a=0;a<WebSocket.__tasks.length;++a)WebSocket.__tasks[a]();WebSocket.__tasks=[]},0)},WebSocket.__onFlashEvent=function(){setTimeout(function(){try{for(var b=WebSocket.__flash.receiveEvents(),c=0;c<b.length;++c)WebSocket.__instances[b[c].webSocketId].__handleEvent(b[c])}catch(d){a.error(d)}},0);return!0},WebSocket.__log=function(b){a.log(decodeURIComponent(b))},WebSocket.__error=function(b){a.error(decodeURIComponent(b))},WebSocket.__addTask=
+function(a){WebSocket.__flash?a():WebSocket.__tasks.push(a)},WebSocket.__isFlashLite=function(){if(!window.navigator||!window.navigator.mimeTypes)return!1;var a=window.navigator.mimeTypes["application/x-shockwave-flash"];return!a||!a.enabledPlugin||!a.enabledPlugin.filename?!1:a.enabledPlugin.filename.match(/flashlite/i)?!0:!1},window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION||swfobject.addDomLoadEvent(function(){WebSocket.__initialize()}))})();
+var Base64=function(){return{encode:function(a){var b="",c,d,e,f,g,k,n=0;do c=a.charCodeAt(n++),d=a.charCodeAt(n++),e=a.charCodeAt(n++),f=c>>2,c=(c&3)<<4|d>>4,g=(d&15)<<2|e>>6,k=e&63,isNaN(d)?g=k=64:isNaN(e)&&(k=64),b=b+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(f)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(c)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(g)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(k);
+while(n<a.length);return b},decode:function(a){var b="",c,d,e,f,g,k=0,a=a.replace(/[^A-Za-z0-9\+\/\=]/g,"");do c="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(k++)),d="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(k++)),f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(k++)),g="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(k++)),c=c<<2|d>>4,d=(d&15)<<
+4|f>>2,e=(f&3)<<6|g,b+=String.fromCharCode(c),64!=f&&(b+=String.fromCharCode(d)),64!=g&&(b+=String.fromCharCode(e));while(k<a.length);return b}}}(),MD5=function(){var a=function(a,b){var c=(a&65535)+(b&65535);return(a>>16)+(b>>16)+(c>>16)<<16|c&65535},b=function(a){for(var b=[],c=0;c<8*a.length;c+=8)b[c>>5]|=(a.charCodeAt(c/8)&255)<<c%32;return b},c=function(a){for(var b="",c=0;c<32*a.length;c+=8)b+=String.fromCharCode(a[c>>5]>>>c%32&255);return b},d=function(a){for(var b="",c=0;c<4*a.length;c++)b+=
+"0123456789abcdef".charAt(a[c>>2]>>8*(c%4)+4&15)+"0123456789abcdef".charAt(a[c>>2]>>8*(c%4)&15);return b},e=function(a){for(var b="",c,g,d=0;d<4*a.length;d+=3){c=(a[d>>2]>>8*(d%4)&255)<<16|(a[d+1>>2]>>8*((d+1)%4)&255)<<8|a[d+2>>2]>>8*((d+2)%4)&255;for(g=0;4>g;g++)b=8*d+6*g>32*a.length?b+"":b+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".charAt(c>>6*(3-g)&63)}return b},f=function(b,c,g,d,f,e){b=a(a(c,b),a(d,e));return a(b<<f|b>>>32-f,g)},g=function(a,b,c,g,d,e,k){return f(b&c|
+~b&g,a,b,d,e,k)},k=function(a,b,c,g,d,e,k){return f(b&g|c&~g,a,b,d,e,k)},n=function(a,b,c,g,d,e,k){return f(c^(b|~g),a,b,d,e,k)},r=function(b,c){b[c>>5]|=128<<c%32;b[(c+64>>>9<<4)+14]=c;for(var d=1732584193,e=-271733879,h=-1732584194,i=271733878,r,u,v,B,j=0;j<b.length;j+=16)r=d,u=e,v=h,B=i,d=g(d,e,h,i,b[j+0],7,-680876936),i=g(i,d,e,h,b[j+1],12,-389564586),h=g(h,i,d,e,b[j+2],17,606105819),e=g(e,h,i,d,b[j+3],22,-1044525330),d=g(d,e,h,i,b[j+4],7,-176418897),i=g(i,d,e,h,b[j+5],12,1200080426),h=g(h,i,
+d,e,b[j+6],17,-1473231341),e=g(e,h,i,d,b[j+7],22,-45705983),d=g(d,e,h,i,b[j+8],7,1770035416),i=g(i,d,e,h,b[j+9],12,-1958414417),h=g(h,i,d,e,b[j+10],17,-42063),e=g(e,h,i,d,b[j+11],22,-1990404162),d=g(d,e,h,i,b[j+12],7,1804603682),i=g(i,d,e,h,b[j+13],12,-40341101),h=g(h,i,d,e,b[j+14],17,-1502002290),e=g(e,h,i,d,b[j+15],22,1236535329),d=k(d,e,h,i,b[j+1],5,-165796510),i=k(i,d,e,h,b[j+6],9,-1069501632),h=k(h,i,d,e,b[j+11],14,643717713),e=k(e,h,i,d,b[j+0],20,-373897302),d=k(d,e,h,i,b[j+5],5,-701558691),
+i=k(i,d,e,h,b[j+10],9,38016083),h=k(h,i,d,e,b[j+15],14,-660478335),e=k(e,h,i,d,b[j+4],20,-405537848),d=k(d,e,h,i,b[j+9],5,568446438),i=k(i,d,e,h,b[j+14],9,-1019803690),h=k(h,i,d,e,b[j+3],14,-187363961),e=k(e,h,i,d,b[j+8],20,1163531501),d=k(d,e,h,i,b[j+13],5,-1444681467),i=k(i,d,e,h,b[j+2],9,-51403784),h=k(h,i,d,e,b[j+7],14,1735328473),e=k(e,h,i,d,b[j+12],20,-1926607734),d=f(e^h^i,d,e,b[j+5],4,-378558),i=f(d^e^h,i,d,b[j+8],11,-2022574463),h=f(i^d^e,h,i,b[j+11],16,1839030562),e=f(h^i^d,e,h,b[j+14],
+23,-35309556),d=f(e^h^i,d,e,b[j+1],4,-1530992060),i=f(d^e^h,i,d,b[j+4],11,1272893353),h=f(i^d^e,h,i,b[j+7],16,-155497632),e=f(h^i^d,e,h,b[j+10],23,-1094730640),d=f(e^h^i,d,e,b[j+13],4,681279174),i=f(d^e^h,i,d,b[j+0],11,-358537222),h=f(i^d^e,h,i,b[j+3],16,-722521979),e=f(h^i^d,e,h,b[j+6],23,76029189),d=f(e^h^i,d,e,b[j+9],4,-640364487),i=f(d^e^h,i,d,b[j+12],11,-421815835),h=f(i^d^e,h,i,b[j+15],16,530742520),e=f(h^i^d,e,h,b[j+2],23,-995338651),d=n(d,e,h,i,b[j+0],6,-198630844),i=n(i,d,e,h,b[j+7],10,1126891415),
+h=n(h,i,d,e,b[j+14],15,-1416354905),e=n(e,h,i,d,b[j+5],21,-57434055),d=n(d,e,h,i,b[j+12],6,1700485571),i=n(i,d,e,h,b[j+3],10,-1894986606),h=n(h,i,d,e,b[j+10],15,-1051523),e=n(e,h,i,d,b[j+1],21,-2054922799),d=n(d,e,h,i,b[j+8],6,1873313359),i=n(i,d,e,h,b[j+15],10,-30611744),h=n(h,i,d,e,b[j+6],15,-1560198380),e=n(e,h,i,d,b[j+13],21,1309151649),d=n(d,e,h,i,b[j+4],6,-145523070),i=n(i,d,e,h,b[j+11],10,-1120210379),h=n(h,i,d,e,b[j+2],15,718787259),e=n(e,h,i,d,b[j+9],21,-343485551),d=a(d,r),e=a(e,u),h=a(h,
+v),i=a(i,B);return[d,e,h,i]},u=function(a,c){var d=b(a);16<d.length&&(d=r(d,8*a.length));for(var g=Array(16),e=Array(16),f=0;16>f;f++)g[f]=d[f]^909522486,e[f]=d[f]^1549556828;d=r(g.concat(b(c)),512+8*c.length);return r(e.concat(d),640)};return{hexdigest:function(a){return d(r(b(a),8*a.length))},b64digest:function(a){return e(r(b(a),8*a.length))},hash:function(a){return c(r(b(a),8*a.length))},hmac_hexdigest:function(a,b){return d(u(a,b))},hmac_b64digest:function(a,b){return e(u(a,b))},hmac_hash:function(a,
+b){return c(u(a,b))},test:function(){return"900150983cd24fb0d6963f7d28e17f72"===MD5.hexdigest("abc")}}}();Function.prototype.bind||(Function.prototype.bind=function(a){var b=this;return function(){return b.apply(a,arguments)}});Function.prototype.prependArg||(Function.prototype.prependArg=function(a){var b=this;return function(){for(var c=[a],d=0;d<arguments.length;d++)c.push(arguments[d]);return b.apply(this,c)}});
+Array.prototype.indexOf||(Array.prototype.indexOf=function(a,b){var c=this.length,d=Number(b)||0,d=0>d?Math.ceil(d):Math.floor(d);for(0>d&&(d+=c);d<c;d++)if(d in this&&this[d]===a)return d;return-1});
+(function(a){function b(a,b){return new f.Builder(a,b)}function c(a){return new f.Builder("message",a)}function d(a){return new f.Builder("iq",a)}function e(a){return new f.Builder("presence",a)}var f;f={VERSION:"2a276a4",NS:{HTTPBIND:"http://jabber.org/protocol/httpbind",BOSH:"urn:xmpp:xbosh",CLIENT:"jabber:client",AUTH:"jabber:iq:auth",ROSTER:"jabber:iq:roster",PROFILE:"jabber:iq:profile",DISCO_INFO:"http://jabber.org/protocol/disco#info",DISCO_ITEMS:"http://jabber.org/protocol/disco#items",MUC:"http://jabber.org/protocol/muc",
+SASL:"urn:ietf:params:xml:ns:xmpp-sasl",STREAM:"http://etherx.jabber.org/streams",BIND:"urn:ietf:params:xml:ns:xmpp-bind",SESSION:"urn:ietf:params:xml:ns:xmpp-session",VERSION:"jabber:iq:version",STANZAS:"urn:ietf:params:xml:ns:xmpp-stanzas"},addNamespace:function(a,b){f.NS[a]=b},Status:{ERROR:0,CONNECTING:1,CONNFAIL:2,AUTHENTICATING:3,AUTHFAIL:4,CONNECTED:5,DISCONNECTED:6,DISCONNECTING:7,ATTACHED:8},LogLevel:{DEBUG:0,INFO:1,WARN:2,ERROR:3,FATAL:4},ElementType:{NORMAL:1,TEXT:3},TIMEOUT:1.1,SECONDARY_TIMEOUT:0.1,
+forEachChild:function(a,b,c){var d,e;for(d=0;d<a.childNodes.length;d++)e=a.childNodes[d],e.nodeType==f.ElementType.NORMAL&&(!b||this.isTagEqual(e,b))&&c(e)},isTagEqual:function(a,b){return a.tagName.toLowerCase()==b.toLowerCase()},_xmlGenerator:null,_makeGenerator:function(){var a;window.ActiveXObject?(a=new ActiveXObject("Microsoft.XMLDOM"),a.appendChild(a.createElement("strophe"))):a=document.implementation.createDocument("jabber:client","strophe",null);return a},xmlElement:function(a){if(!a)return null;
+var b=null;f._xmlGenerator||(f._xmlGenerator=f._makeGenerator());var b=f._xmlGenerator.createElement(a),c,d,e;for(c=1;c<arguments.length;c++)if(arguments[c])if("string"==typeof arguments[c]||"number"==typeof arguments[c])b.appendChild(f.xmlTextNode(arguments[c]));else if("object"==typeof arguments[c]&&"function"==typeof arguments[c].sort)for(d=0;d<arguments[c].length;d++)"object"==typeof arguments[c][d]&&"function"==typeof arguments[c][d].sort&&b.setAttribute(arguments[c][d][0],arguments[c][d][1]);
+else if("object"==typeof arguments[c])for(e in arguments[c])arguments[c].hasOwnProperty(e)&&b.setAttribute(e,arguments[c][e]);return b},xmlescape:function(a){a=a.replace(/\&/g,"&amp;");a=a.replace(/</g,"&lt;");return a=a.replace(/>/g,"&gt;")},xmlTextNode:function(a){a=f.xmlescape(a);f._xmlGenerator||(f._xmlGenerator=f._makeGenerator());return f._xmlGenerator.createTextNode(a)},getText:function(a){if(!a)return null;var b="";0===a.childNodes.length&&a.nodeType==f.ElementType.TEXT&&(b+=a.nodeValue);
+for(var c=0;c<a.childNodes.length;c++)a.childNodes[c].nodeType==f.ElementType.TEXT&&(b+=a.childNodes[c].nodeValue);return b},copyElement:function(a){var b,c;if(a.nodeType==f.ElementType.NORMAL){c=f.xmlElement(a.tagName);for(b=0;b<a.attributes.length;b++)c.setAttribute(a.attributes[b].nodeName.toLowerCase(),a.attributes[b].value);for(b=0;b<a.childNodes.length;b++)c.appendChild(f.copyElement(a.childNodes[b]))}else a.nodeType==f.ElementType.TEXT&&(c=f.xmlTextNode(a.nodeValue));return c},escapeNode:function(a){return a.replace(/^\s+|\s+$/g,
+"").replace(/\\/g,"\\5c").replace(/ /g,"\\20").replace(/\"/g,"\\22").replace(/\&/g,"\\26").replace(/\'/g,"\\27").replace(/\//g,"\\2f").replace(/:/g,"\\3a").replace(/</g,"\\3c").replace(/>/g,"\\3e").replace(/@/g,"\\40")},unescapeNode:function(a){return a.replace(/\\20/g," ").replace(/\\22/g,'"').replace(/\\26/g,"&").replace(/\\27/g,"'").replace(/\\2f/g,"/").replace(/\\3a/g,":").replace(/\\3c/g,"<").replace(/\\3e/g,">").replace(/\\40/g,"@").replace(/\\5c/g,"\\")},getNodeFromJid:function(a){return 0>
+a.indexOf("@")?null:a.split("@")[0]},getDomainFromJid:function(a){a=f.getBareJidFromJid(a);if(0>a.indexOf("@"))return a;a=a.split("@");a.splice(0,1);return a.join("@")},getResourceFromJid:function(a){a=a.split("/");if(2>a.length)return null;a.splice(0,1);return a.join("/")},getBareJidFromJid:function(a){return a?a.split("/")[0]:null},log:function(){},debug:function(a){this.log(this.LogLevel.DEBUG,a)},info:function(a){this.log(this.LogLevel.INFO,a)},warn:function(a){this.log(this.LogLevel.WARN,a)},
+error:function(a){this.log(this.LogLevel.ERROR,a)},fatal:function(a){this.log(this.LogLevel.FATAL,a)},serialize:function(a){var b;if(!a)return null;"function"===typeof a.tree&&(a=a.tree());var c=a.nodeName,d,e;a.getAttribute("_realname")&&(c=a.getAttribute("_realname"));b="<"+c;for(d=0;d<a.attributes.length;d++)"_realname"!=a.attributes[d].nodeName&&(b+=" "+a.attributes[d].nodeName.toLowerCase()+"='"+a.attributes[d].value.replace("&","&amp;").replace("'","&apos;").replace("<","&lt;")+"'");if(0<a.childNodes.length){b+=
+">";for(d=0;d<a.childNodes.length;d++)e=a.childNodes[d],e.nodeType==f.ElementType.NORMAL?b+=f.serialize(e):e.nodeType==f.ElementType.TEXT&&(b+=e.nodeValue);b+="</"+c+">"}else b+="/>";return b},_requestId:0,_connectionPlugins:{},addConnectionPlugin:function(a,b){f._connectionPlugins[a]=b}};f.Builder=function(a,b){if("presence"==a||"message"==a||"iq"==a)b&&!b.xmlns?b.xmlns=f.NS.CLIENT:b||(b={xmlns:f.NS.CLIENT});this.node=this.nodeTree=f.xmlElement(a,b)};f.Builder.prototype={tree:function(){return this.nodeTree},
+toString:function(){return f.serialize(this.nodeTree)},up:function(){this.node=this.node.parentNode;return this},attrs:function(a){for(var b in a)a.hasOwnProperty(b)&&this.node.setAttribute(b,a[b]);return this},c:function(a,b){var c=f.xmlElement(a,b);this.node.appendChild(c);this.node=c;return this},cnode:function(a){this.node.appendChild(a);this.node=a;return this},t:function(a){this.node.appendChild(f.xmlTextNode(a));return this}};f.Handler=function(a,b,c,d,e,t,s){this.handler=a;this.ns=b;this.name=
+c;this.type=d;this.id=e;this.options=s||{matchbare:!1};this.options.matchBare||(this.options.matchBare=!1);this.from=this.options.matchBare?t?f.getBareJidFromJid(t):null:t;this.user=!0};f.Handler.prototype={isMatch:function(a){var b,c=null,c=this.options.matchBare?f.getBareJidFromJid(a.getAttribute("from")):a.getAttribute("from");b=!1;if(this.ns){var d=this;f.forEachChild(a,null,function(a){a.getAttribute("xmlns")==d.ns&&(b=!0)});b=b||a.getAttribute("xmlns")==this.ns}else b=!0;return b&&(!this.name||
+f.isTagEqual(a,this.name))&&(!this.type||a.getAttribute("type")==this.type)&&(!this.id||a.getAttribute("id")==this.id)&&(!this.from||c==this.from)?!0:!1},run:function(a){var b=null;try{b=this.handler(a)}catch(c){throw c.sourceURL?f.fatal("error: "+this.handler+" "+c.sourceURL+":"+c.line+" - "+c.name+": "+c.message):c.fileName?("undefined"!=typeof console&&(console.trace(),console.error(this.handler," - error - ",c,c.message)),f.fatal("error: "+this.handler+" "+c.fileName+":"+c.lineNumber+" - "+c.name+
+": "+c.message)):f.fatal("error: "+this.handler),c;}return b},toString:function(){return"{Handler: "+this.handler+"("+this.name+","+this.id+","+this.ns+")}"}};f.TimedHandler=function(a,b){this.period=a;this.handler=b;this.lastCalled=(new Date).getTime();this.user=!0};f.TimedHandler.prototype={run:function(){this.lastCalled=(new Date).getTime();return this.handler()},reset:function(){this.lastCalled=(new Date).getTime()},toString:function(){return"{TimedHandler: "+this.handler+"("+this.period+")}"}};
+f.Request=function(a,b,c,d){this.id=++f._requestId;this.xmlData=a;this.data=f.serialize(a);this.func=this.origFunc=b;this.rid=c;this.date=NaN;this.sends=d||0;this.abort=!1;this.dead=null;this.age=function(){return!this.date?0:(new Date-this.date)/1E3};this.timeDead=function(){return!this.dead?0:(new Date-this.dead)/1E3};this.xhr=this._newXHR()};f.Request.prototype={getResponse:function(){var a=null;if(this.xhr.responseXML&&this.xhr.responseXML.documentElement){if(a=this.xhr.responseXML.documentElement,
+"parsererror"==a.tagName)throw f.error("invalid response received"),f.error("responseText: "+this.xhr.responseText),f.error("responseXML: "+f.serialize(this.xhr.responseXML)),"parsererror";}else this.xhr.responseText&&(f.error("invalid response received"),f.error("responseText: "+this.xhr.responseText),f.error("responseXML: "+f.serialize(this.xhr.responseXML)));return a},_newXHR:function(){var a=null;window.XMLHttpRequest?(a=new XMLHttpRequest,a.overrideMimeType&&a.overrideMimeType("text/xml")):window.ActiveXObject&&
+(a=new ActiveXObject("Microsoft.XMLHTTP"));a.onreadystatechange=this.func.prependArg(this);return a}};a&&a(f,b,c,d,e)})(function(a,b,c,d,e){window.Strophe=a;window.$build=b;window.$msg=c;window.$iq=d;window.$pres=e});
+Strophe.Connection=function(a){this.service=a;this.jid="";this.rid=Math.floor(4294967295*Math.random());this.streamId=this.sid=null;this.do_bind=this.do_session=!1;this.timedHandlers=[];this.handlers=[];this.removeTimeds=[];this.removeHandlers=[];this.addTimeds=[];this.addHandlers=[];this._disconnectTimeout=this._idleTimeout=null;this.connected=this.disconnecting=this.authenticated=!1;this.errors=0;this.paused=!1;this.hold=1;this.wait=60;this.window=5;this._data=[];this._requests=[];this._uniqueId=
+Math.round(1E4*Math.random());this._sasl_challenge_handler=this._sasl_failure_handler=this._sasl_success_handler=null;this._idleTimeout=setTimeout(this._onIdle.bind(this),100);for(var b in Strophe._connectionPlugins)Strophe._connectionPlugins.hasOwnProperty(b)&&(a=function(){},a.prototype=Strophe._connectionPlugins[b],this[b]=new a,this[b].init(this))};
+Strophe.Connection.prototype={reset:function(){this.rid=Math.floor(4294967295*Math.random());this.streamId=this.sid=null;this.do_bind=this.do_session=!1;this.timedHandlers=[];this.handlers=[];this.removeTimeds=[];this.removeHandlers=[];this.addTimeds=[];this.addHandlers=[];this.connected=this.disconnecting=this.authenticated=!1;this.errors=0;this._requests=[];this._uniqueId=Math.round(1E4*Math.random())},pause:function(){this.paused=!0},resume:function(){this.paused=!1},getUniqueId:function(a){return"string"==
+typeof a||"number"==typeof a?++this._uniqueId+":"+a:++this._uniqueId+""},connect:function(a,b,c,d,e){this.jid=a;this.pass=b;this.connect_callback=c;this.authenticated=this.connected=this.disconnecting=!1;this.errors=0;this.wait=d||this.wait;this.hold=e||this.hold;this.domain=Strophe.getDomainFromJid(this.jid);a=this._buildBody().attrs({to:this.domain,"xml:lang":"en",wait:this.wait,hold:this.hold,content:"text/xml; charset=utf-8",ver:"1.6","xmpp:version":"1.0","xmlns:xmpp":Strophe.NS.BOSH});this._changeConnectStatus(Strophe.Status.CONNECTING,
+null);this._requests.push(new Strophe.Request(a.tree(),this._onRequestStateChange.bind(this).prependArg(this._connect_cb.bind(this)),a.tree().rid));this._throttledRequestHandler()},attach:function(a,b,c,d,e,f,g){this.jid=a;this.sid=b;this.rid=c;this.connect_callback=d;this.domain=Strophe.getDomainFromJid(this.jid);this.connected=this.authenticated=!0;this.wait=e||this.wait;this.hold=f||this.hold;this.window=g||this.window;this._changeConnectStatus(Strophe.Status.ATTACHED,null)},xmlInput:function(){},
+xmlOutput:function(){},rawInput:function(){},rawOutput:function(){},send:function(a){if(null!==a){if("function"===typeof a.sort)for(var b=0;b<a.length;b++)this._queueData(a[b]);else"function"===typeof a.tree?this._queueData(a.tree()):this._queueData(a);this._throttledRequestHandler();clearTimeout(this._idleTimeout);this._idleTimeout=setTimeout(this._onIdle.bind(this),100)}},flush:function(){clearTimeout(this._idleTimeout);this._onIdle()},sendIQ:function(a,b,c,d){var e=null,f=this;"function"===typeof a.tree&&
+(a=a.tree());var g=a.getAttribute("id");g||(g=this.getUniqueId("sendIQ"),a.setAttribute("id",g));var k=this.addHandler(function(a){e&&f.deleteTimedHandler(e);var d=a.getAttribute("type");if("result"==d)b&&b(a);else if("error"==d)c&&c(a);else throw{name:"StropheError",message:"Got bad IQ type of "+d};},null,"iq",null,g);d&&(e=this.addTimedHandler(d,function(){f.deleteHandler(k);c&&c(null);return!1}));this.send(a);return g},_queueData:function(a){if(null===a||!a.tagName||!a.childNodes)throw{name:"StropheError",
+message:"Cannot queue non-DOMElement."};this._data.push(a)},_sendRestart:function(){this._data.push("restart");this._throttledRequestHandler();clearTimeout(this._idleTimeout);this._idleTimeout=setTimeout(this._onIdle.bind(this),100)},addTimedHandler:function(a,b){var c=new Strophe.TimedHandler(a,b);this.addTimeds.push(c);return c},deleteTimedHandler:function(a){this.removeTimeds.push(a)},addHandler:function(a,b,c,d,e,f,g){a=new Strophe.Handler(a,b,c,d,e,f,g);this.addHandlers.push(a);return a},deleteHandler:function(a){this.removeHandlers.push(a)},
+disconnect:function(a){this._changeConnectStatus(Strophe.Status.DISCONNECTING,a);Strophe.info("Disconnect was called because: "+a);this.connected&&(this._disconnectTimeout=this._addSysTimedHandler(3E3,this._onDisconnectTimeout.bind(this)),this._sendTerminate())},_changeConnectStatus:function(a,b){for(var c in Strophe._connectionPlugins)if(Strophe._connectionPlugins.hasOwnProperty(c)){var d=this[c];if(d.statusChanged)try{d.statusChanged(a,b)}catch(e){Strophe.error(""+c+" plugin caused an exception changing status: "+
+e)}}if(this.connect_callback)try{this.connect_callback(a,b)}catch(f){Strophe.error("User connection callback caused an exception: "+f)}},_buildBody:function(){var a=$build("body",{rid:this.rid++,xmlns:Strophe.NS.HTTPBIND});null!==this.sid&&a.attrs({sid:this.sid});return a},_removeRequest:function(a){Strophe.debug("removing request");var b;for(b=this._requests.length-1;0<=b;b--)a==this._requests[b]&&this._requests.splice(b,1);a.xhr.onreadystatechange=function(){};this._throttledRequestHandler()},_restartRequest:function(a){var b=
+this._requests[a];null===b.dead&&(b.dead=new Date);this._processRequest(a)},_processRequest:function(a){var b=this._requests[a],c=-1;try{4==b.xhr.readyState&&(c=b.xhr.status)}catch(d){Strophe.error("caught an error in _requests["+a+"], reqStatus: "+c)}"undefined"==typeof c&&(c=-1);var e=b.age(),e=!isNaN(e)&&e>Math.floor(Strophe.TIMEOUT*this.wait),f=null!==b.dead&&b.timeDead()>Math.floor(Strophe.SECONDARY_TIMEOUT*this.wait),c=4==b.xhr.readyState&&(1>c||500<=c);if(e||f||c)f&&Strophe.error("Request "+
+this._requests[a].id+" timed out (secondary), restarting"),b.abort=!0,b.xhr.abort(),b.xhr.onreadystatechange=function(){},this._requests[a]=new Strophe.Request(b.xmlData,b.origFunc,b.rid,b.sends),b=this._requests[a];if(0===b.xhr.readyState){Strophe.debug("request id "+b.id+"."+b.sends+" posting");b.date=new Date;try{b.xhr.open("POST",this.service,!0)}catch(g){Strophe.error("XHR open failed.");this.connected||this._changeConnectStatus(Strophe.Status.CONNFAIL,"bad-service");this.disconnect();return}a=
+function(){b.xhr.send(b.data)};1<b.sends?(c=1E3*Math.pow(b.sends,3),setTimeout(a,c)):a();b.sends++;this.xmlOutput(b.xmlData);this.rawOutput(b.data)}else Strophe.debug("_processRequest: "+(0===a?"first":"second")+" request has readyState of "+b.xhr.readyState)},_throttledRequestHandler:function(){this._requests?Strophe.debug("_throttledRequestHandler called with "+this._requests.length+" requests"):Strophe.debug("_throttledRequestHandler called with undefined requests");this._requests&&0!==this._requests.length&&
+(0<this._requests.length&&this._processRequest(0),1<this._requests.length&&Math.abs(this._requests[0].rid-this._requests[1].rid)<this.window-1&&this._processRequest(1))},_onRequestStateChange:function(a,b){Strophe.debug("request id "+b.id+"."+b.sends+" state changed to "+b.xhr.readyState);if(b.abort)b.abort=!1;else{var c;if(4==b.xhr.readyState){c=0;try{c=b.xhr.status}catch(d){}"undefined"==typeof c&&(c=0);if(this.disconnecting&&400<=c)this._hitError(c);else{var e=this._requests[0]==b,f=this._requests[1]==
+b;if(0<c&&500>c||5<b.sends)this._removeRequest(b),Strophe.debug("request id "+b.id+" should now be removed");if(200==c)(f||e&&0<this._requests.length&&this._requests[0].age()>Math.floor(Strophe.SECONDARY_TIMEOUT*this.wait))&&this._restartRequest(0),Strophe.debug("request id "+b.id+"."+b.sends+" got 200"),a(b),this.errors=0;else if(Strophe.error("request id "+b.id+"."+b.sends+" error "+c+" happened"),0===c||400<=c&&600>c||12E3<=c)this._hitError(c),400<=c&&500>c&&(this._changeConnectStatus(Strophe.Status.DISCONNECTING,
+null),this._doDisconnect());0<c&&1E4>c||5<b.sends||this._throttledRequestHandler()}}}},_hitError:function(a){this.errors++;Strophe.warn("request errored, status: "+a+", number of errors: "+this.errors);4<this.errors&&this._onDisconnectTimeout()},_doDisconnect:function(){Strophe.info("_doDisconnect was called");this.disconnecting=this.authenticated=!1;this.streamId=this.sid=null;this.rid=Math.floor(4294967295*Math.random());this.connected&&(this._changeConnectStatus(Strophe.Status.DISCONNECTED,null),
+this.connected=!1);this.handlers=[];this.timedHandlers=[];this.removeTimeds=[];this.removeHandlers=[];this.addTimeds=[];this.addHandlers=[]},_dataRecv:function(a){try{var b=a.getResponse()}catch(c){if("parsererror"!=c)throw c;this.disconnect("strophe-parsererror")}if(null!==b){this.xmlInput(b);for(this.rawInput(Strophe.serialize(b));0<this.removeHandlers.length;)a=this.removeHandlers.pop(),a=this.handlers.indexOf(a),0<=a&&this.handlers.splice(a,1);for(;0<this.addHandlers.length;)this.handlers.push(this.addHandlers.pop());
+if(this.disconnecting&&0===this._requests.length)this.deleteTimedHandler(this._disconnectTimeout),this._disconnectTimeout=null,this._doDisconnect();else if(a=b.getAttribute("type"),null!==a&&"terminate"==a)a=b.getAttribute("condition"),b=b.getElementsByTagName("conflict"),null!==a?("remote-stream-error"==a&&0<b.length&&(a="conflict"),this._changeConnectStatus(Strophe.Status.CONNFAIL,a)):this._changeConnectStatus(Strophe.Status.CONNFAIL,"unknown"),this.disconnect();else{var d=this;Strophe.forEachChild(b,
+null,function(a){var b,c;c=d.handlers;d.handlers=[];for(b=0;b<c.length;b++){var k=c[b];k.isMatch(a)&&(d.authenticated||!k.user)?k.run(a)&&d.handlers.push(k):d.handlers.push(k)}})}}},_sendTerminate:function(){Strophe.info("_sendTerminate was called");var a=this._buildBody().attrs({type:"terminate"});this.authenticated&&a.c("presence",{xmlns:Strophe.NS.CLIENT,type:"unavailable"});this.disconnecting=!0;this._requests.push(new Strophe.Request(a.tree(),this._onRequestStateChange.bind(this).prependArg(this._dataRecv.bind(this)),
+a.tree().getAttribute("rid")));this._throttledRequestHandler()},_connect_cb:function(a){Strophe.info("_connect_cb was called");this.connected=!0;if(a=a.getResponse()){this.xmlInput(a);this.rawInput(Strophe.serialize(a));var b=a.getAttribute("type");if(null!==b&&"terminate"==b)b=a.getAttribute("condition"),a=a.getElementsByTagName("conflict"),null!==b?("remote-stream-error"==b&&0<a.length&&(b="conflict"),this._changeConnectStatus(Strophe.Status.CONNFAIL,b)):this._changeConnectStatus(Strophe.Status.CONNFAIL,
+"unknown");else{this.sid||(this.sid=a.getAttribute("sid"));this.stream_id||(this.stream_id=a.getAttribute("authid"));if(b=a.getAttribute("requests"))this.window=parseInt(b,10);if(b=a.getAttribute("hold"))this.hold=parseInt(b,10);if(b=a.getAttribute("wait"))this.wait=parseInt(b,10);var c=b=!1,d=!1,a=a.getElementsByTagName("mechanism"),e,f;if(0<a.length){for(e=0;e<a.length;e++)f=Strophe.getText(a[e]),"DIGEST-MD5"==f?c=!0:"PLAIN"==f?b=!0:"ANONYMOUS"==f&&(d=!0);null===Strophe.getNodeFromJid(this.jid)&&
+d?(this._changeConnectStatus(Strophe.Status.AUTHENTICATING,null),this._sasl_success_handler=this._addSysHandler(this._sasl_success_cb.bind(this),null,"success",null,null),this._sasl_failure_handler=this._addSysHandler(this._sasl_failure_cb.bind(this),null,"failure",null,null),this.send($build("auth",{xmlns:Strophe.NS.SASL,mechanism:"ANONYMOUS"}).tree())):null===Strophe.getNodeFromJid(this.jid)?(this._changeConnectStatus(Strophe.Status.CONNFAIL,"x-strophe-bad-non-anon-jid"),this.disconnect()):c?(this._changeConnectStatus(Strophe.Status.AUTHENTICATING,
+null),this._sasl_challenge_handler=this._addSysHandler(this._sasl_challenge1_cb.bind(this),null,"challenge",null,null),this._sasl_failure_handler=this._addSysHandler(this._sasl_failure_cb.bind(this),null,"failure",null,null),this.send($build("auth",{xmlns:Strophe.NS.SASL,mechanism:"DIGEST-MD5"}).tree())):b?(a=Strophe.getBareJidFromJid(this.jid),a=a+"\x00"+Strophe.getNodeFromJid(this.jid),a=a+"\x00"+this.pass,this._changeConnectStatus(Strophe.Status.AUTHENTICATING,null),this._sasl_success_handler=
+this._addSysHandler(this._sasl_success_cb.bind(this),null,"success",null,null),this._sasl_failure_handler=this._addSysHandler(this._sasl_failure_cb.bind(this),null,"failure",null,null),a=Base64.encode(a),this.send($build("auth",{xmlns:Strophe.NS.SASL,mechanism:"PLAIN"}).t(a).tree())):(this._changeConnectStatus(Strophe.Status.AUTHENTICATING,null),this._addSysHandler(this._auth1_cb.bind(this),null,null,null,"_auth_1"),this.send($iq({type:"get",to:this.domain,id:"_auth_1"}).c("query",{xmlns:Strophe.NS.AUTH}).c("username",
+{}).t(Strophe.getNodeFromJid(this.jid)).tree()))}else a=this._buildBody(),this._requests.push(new Strophe.Request(a.tree(),this._onRequestStateChange.bind(this).prependArg(this._connect_cb.bind(this)),a.tree().getAttribute("rid"))),this._throttledRequestHandler()}}},_sasl_challenge1_cb:function(a){var b=/([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/,c=Base64.decode(Strophe.getText(a)),a=MD5.hexdigest(1234567890*Math.random()),d="",e=null,f="",g;for(this.deleteHandler(this._sasl_failure_handler);c.match(b);)switch(g=
+c.match(b),c=c.replace(g[0],""),g[2]=g[2].replace(/^"(.+)"$/,"$1"),g[1]){case "realm":d=g[2];break;case "nonce":f=g[2];break;case "host":e=g[2]}b="xmpp/"+this.domain;null!==e&&(b=b+"/"+e);e=MD5.hash(Strophe.getNodeFromJid(this.jid)+":"+d+":"+this.pass)+":"+f+":"+a;c="AUTHENTICATE:"+b;g=""+("username="+this._quote(Strophe.getNodeFromJid(this.jid))+",");g+="realm="+this._quote(d)+",";g+="nonce="+this._quote(f)+",";g+="cnonce="+this._quote(a)+",";g=g+'nc="00000001",qop="auth",'+("digest-uri="+this._quote(b)+
+",");g+="response="+this._quote(MD5.hexdigest(MD5.hexdigest(e)+":"+f+":00000001:"+a+":auth:"+MD5.hexdigest(c)))+",";g+='charset="utf-8"';this._sasl_challenge_handler=this._addSysHandler(this._sasl_challenge2_cb.bind(this),null,"challenge",null,null);this._sasl_success_handler=this._addSysHandler(this._sasl_success_cb.bind(this),null,"success",null,null);this._sasl_failure_handler=this._addSysHandler(this._sasl_failure_cb.bind(this),null,"failure",null,null);this.send($build("response",{xmlns:Strophe.NS.SASL}).t(Base64.encode(g)).tree());
+return!1},_quote:function(a){return'"'+a.replace(/\\/g,"\\\\").replace(/"/g,'\\"')+'"'},_sasl_challenge2_cb:function(){this.deleteHandler(this._sasl_success_handler);this.deleteHandler(this._sasl_failure_handler);this._sasl_success_handler=this._addSysHandler(this._sasl_success_cb.bind(this),null,"success",null,null);this._sasl_failure_handler=this._addSysHandler(this._sasl_failure_cb.bind(this),null,"failure",null,null);this.send($build("response",{xmlns:Strophe.NS.SASL}).tree());return!1},_auth1_cb:function(){var a=
+$iq({type:"set",id:"_auth_2"}).c("query",{xmlns:Strophe.NS.AUTH}).c("username",{}).t(Strophe.getNodeFromJid(this.jid)).up().c("password").t(this.pass);Strophe.getResourceFromJid(this.jid)||(this.jid=Strophe.getBareJidFromJid(this.jid)+"/strophe");a.up().c("resource",{}).t(Strophe.getResourceFromJid(this.jid));this._addSysHandler(this._auth2_cb.bind(this),null,null,null,"_auth_2");this.send(a.tree());return!1},_sasl_success_cb:function(){Strophe.info("SASL authentication succeeded.");this.deleteHandler(this._sasl_failure_handler);
+this._sasl_failure_handler=null;this._sasl_challenge_handler&&(this.deleteHandler(this._sasl_challenge_handler),this._sasl_challenge_handler=null);this._addSysHandler(this._sasl_auth1_cb.bind(this),null,"stream:features",null,null);this._sendRestart();return!1},_sasl_auth1_cb:function(a){var b,c;for(b=0;b<a.childNodes.length;b++)c=a.childNodes[b],"bind"==c.nodeName&&(this.do_bind=!0),"session"==c.nodeName&&(this.do_session=!0);this.do_bind?(this._addSysHandler(this._sasl_bind_cb.bind(this),null,null,
+null,"_bind_auth_2"),(a=Strophe.getResourceFromJid(this.jid))?this.send($iq({type:"set",id:"_bind_auth_2"}).c("bind",{xmlns:Strophe.NS.BIND}).c("resource",{}).t(a).tree()):this.send($iq({type:"set",id:"_bind_auth_2"}).c("bind",{xmlns:Strophe.NS.BIND}).tree())):this._changeConnectStatus(Strophe.Status.AUTHFAIL,null);return!1},_sasl_bind_cb:function(a){if("error"==a.getAttribute("type"))return Strophe.info("SASL binding failed."),this._changeConnectStatus(Strophe.Status.AUTHFAIL,null),!1;a=a.getElementsByTagName("bind");
+if(0<a.length)a=a[0].getElementsByTagName("jid"),0<a.length&&(this.jid=Strophe.getText(a[0]),this.do_session?(this._addSysHandler(this._sasl_session_cb.bind(this),null,null,null,"_session_auth_2"),this.send($iq({type:"set",id:"_session_auth_2"}).c("session",{xmlns:Strophe.NS.SESSION}).tree())):(this.authenticated=!0,this._changeConnectStatus(Strophe.Status.CONNECTED,null)));else return Strophe.info("SASL binding failed."),this._changeConnectStatus(Strophe.Status.AUTHFAIL,null),!1},_sasl_session_cb:function(a){"result"==
+a.getAttribute("type")?(this.authenticated=!0,this._changeConnectStatus(Strophe.Status.CONNECTED,null)):"error"==a.getAttribute("type")&&(Strophe.info("Session creation failed."),this._changeConnectStatus(Strophe.Status.AUTHFAIL,null));return!1},_sasl_failure_cb:function(){this._sasl_success_handler&&(this.deleteHandler(this._sasl_success_handler),this._sasl_success_handler=null);this._sasl_challenge_handler&&(this.deleteHandler(this._sasl_challenge_handler),this._sasl_challenge_handler=null);this._changeConnectStatus(Strophe.Status.AUTHFAIL,
+null);return!1},_auth2_cb:function(a){"result"==a.getAttribute("type")?(this.authenticated=!0,this._changeConnectStatus(Strophe.Status.CONNECTED,null)):"error"==a.getAttribute("type")&&(this._changeConnectStatus(Strophe.Status.AUTHFAIL,null),this.disconnect());return!1},_addSysTimedHandler:function(a,b){var c=new Strophe.TimedHandler(a,b);c.user=!1;this.addTimeds.push(c);return c},_addSysHandler:function(a,b,c,d,e){a=new Strophe.Handler(a,b,c,d,e);a.user=!1;this.addHandlers.push(a);return a},_onDisconnectTimeout:function(){Strophe.info("_onDisconnectTimeout was called");
+for(var a;0<this._requests.length;)a=this._requests.pop(),a.abort=!0,a.xhr.abort(),a.xhr.onreadystatechange=function(){};this._doDisconnect();return!1},_onIdle:function(){for(var a,b,c,d;0<this.removeTimeds.length;)b=this.removeTimeds.pop(),a=this.timedHandlers.indexOf(b),0<=a&&this.timedHandlers.splice(a,1);for(;0<this.addTimeds.length;)this.timedHandlers.push(this.addTimeds.pop());var e=(new Date).getTime();d=[];for(a=0;a<this.timedHandlers.length;a++)if(b=this.timedHandlers[a],this.authenticated||
+!b.user)c=b.lastCalled+b.period,0>=c-e?b.run()&&d.push(b):d.push(b);this.timedHandlers=d;this.authenticated&&0===this._requests.length&&0===this._data.length&&!this.disconnecting&&(Strophe.info("no requests during idle cycle, sending blank request"),this._data.push(null));if(2>this._requests.length&&0<this._data.length&&!this.paused){b=this._buildBody();for(a=0;a<this._data.length;a++)null!==this._data[a]&&("restart"===this._data[a]?b.attrs({to:this.domain,"xml:lang":"en","xmpp:restart":"true","xmlns:xmpp":Strophe.NS.BOSH}):
+b.cnode(this._data[a]).up());delete this._data;this._data=[];this._requests.push(new Strophe.Request(b.tree(),this._onRequestStateChange.bind(this).prependArg(this._dataRecv.bind(this)),b.tree().rid));this._processRequest(this._requests.length-1)}0<this._requests.length&&(a=this._requests[0].age(),null!==this._requests[0].dead&&this._requests[0].timeDead()>Math.floor(Strophe.SECONDARY_TIMEOUT*this.wait)&&this._throttledRequestHandler(),a>Math.floor(Strophe.TIMEOUT*this.wait)&&(Strophe.warn("Request "+
+this._requests[0].id+" timed out, over "+Math.floor(Strophe.TIMEOUT*this.wait)+" seconds since last activity"),this._throttledRequestHandler()));clearTimeout(this._idleTimeout);this._idleTimeout=setTimeout(this._onIdle.bind(this),100)}};
+Strophe.addConnectionPlugin("pubsub",{_connection:null,init:function(a){this._connection=a;Strophe.addNamespace("PUBSUB","http://jabber.org/protocol/pubsub");Strophe.addNamespace("PUBSUB_SUBSCRIBE_OPTIONS",Strophe.NS.PUBSUB+"#subscribe_options");Strophe.addNamespace("PUBSUB_ERRORS",Strophe.NS.PUBSUB+"#errors");Strophe.addNamespace("PUBSUB_EVENT",Strophe.NS.PUBSUB+"#event");Strophe.addNamespace("PUBSUB_OWNER",Strophe.NS.PUBSUB+"#owner");Strophe.addNamespace("PUBSUB_AUTO_CREATE",Strophe.NS.PUBSUB+"#auto-create");
+Strophe.addNamespace("PUBSUB_PUBLISH_OPTIONS",Strophe.NS.PUBSUB+"#publish-options");Strophe.addNamespace("PUBSUB_NODE_CONFIG",Strophe.NS.PUBSUB+"#node_config");Strophe.addNamespace("PUBSUB_CREATE_AND_CONFIGURE",Strophe.NS.PUBSUB+"#create-and-configure");Strophe.addNamespace("PUBSUB_SUBSCRIBE_AUTHORIZATION",Strophe.NS.PUBSUB+"#subscribe_authorization");Strophe.addNamespace("PUBSUB_GET_PENDING",Strophe.NS.PUBSUB+"#get-pending");Strophe.addNamespace("PUBSUB_MANAGE_SUBSCRIPTIONS",Strophe.NS.PUBSUB+"#manage-subscriptions");
+Strophe.addNamespace("PUBSUB_META_DATA",Strophe.NS.PUBSUB+"#meta-data")},createNode:function(a,b,c,d,e){var f=this._connection.getUniqueId("pubsubcreatenode"),a=$iq({from:a,to:b,type:"set",id:f}),b=Strophe.xmlElement("configure",[]),g=Strophe.xmlElement("x",[["xmlns","jabber:x:data"]]),k=Strophe.xmlElement("field",[["var","FORM_TYPE"],["type","hidden"]]),n=Strophe.xmlElement("value",[]),r=Strophe.xmlTextNode(Strophe.NS.PUBSUB+"#node_config");n.appendChild(r);k.appendChild(n);g.appendChild(k);for(var u in d)g.appendChild(d[u]);
+d.length&&0!=d.length&&b.appendChild(g);a.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).c("create",{node:c}).up().cnode(b);this._connection.addHandler(e,null,"iq",null,f,null);this._connection.send(a.tree());return f},subscribe:function(a,b,c,d,e){var f=this._connection.getUniqueId("subscribenode"),g=Strophe.xmlElement("options",[]),k=Strophe.xmlElement("x",[["xmlns","jabber:x:data"]]),n=Strophe.xmlElement("field",[["var","FORM_TYPE"],["type","hidden"]]),r=Strophe.xmlElement("value",[]),u=Strophe.xmlTextNode(Strophe.NS.PUBSUB_SUBSCRIBE_OPTIONS);
+r.appendChild(u);n.appendChild(r);k.appendChild(n);b=$iq({from:a,to:b,type:"set",id:f});if(d&&d.length&&0!==d.length){for(n=0;n<d.length;n++)k.appendChild(d[n]);g.appendChild(k);b.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).c("subscribe",{node:c,jid:a}).up().cnode(g)}else b.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).c("subscribe",{node:c,jid:a});this._connection.addHandler(e,null,"iq",null,f,null);this._connection.send(b.tree());return f},unsubscribe:function(a,b,c,d){var e=this._connection.getUniqueId("unsubscribenode"),
+b=$iq({from:a,to:b,type:"set",id:e});b.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).c("unsubscribe",{node:c,jid:a});this._connection.addHandler(d,null,"iq",null,e,null);this._connection.send(b.tree());return e},publish:function(a,b,c,d,e){var f=this._connection.getUniqueId("publishnode"),c=Strophe.xmlElement("publish",[["node",c]]),g;for(g in d){var k=Strophe.xmlElement("item",[["id",d[g].id]]);k.appendChild(d[g].value[0]);c.appendChild(k)}a=$iq({from:a,to:b,type:"set",id:f});a.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).cnode(c);
+this._connection.addHandler(e,null,"iq",null,f,null);this._connection.send(a.tree());return f},deleteNode:function(a,b,c,d){var e=this._connection.getUniqueId("deletenode"),a=$iq({from:a,to:b,type:"set",id:e});a.c("pubsub",{xmlns:Strophe.NS.PUBSUB_OWNER}).c("delete",{node:c});this._connection.addHandler(d,null,"iq",null,e,null);this._connection.send(a.tree());return e},items:function(a,b,c,d,e,f){a=$iq({from:a,to:b,type:"get"});a.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).c("items",{node:c,max_items:d});
+return this._connection.sendIQ(a.tree(),e,f)},item:function(a,b,c,d,e,f){a=$iq({from:a,to:b,type:"get"});a.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).c("items",{node:c}).c("item",{id:d});return this._connection.sendIQ(a.tree(),e,f)}});function Contact(){this.name="";this.resources={};this.subscription="none";this.ask="";this.groups=[]}Contact.prototype={online:function(){var a=!1,b;for(b in this.resources){a=!0;break}return a}};
+Strophe.addConnectionPlugin("roster",{init:function(a){this.connection=a;this.contacts={};Strophe.addNamespace("ROSTER","jabber:iq:roster")},statusChanged:function(a){if(a===Strophe.Status.CONNECTED){this.contacts={};this.connection.addHandler(this.rosterChanged.bind(this),Strophe.NS.ROSTER,"iq","set");this.connection.addHandler(this.presenceChanged.bind(this),null,"presence");var b=this;this.connection.sendIQ($iq({type:"get"}).c("query",{xmlns:Strophe.NS.ROSTER}),function(a){$(a).find("item").each(function(){var a=
+new Contact;a.name=$(this).attr("name")||"";a.subscription=$(this).attr("subscription")||"none";a.ask=$(this).attr("ask")||"";$(this).find("group").each(function(){a.groups.push($(this).text())});b.contacts[$(this).attr("jid")]=a});$(document).trigger("roster_changed",b)})}else if(a===Strophe.Status.DISCONNECTED){for(var c in this.contacts)this.contacts[c].resources={};$(document).trigger("roster_changed",this)}},rosterChanged:function(a){var b=$(a).find("item"),c=b.attr("jid"),d=b.attr("subscription")||
+"";if("remove"===d)delete this.contacts[c];else if("none"===d){var e=new Contact;e.name=b.attr("name")||"";b.find("group").each(function(){e.groups.push(this.text())});this.contacts[c]=e}else e=this.contacts[c],e.name=b.attr("name")||e.name,e.subscription=d||e.subscription,e.ask=b.attr("ask")||e.ask,e.groups=[],b.find("group").each(function(){e.groups.push(this.text())});this.connection.send($iq({type:"result",id:$(a).attr("id")}));$(document).trigger("roster_changed",this);return!0},presenceChanged:function(a){var b=
+$(a).attr("from"),c=Strophe.getBareJidFromJid(b),b=Strophe.getResourceFromJid(b),d=$(a).attr("type")||"available";if(!this.contacts[c]||"error"===d)return!0;"unavailable"===d?delete this.contacts[c].resources[b]:this.contacts[c].resources[b]={show:$(a).find("show").text()||"online",status:$(a).find("status").text()};$(document).trigger("roster_changed",this);return!0},addContact:function(a,b,c){var d=$iq({type:"set"}).c("query",{xmlns:Strophe.NS.ROSTER}).c("item",{name:b||"",jid:a});c&&0<c.length&&
+$.each(c,function(){d.c("group").t(this).up()});this.connection.sendIQ(d)},deleteContact:function(a){this.connection.sendIQ($iq({type:"set"}).c("query",{xmlns:Strophe.NS.ROSTER}).c("item",{jid:a,subscription:"remove"}))},modifyContact:function(a,b,c){this.addContact(a,b,c)},subscribe:function(a,b,c){this.addContact(a,b,c);this.connection.send($pres({to:a,type:"subscribe"}))},unsubscribe:function(a){this.connection.send($pres({to:a,type:"unsubscribe"}));this.deleteContact(a)}});
+Strophe.Status.REBINDFAILED=9;
+Strophe.WebSocket=function(a){this.service=a;this.ws=null;this.connect_timeout=300;this.jid="";this.streamId=null;this.do_bind=this.do_session=!1;this.timedHandlers=[];this.handlers=[];this.removeTimeds=[];this.removeHandlers=[];this.addTimeds=[];this.addHandlers=[];this._disconnectTimeout=this._idleTimeout=null;this.connected=this.disconnecting=this.authenticated=!1;this._keep_alive_timer=2E4;this.errors=0;this._data=[];this._requests=[];this._uniqueId=Math.round(1E4*Math.random());this._rebind_failure_handler=
+this._rebind_success_handler=this._sasl_challenge_handler=this._sasl_failure_handler=this._sasl_success_handler=null;this._idleTimeout=setTimeout(this._onIdle.bind(this),100);for(var b in Strophe._connectionPlugins)Strophe._connectionPlugins.hasOwnProperty(b)&&(a=function(){},a.prototype=Strophe._connectionPlugins[b],this[b]=new a,this[b].init(this))};
+Strophe.WebSocket.prototype={reset:function(){this.streamId=this.sid=null;this.do_bind=this.do_session=!1;this.timedHandlers=[];this.handlers=[];this.removeTimeds=[];this.removeHandlers=[];this.addTimeds=[];this.addHandlers=[];this.connected=this.disconnecting=this.authenticated=!1;this.errors=0},pause:function(){},resume:function(){},getUniqueId:function(a){return"string"==typeof a||"number"==typeof a?++this._uniqueId+":"+a:++this._uniqueId+""},connect:function(a,b,c){this.jid=a;this.pass=b;this.connect_callback=
+c;this.authenticated=this.connected=this.disconnecting=!1;this.errors=0;this.domain=Strophe.getDomainFromJid(this.jid);if(window.WebSocket)try{this.ws=new WebSocket(this.service),this.ws.onopen=this._send_initial_stream.bind(this),this._addSysTimedHandler(this._keep_alive_timer,this._keep_alive_handler.bind(this)),this.ws.onmessage=this._get_stream_id.bind(this).prependArg(this._connect_cb.bind(this)),this.ws.onerror=function(a){Strophe.error("error : "+a)},this.ws.onclose=this._ws_on_close.bind(this)}catch(d){}else throw"no websocket support";
+},attach:function(){},rebind:function(a,b,c){this.jid=a;this.connect_callback=c;this.domain=Strophe.getDomainFromJid(this.jid);this._addSysTimedHandler(this._keep_alive_timer,this._keep_alive_handler.bind(this));this.ws=new WebSocket(this.service);this.ws.onopen=this._send_initial_stream.bind(this);this.ws.onmessage=this._get_stream_id.bind(this).prependArg(this._rebind_cb.bind(this).prependArg(a).prependArg(b));this.ws.onclose=this._ws_on_close.bind(this)},save:function(a,b){this.sendIQ($iq({type:"set"}).c("push",
+{xmlns:"p1:push"}).c("keepalive",{max:"30"}).up().c("session",{duration:"1"}),a,b)},xmlInput:function(){},xmlOutput:function(){},rawInput:function(){},rawOutput:function(){},send:function(a){var b="";if(null!==a){if("function"===typeof a.sort)for(var c=0;c<a.length;c++)b+=Strophe.serialize(a[c]),this.xmlOutput(a);else"function"===typeof a.tree?(b=Strophe.serialize(a.tree()),this.xmlOutput(a.tree())):(b=Strophe.serialize(a),this.xmlOutput(a));this.rawOutput(b);this.ws.send(b)}},flush:function(){},
+sendIQ:function(a,b,c,d){var e=null,f=this;"function"===typeof a.tree&&(a=a.tree());var g=a.getAttribute("id");g||(g=this.getUniqueId("sendIQ"),a.setAttribute("id",g));var k=this.addHandler(function(a){e&&f.deleteTimedHandler(e);var d=a.getAttribute("type");if("result"==d)b&&b(a);else if("error"==d)c&&c(a);else throw{name:"StropheError",message:"Got bad IQ type of "+d};},null,"iq",null,g);d&&(e=this.addTimedHandler(d,function(){f.deleteHandler(k);c&&c(null);return!1}));this.send(a);return g},addTimedHandler:function(a,
+b){var c=new Strophe.TimedHandler(a,b);this.addTimeds.push(c);return c},deleteTimedHandler:function(a){this.removeTimeds.push(a)},addHandler:function(a,b,c,d,e,f,g){a=new Strophe.Handler(a,b,c,d,e,f,g);this.addHandlers.push(a);return a},deleteHandler:function(a){this.removeHandlers.push(a)},disconnect:function(a){this._changeConnectStatus(Strophe.Status.DISCONNECTING,a);Strophe.info("Disconnect was called because: "+a);this.connected&&(this._disconnectTimeout=this._addSysTimedHandler(3E3,this._onDisconnectTimeout.bind(this)),
+this._sendTerminate())},_changeConnectStatus:function(a,b){for(var c in Strophe._connectionPlugins)if(Strophe._connectionPlugins.hasOwnProperty(c)){var d=this[c];if(d.statusChanged)try{d.statusChanged(a,b)}catch(e){Strophe.error(""+c+" plugin caused an exception changing status: "+e)}}if(this.connect_callback)try{this.connect_callback(a,b)}catch(f){Strophe.error("User connection callback caused an exception: "+f)}},_doDisconnect:function(){Strophe.info("_doDisconnect was called");this.disconnecting=
+this.authenticated=!1;this.streamId=this.sid=null;this.connected&&(this._changeConnectStatus(Strophe.Status.DISCONNECTED,null),this.connected=!1);this.handlers=[];this.timedHandlers=[];this.removeTimeds=[];this.removeHandlers=[];this.addTimeds=[];this.addHandlers=[];this.ws.readyState!=this.ws.CLOSED&&this.ws.close()},_ws_on_close:function(){Strophe.info("websocket closed");this._doDisconnect()},_keep_alive_handler:function(){this.ws.send("\n");return!0},_send_initial_stream:function(){this._changeConnectStatus(Strophe.Status.CONNECTING,
+null);var a='<?xml version="1.0"?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" version="1.0" xmlns="jabber:client" to="'+this.domain+'" xml:lang="en" xmlns:xml="http://www.w3.org/XML/1998/namespace" >';this.rawOutput(a);this.ws.send(a)},_get_stream_id:function(a,b){elem=b.data;this.rawInput(elem);b.data.match(/id=[\'\"]([^\'\"]+)[\'\"]/)&&(this.streamId=RegExp.$1);this.ws.onmessage=a},_parseTree:function(a){try{return void 0==this._xml_parse&&(window.DOMParser?(this._xml_parser=
+new DOMParser,this._xml_parse=function(a){return this._xml_parser.parseFromString("<body xmlns:stream='foo' >"+a+"</body>","text/xml").documentElement.firstChild}):this._xml_parse=function(a){var b=new ActiveXObject("MSXML2.DomDocument");b.async="false";b.loadXML("<body xmlns:stream='foo'>"+a+"</body>");return b.documentElement.firstChild}),this._xml_parse(a)}catch(b){Strophe.error("Error : "+b.message)}return null},_dataRecv:function(a){var b;try{b=this._parseTree(a.data)}catch(c){if("parsererror"!=
+c)throw c;this.disconnect("strophe-parsererror")}if(null!==b){this.xmlInput(b);this.rawInput(a.data);for(var d;0<this.removeHandlers.length;)d=this.removeHandlers.pop(),a=this.handlers.indexOf(d),0<=a&&this.handlers.splice(a,1);for(;0<this.addHandlers.length;)this.handlers.push(this.addHandlers.pop());if(this.disconnecting)this.deleteTimedHandler(this._disconnectTimeout),this._disconnectTimeout=null,this._doDisconnect();else if(a=b.getAttribute("type"),null!==a&&"terminate"==a)a=b.getAttribute("condition"),
+b=b.getElementsByTagName("conflict"),null!==a?("remote-stream-error"==a&&0<b.length&&(a="conflict"),this._changeConnectStatus(Strophe.Status.CONNFAIL,a)):this._changeConnectStatus(Strophe.Status.CONNFAIL,"unknown"),this.disconnect();else{var e;e=this.handlers;this.handlers=[];for(a=0;a<e.length;a++)d=e[a],d.isMatch(b)&&(this.authenticated||!d.user)?d.run(b)&&this.handlers.push(d):this.handlers.push(d)}}},_sendTerminate:function(){Strophe.info("_sendTerminate was called");var a={};this.authenticated&&
+(a=$pres({xmlns:Strophe.NS.CLIENT,type:"unavailable"}));this.disconnecting=!0;this.send(a);this.ws.send("</stream:stream>")},_connect_cb:function(a){Strophe.info("_connect_cb was called");this.connected=!0;this.ws.onmessage=this._dataRecv.bind(this);if(a=a.data){stanza=this._parseTree(a);this.xmlInput(stanza);this.rawInput(Strophe.serialize(stanza));var b=a=!1,c=!1,d=stanza.getElementsByTagName("mechanism"),e,f;if(0<d.length)for(e=0;e<d.length;e++)f=Strophe.getText(d[e]),"DIGEST-MD5"==f?b=!0:"PLAIN"==
+f?a=!0:"ANONYMOUS"==f&&(c=!0);null===Strophe.getNodeFromJid(this.jid)&&c?(this._changeConnectStatus(Strophe.Status.AUTHENTICATING,null),this._sasl_success_handler=this._addSysHandler(this._sasl_success_cb.bind(this),null,"success",null,null),this._sasl_failure_handler=this._addSysHandler(this._sasl_failure_cb.bind(this),null,"failure",null,null),this.send($build("auth",{xmlns:Strophe.NS.SASL,mechanism:"ANONYMOUS"}).tree())):null===Strophe.getNodeFromJid(this.jid)?(this._changeConnectStatus(Strophe.Status.CONNFAIL,
+"x-strophe-bad-non-anon-jid"),this.disconnect()):b?(this._changeConnectStatus(Strophe.Status.AUTHENTICATING,null),this._sasl_challenge_handler=this._addSysHandler(this._sasl_challenge1_cb.bind(this),null,"challenge",null,null),this._sasl_failure_handler=this._addSysHandler(this._sasl_failure_cb.bind(this),null,"failure",null,null),this.send($build("auth",{xmlns:Strophe.NS.SASL,mechanism:"DIGEST-MD5"}).tree())):a?(a=Strophe.getBareJidFromJid(this.jid),a=a+"\x00"+Strophe.getNodeFromJid(this.jid),a=
+a+"\x00"+this.pass,this._changeConnectStatus(Strophe.Status.AUTHENTICATING,null),this._sasl_success_handler=this._addSysHandler(this._sasl_success_cb.bind(this),null,"success",null,null),this._sasl_failure_handler=this._addSysHandler(this._sasl_failure_cb.bind(this),null,"failure",null,null),a=Base64.encode(a),this.send($build("auth",{xmlns:Strophe.NS.SASL,mechanism:"PLAIN"}).t(a).tree())):(this._changeConnectStatus(Strophe.Status.AUTHENTICATING,null),this._addSysHandler(this._auth1_cb.bind(this),
+null,null,null,"_auth_1"),this.send($iq({type:"get",to:this.domain,id:"_auth_1"}).c("query",{xmlns:Strophe.NS.AUTH}).c("username",{}).t(Strophe.getNodeFromJid(this.jid)).tree()))}},_rebind_cb:function(a,b,c){this.connected=!0;this.ws.onmessage=this._dataRecv.bind(this);if(c=c.data)stanza=this._parseTree(c),this.xmlInput(stanza),this.rawInput(Strophe.serialize(stanza)),0<stanza.getElementsByTagName("rebind").length?(this.send($build("rebind",{xmlns:"p1:rebind"}).c("jid",{}).t(a).up().c("sid",{}).t(b).tree()),
+this._rebind_success_handler=this._addSysHandler(this._rebind_success_cb.bind(this),null,"rebind",null,null),this._rebind_failure_handler=this._addSysHandler(this._rebind_failure_cb.bind(this),null,"failure",null,null)):this._changeConnectStatus(Strophe.Status.CONNFAIL,"x-strophe-rebind-not-supported")},_rebind_success_cb:function(){Strophe.info("Rebinding succeeded.");this.connected=this.authenticated=!0;this.deleteHandler(this._rebind_failure_handler);this._rebind_failure_handler=null;this._changeConnectStatus(Strophe.Status.ATTACHED,
+null);return!1},_rebind_failure_cb:function(){Strophe.info("Rebinding failed.");this._rebind_success_handler&&(this.deleteHandler(this._rebind_success_handler),this._rebind_success_handler=null);this._changeConnectStatus(Strophe.Status.REBINDFAILED,null);return!1},_sasl_challenge1_cb:function(a){var b=/([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/,c=Base64.decode(Strophe.getText(a)),a=MD5.hexdigest(1234567890*Math.random()),d="",e=null,f="",g;for(this.deleteHandler(this._sasl_failure_handler);c.match(b);)switch(g=
+c.match(b),c=c.replace(g[0],""),g[2]=g[2].replace(/^"(.+)"$/,"$1"),g[1]){case "realm":d=g[2];break;case "nonce":f=g[2];break;case "host":e=g[2]}b="xmpp/"+this.domain;null!==e&&(b=b+"/"+e);e=MD5.hash(Strophe.getNodeFromJid(this.jid)+":"+d+":"+this.pass)+":"+f+":"+a;c="AUTHENTICATE:"+b;g=""+("username="+this._quote(Strophe.getNodeFromJid(this.jid))+",");g+="realm="+this._quote(d)+",";g+="nonce="+this._quote(f)+",";g+="cnonce="+this._quote(a)+",";g=g+'nc="00000001",qop="auth",'+("digest-uri="+this._quote(b)+
+",");g+="response="+this._quote(MD5.hexdigest(MD5.hexdigest(e)+":"+f+":00000001:"+a+":auth:"+MD5.hexdigest(c)))+",";g+='charset="utf-8"';this._sasl_challenge_handler=this._addSysHandler(this._sasl_challenge2_cb.bind(this),null,"challenge",null,null);this._sasl_success_handler=this._addSysHandler(this._sasl_success_cb.bind(this),null,"success",null,null);this._sasl_failure_handler=this._addSysHandler(this._sasl_failure_cb.bind(this),null,"failure",null,null);this.send($build("response",{xmlns:Strophe.NS.SASL}).t(Base64.encode(g)).tree());
+return!1},_quote:function(a){return'"'+a.replace(/\\/g,"\\\\").replace(/"/g,'\\"')+'"'},_sasl_challenge2_cb:function(){this.deleteHandler(this._sasl_success_handler);this.deleteHandler(this._sasl_failure_handler);this._sasl_success_handler=this._addSysHandler(this._sasl_success_cb.bind(this),null,"success",null,null);this._sasl_failure_handler=this._addSysHandler(this._sasl_failure_cb.bind(this),null,"failure",null,null);this.send($build("response",{xmlns:Strophe.NS.SASL}).tree());return!1},_auth1_cb:function(){var a=
+$iq({type:"set",id:"_auth_2"}).c("query",{xmlns:Strophe.NS.AUTH}).c("username",{}).t(Strophe.getNodeFromJid(this.jid)).up().c("password").t(this.pass);Strophe.getResourceFromJid(this.jid)||(this.jid=Strophe.getBareJidFromJid(this.jid)+"/strophe");a.up().c("resource",{}).t(Strophe.getResourceFromJid(this.jid));this._addSysHandler(this._auth2_cb.bind(this),null,null,null,"_auth_2");this.send(a.tree());return!1},_sasl_success_cb:function(){Strophe.info("SASL authentication succeeded.");this.deleteHandler(this._sasl_failure_handler);
+this._sasl_failure_handler=null;this._sasl_challenge_handler&&(this.deleteHandler(this._sasl_challenge_handler),this._sasl_challenge_handler=null);this._addSysHandler(this._sasl_auth1_cb.bind(this),null,"stream:features",null,null);this.ws.onmessage=this._get_stream_id.bind(this).prependArg(this._dataRecv.bind(this));this._send_initial_stream();return!1},_sasl_auth1_cb:function(a){var b,c;for(b=0;b<a.childNodes.length;b++)c=a.childNodes[b],"bind"==c.nodeName&&(this.do_bind=!0),"session"==c.nodeName&&
+(this.do_session=!0);this.do_bind?(this._addSysHandler(this._sasl_bind_cb.bind(this),null,null,null,"_bind_auth_2"),(a=Strophe.getResourceFromJid(this.jid))?this.send($iq({type:"set",id:"_bind_auth_2"}).c("bind",{xmlns:Strophe.NS.BIND}).c("resource",{}).t(a).tree()):this.send($iq({type:"set",id:"_bind_auth_2"}).c("bind",{xmlns:Strophe.NS.BIND}).tree())):this._changeConnectStatus(Strophe.Status.AUTHFAIL,null);return!1},_sasl_bind_cb:function(a){if("error"==a.getAttribute("type"))return Strophe.info("SASL binding failed."),
+this._changeConnectStatus(Strophe.Status.AUTHFAIL,null),!1;a=a.getElementsByTagName("bind");if(0<a.length)a=a[0].getElementsByTagName("jid"),0<a.length&&(this.jid=Strophe.getText(a[0]),this.do_session?(this._addSysHandler(this._sasl_session_cb.bind(this),null,null,null,"_session_auth_2"),this.send($iq({type:"set",id:"_session_auth_2"}).c("session",{xmlns:Strophe.NS.SESSION}).tree())):(this.authenticated=!0,this._changeConnectStatus(Strophe.Status.CONNECTED,null)));else return Strophe.info("SASL binding failed."),
+this._changeConnectStatus(Strophe.Status.AUTHFAIL,null),!1},_sasl_session_cb:function(a){"result"==a.getAttribute("type")?(this.authenticated=!0,this._changeConnectStatus(Strophe.Status.CONNECTED,null)):"error"==a.getAttribute("type")&&(Strophe.info("Session creation failed."),this._changeConnectStatus(Strophe.Status.AUTHFAIL,null));return!1},_sasl_failure_cb:function(){this._sasl_success_handler&&(this.deleteHandler(this._sasl_success_handler),this._sasl_success_handler=null);this._sasl_challenge_handler&&
+(this.deleteHandler(this._sasl_challenge_handler),this._sasl_challenge_handler=null);this._changeConnectStatus(Strophe.Status.AUTHFAIL,null);return!1},_auth2_cb:function(a){"result"==a.getAttribute("type")?(this.authenticated=!0,this._changeConnectStatus(Strophe.Status.CONNECTED,null)):"error"==a.getAttribute("type")&&(this._changeConnectStatus(Strophe.Status.AUTHFAIL,null),this.disconnect());return!1},_addSysTimedHandler:function(a,b){var c=new Strophe.TimedHandler(a,b);c.user=!1;this.addTimeds.push(c);
+return c},_addSysHandler:function(a,b,c,d,e){a=new Strophe.Handler(a,b,c,d,e);a.user=!1;this.addHandlers.push(a);return a},_onDisconnectTimeout:function(){Strophe.info("_onDisconnectTimeout was called");this._doDisconnect();return!1},_onIdle:function(){for(var a,b,c,d;0<this.removeTimeds.length;)b=this.removeTimeds.pop(),a=this.timedHandlers.indexOf(b),0<=a&&this.timedHandlers.splice(a,1);for(;0<this.addTimeds.length;)this.timedHandlers.push(this.addTimeds.pop());var e=(new Date).getTime();d=[];for(a=
+0;a<this.timedHandlers.length;a++)if(b=this.timedHandlers[a],this.authenticated||!b.user)c=b.lastCalled+b.period,0>=c-e?b.run()&&d.push(b):d.push(b);this.timedHandlers=d;clearTimeout(this._idleTimeout);this._idleTimeout=setTimeout(this._onIdle.bind(this),100)}};
+P1PP.prototype={connect:function(a){var b=this,c=this.params;if(!this.connection||!this.connection.connected){this._check_rebind();var d=function(){b.retries++;b.retries>c.connectretry?b.console.log(c.connectretry+" connect retry exceeded"):(b.timeout_id=setTimeout(function(){b.connect(!0)},c.connect_timeout),!a&&c.ws_url&&window.WebSocket?(b.current_protocol="WEBSOCKET",b.websocket()):(b.current_protocol="BOSH",b.bosh()))};0==this.retries&&!a?setTimeout(d.bind(this),c.connect_delay):d()}},disconnect:function(){this.closing=
+!0;window.clearTimeout(this.timeout_id);this.rebind_delete();this.connection&&(this.connection.deleteTimedHandler(this.bosh_rebind_id),this.connection.disconnect())},bosh:function(){try{this.connection=new Strophe.Connection(this.params.bosh_url);var a=this.rebind_fetch();a&&this.params.rebind?(prev_connection=a.split(" "),"BOSH"==prev_connection[0]?this.connection.attach(prev_connection[1],prev_connection[2],prev_connection[3],this.conn_callback.bind(this)):this._transport_connect()):this._transport_connect()}catch(b){this.connect()}},
+websocket:function(){try{this.connection=new Strophe.WebSocket(this.params.ws_url);var a=this.rebind_fetch();a&&this.params.rebind?(prev_connection=a.split(" "),this.connection.rebind(prev_connection[1],prev_connection[2],this.conn_callback.bind(this))):this._transport_connect()}catch(b){this.rebind_delete(),this.connect()}},_transport_connect:function(){this.connection.connect(this.params.jid?this.params.jid:this.params.domain,this.params.password?this.params.password:"",this.conn_callback.bind(this))},
+_check_rebind:function(){if(this.params.rebind){var a=this.rebind_fetch();if(a&&this.params.jid&&""!==this.params.jid){a.split(" ");var b=Strophe.getBareJidFromJid(this.params.jid),a=Strophe.getBareJidFromJid(a.split(" ")[1]);b!=a&&this.rebind_delete()}}},conn_callback:function(a){var b=this;this.params.debug&&(this.connection.rawOutput=function(a){b.console.log("out -> "+a)},this.connection.rawInput=function(a){b.console.log("in <- "+a)});window.clearTimeout(this.timeout_id);this.bosh_rebind_id=
+this.connection.addTimedHandler(2E3,function(){if("BOSH"===b.current_protocol&&!b.closing){var a=["BOSH",b.connection.jid,b.connection.sid,b.connection.rid].join(" ");b.rebind_store(a)}return!0});this.params.on_strophe_event(a,this.connection);var c=function(a,c){b.params.jid=a;b.params.password=c;b.connect()};if(a===Strophe.Status.CONNECTED)this.params.on_connected(),this.retries=0,!0==this.params.rebind&&"WEBSOCKET"==this.current_protocol&&this.connection.save(function(){var a=["WEBSOCKET",b.connection.jid,
+b.connection.streamId].join(" ");b.rebind_store(a)},function(){}),this.subscribe();else if(a===Strophe.Status.ATTACHED)"WEBSOCKET"==this.current_protocol?this.subscribe():setTimeout(function(){nodes=b.params.nodes;for(var a in nodes)"string"==typeof nodes[a]&&0<b.params.num_old&&b.connection.pubsub.items(b.connection.jid,b.params.pubsub_domain,nodes[a],b.params.num_old,function(a){for(var a=a.getElementsByTagName("item"),c=0;c<a.length;c++)id=a[c].getAttribute("id"),b.params.publish(id,a[c].firstChild)});
+b.connection.addHandler(b.on_event.bind(b),null,"message",null,null,b.params.pubsub_domain)},100);else if(!this.closing&&(a===Strophe.Status.CONNFAIL||a===Strophe.Status.DISCONNECTED))if(this.connection.deleteTimedHandler(this.bosh_rebind_id),this.rebind_delete(),!this.params.jid&&this.params.login_required)this.params.on_login_required(c);else a=Math.round(Math.random()*this.params.connect_timeout*(this.retries+1)),this.timeout_id=setTimeout(function(){b.connect()},a);else a===Strophe.Status.DISCONNECTED?
+(this.connection.reset(),this.closing=!1,this.params.on_disconnect()):a===Strophe.Status.REBINDFAILED?(this.rebind_delete(),this.connection=null,this.connect()):a===Strophe.Status.AUTHFAIL&&(delete this.connection,this.params.on_login_required(c))},subscribe:function(a){void 0===a&&(a=this.params.nodes);for(var b in a)if("string"==typeof a[b]){if(0<this.params.num_old){var c=this;this.connection.pubsub.items(this.connection.jid,this.params.pubsub_domain,a[b],this.params.num_old,function(a){for(var a=
+a.getElementsByTagName("item"),b=0;b<a.length;b++)id=a[b].getAttribute("id"),c.params.publish(id,a[b].firstChild)})}this.connection.pubsub.subscribe(this.connection.jid,this.params.pubsub_domain,a[b],[],function(){})}this.connection.addHandler(this.on_event.bind(this),null,"message",null,null,this.params.pubsub_domain)},unsubscribe:function(a){void 0===a&&(a=this.params.nodes);for(var b in a)"string"==typeof a[b]&&this.connection.pubsub.unsubscribe(this.connection.jid,this.params.pubsub_domain,a[b],
+function(){})},_extract_error_code:function(a){var b=a.getElementsByTagNameNS("http://jabber.org/protocol/pubsub#errors","*");b.length||(b=a.getElementsByTagNameNS("urn:ietf:params:xml:ns:xmpp-stanzas","*"));return b.length?b[0].localName:null},_publish:function(a,b,c,d){var e=this;null==b&&(b=this.connection.getUniqueId("publish"));this.connection.pubsub.publish(this.connection.jid,this.params.pubsub_domain,a,[{id:b,value:[c]}],function(a){e._done_publish(a,d)});return b},_done_publish:function(a,
+b){var c=a.getElementsByTagName("item"),c=c.length?c[0].getAttribute("id"):null;"result"==a.getAttribute("type")?b(c,"ok"):b(c,this._extract_error_code(a)||"error")},_deleteNode:function(a,b){var c=this;this.connection.pubsub.deleteNode(this.connection.jid,this.params.pubsub_domain,a,function(a){c._done_delete(a,b)})},_done_delete:function(a,b){"result"==a.getAttribute("type")?b("ok"):b(this._extract_error_code(a)||"error")},on_event:function(a){for(var b=a.getElementsByTagName("retract"),c=b.length,
+d=0;d<c;d++)this.params.retract(b[d].getAttribute("id"));b=void 0;c=a.getElementsByTagName("delay");c.length&&(b=c[0].getAttribute("stamp"));for(var a=a.getElementsByTagName("items"),c=a.length,e,f,g,d=0;d<c;d++){e=a[d].getAttribute("node");f=a[d].getElementsByTagName("item");g=f.length;for(d=0;d<g;d++)this.params.publish(f[d].getAttribute("id"),f[d].firstChild,e,b)}return!0},cookie:function(a,b){if("undefined"!=typeof b){options=this.params.cookie_opts;null===b&&(b="",options.expires=-1);var c="";
+if(options.expires&&("number"==typeof options.expires||options.expires.toUTCString))"number"==typeof options.expires?(c=new Date,c.setTime(c.getTime()+864E5*options.expires)):c=options.expires,c="; expires="+c.toUTCString();var d=options.path?"; path="+options.path:"",e=options.domain?"; domain="+options.domain:"",f=options.secure?"; secure":"";document.cookie=[a,"=",encodeURIComponent(b),c,d,e,f].join("")}else{c=null;if(document.cookie&&""!=document.cookie){d=document.cookie.split(";");for(e=0;e<
+d.length;e++)if(f="function"===typeof String.trim?(d[e]||"").trim():(d[e]||"").replace(/^\s\s*/,"").replace(/\s\s*$/,""),f.substring(0,a.length+1)==a+"="){c=decodeURIComponent(f.substring(a.length+1));break}}return c}},rebind_store:function(a){var b=this._build_key();window.sessionStorage?sessionStorage[b]=a:this.cookie(b,a)},rebind_delete:function(){var a=this._build_key();window.sessionStorage?sessionStorage.removeItem(a):this.cookie(a,null)},rebind_fetch:function(){var a=this._build_key();return window.sessionStorage?
+sessionStorage[a]:this.cookie(a)},_build_key:function(){var a=P1PP.COOKIE+"_"+this.current_protocol;"BOSH"===this.current_protocol&&(a=a+"_"+this.scope);return a}};
View
4 src/flash-websocket/swfobject.js
@@ -0,0 +1,4 @@
+/* SWFObject v2.2 <http://code.google.com/p/swfobject/>
+ is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
+*/
+var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
View
389 src/flash-websocket/web_socket.js
@@ -0,0 +1,389 @@
+// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
+// License: New BSD License
+// Reference: http://dev.w3.org/html5/websockets/
+// Reference: http://tools.ietf.org/html/rfc6455
+
+(function() {
+
+ if (window.WEB_SOCKET_FORCE_FLASH) {
+ // Keeps going.
+ } else if (window.WebSocket) {
+ return;
+ } else if (window.MozWebSocket) {
+ // Firefox.
+ window.WebSocket = MozWebSocket;
+ return;
+ }
+
+ var logger;
+ if (window.WEB_SOCKET_LOGGER) {
+ logger = WEB_SOCKET_LOGGER;
+ } else if (window.console && window.console.log && window.console.error) {
+ // In some environment, console is defined but console.log or console.error is missing.
+ logger = window.console;
+ } else {
+ logger = {log: function(){ }, error: function(){ }};
+ }
+
+ // swfobject.hasFlashPlayerVersion("10.0.0") doesn't work with Gnash.
+ if (swfobject.getFlashPlayerVersion().major < 9) {
+ logger.error("Flash Player >= 9.0.0 is required.");
+ return;
+ }
+ if (location.protocol == "file:") {
+ logger.error(
+ "WARNING: web-socket-js doesn't work in file:///... URL " +
+ "unless you set Flash Security Settings properly. " +
+ "Open the page via Web server i.e. http://...");
+ }
+
+ /**
+ * Our own implementation of WebSocket class using Flash.
+ * @param {string} url
+ * @param {array or string} protocols
+ * @param {string} proxyHost
+ * @param {int} proxyPort
+ * @param {string} headers
+ */
+ window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
+ var self = this;
+ self.__id = WebSocket.__nextId++;
+ WebSocket.__instances[self.__id] = self;
+ self.readyState = WebSocket.CONNECTING;
+ self.bufferedAmount = 0;
+ self.__events = {};
+ if (!protocols) {
+ protocols = [];
+ } else if (typeof protocols == "string") {
+ protocols = [protocols];
+ }
+ // Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
+ // Otherwise, when onopen fires immediately, onopen is called before it is set.
+ self.__createTask = setTimeout(function() {
+ WebSocket.__addTask(function() {
+ self.__createTask = null;
+ WebSocket.__flash.create(
+ self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
+ });
+ }, 0);
+ };
+
+ /**
+ * Send data to the web socket.
+ * @param {string} data The data to send to the socket.
+ * @return {boolean} True for success, false for failure.
+ */
+ WebSocket.prototype.send = function(data) {
+ if (this.readyState == WebSocket.CONNECTING) {
+ throw "INVALID_STATE_ERR: Web Socket connection has not been established";
+ }
+ // We use encodeURIComponent() here, because FABridge doesn't work if
+ // the argument includes some characters. We don't use escape() here
+ // because of this:
+ // https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
+ // But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
+ // preserve all Unicode characters either e.g. "\uffff" in Firefox.
+ // Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require
+ // additional testing.
+ var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
+ if (result < 0) { // success
+ return true;
+ } else {
+ this.bufferedAmount += result;
+ return false;
+ }
+ };
+
+ /**
+ * Close this web socket gracefully.
+ */
+ WebSocket.prototype.close = function() {
+ if (this.__createTask) {
+ clearTimeout(this.__createTask);
+ this.__createTask = null;
+ this.readyState = WebSocket.CLOSED;
+ return;
+ }
+ if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
+ return;
+ }
+ this.readyState = WebSocket.CLOSING;
+ WebSocket.__flash.close(this.__id);
+ };
+
+ /**
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
+ *
+ * @param {string} type
+ * @param {function} listener
+ * @param {boolean} useCapture
+ * @return void
+ */
+ WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
+ if (!(type in this.__events)) {
+ this.__events[type] = [];
+ }
+ this.__events[type].push(listener);
+ };
+
+ /**
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
+ *
+ * @param {string} type
+ * @param {function} listener
+ * @param {boolean} useCapture
+ * @return void
+ */
+ WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
+ if (!(type in this.__events)) return;
+ var events = this.__events[type];
+ for (var i = events.length - 1; i >= 0; --i) {
+ if (events[i] === listener) {
+ events.splice(i, 1);
+ break;
+ }
+ }
+ };
+
+ /**
+ * Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
+ *
+ * @param {Event} event
+ * @return void
+ */
+ WebSocket.prototype.dispatchEvent = function(event) {
+ var events = this.__events[event.type] || [];
+ for (var i = 0; i < events.length; ++i) {
+ events[i](event);
+ }
+ var handler = this["on" + event.type];
+ if (handler) handler.apply(this, [event]);
+ };
+
+ /**
+ * Handles an event from Flash.
+ * @param {Object} flashEvent
+ */
+ WebSocket.prototype.__handleEvent = function(flashEvent) {
+
+ if ("readyState" in flashEvent) {
+ this.readyState = flashEvent.readyState;
+ }
+ if ("protocol" in flashEvent) {
+ this.protocol = flashEvent.protocol;
+ }
+
+ var jsEvent;
+ if (flashEvent.type == "open" || flashEvent.type == "error") {
+ jsEvent = this.__createSimpleEvent(flashEvent.type);
+ } else if (flashEvent.type == "close") {
+ jsEvent = this.__createSimpleEvent("close");
+ jsEvent.wasClean = flashEvent.wasClean ? true : false;
+ jsEvent.code = flashEvent.code;
+ jsEvent.reason = flashEvent.reason;
+ } else if (flashEvent.type == "message") {
+ var data = decodeURIComponent(flashEvent.message);
+ jsEvent = this.__createMessageEvent("message", data);
+ } else {
+ throw "unknown event type: " + flashEvent.type;
+ }
+
+ this.dispatchEvent(jsEvent);
+
+ };
+
+ WebSocket.prototype.__createSimpleEvent = function(type) {
+ if (document.createEvent && window.Event) {
+ var event = document.createEvent("Event");
+ event.initEvent(type, false, false);
+ return event;
+ } else {
+ return {type: type, bubbles: false, cancelable: false};
+ }
+ };
+
+ WebSocket.prototype.__createMessageEvent = function(type, data) {
+ if (document.createEvent && window.MessageEvent && !window.opera) {
+ var event = document.createEvent("MessageEvent");
+ event.initMessageEvent("message", false, false, data, null, null, window, null);
+ return event;
+ } else {
+ // IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
+ return {type: type, data: data, bubbles: false, cancelable: false};
+ }
+ };
+
+ /**
+ * Define the WebSocket readyState enumeration.
+ */
+ WebSocket.CONNECTING = 0;
+ WebSocket.OPEN = 1;
+ WebSocket.CLOSING = 2;
+ WebSocket.CLOSED = 3;
+
+ WebSocket.__initialized = false;
+ WebSocket.__flash = null;
+ WebSocket.__instances = {};
+ WebSocket.__tasks = [];
+ WebSocket.__nextId = 0;
+
+ /**
+ * Load a new flash security policy file.
+ * @param {string} url
+ */
+ WebSocket.loadFlashPolicyFile = function(url){
+ WebSocket.__addTask(function() {
+ WebSocket.__flash.loadManualPolicyFile(url);
+ });
+ };
+
+ /**
+ * Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
+ */
+ WebSocket.__initialize = function() {
+
+ if (WebSocket.__initialized) return;
+ WebSocket.__initialized = true;
+
+ if (WebSocket.__swfLocation) {
+ // For backword compatibility.
+ window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
+ }
+ if (!window.WEB_SOCKET_SWF_LOCATION) {
+ logger.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
+ return;
+ }
+ if (!window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR &&
+ !WEB_SOCKET_SWF_LOCATION.match(/(^|\/)WebSocketMainInsecure\.swf(\?.*)?$/) &&
+ WEB_SOCKET_SWF_LOCATION.match(/^\w+:\/\/([^\/]+)/)) {
+ var swfHost = RegExp.$1;
+ if (location.host != swfHost) {
+ logger.error(
+ "[WebSocket] You must host HTML and WebSocketMain.swf in the same host " +
+ "('" + location.host + "' != '" + swfHost + "'). " +
+ "See also 'How to host HTML file and SWF file in different domains' section " +
+ "in README.md. If you use WebSocketMainInsecure.swf, you can suppress this message " +
+ "by WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;");
+ }
+ }
+ var container = document.createElement("div");
+ container.id = "webSocketContainer";
+ // Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
+ // Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
+ // But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
+ // Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
+ // the best we can do as far as we know now.
+ container.style.position = "absolute";
+ if (WebSocket.__isFlashLite()) {
+ container.style.left = "0px";
+ container.style.top = "0px";
+ } else {
+ container.style.left = "-100px";
+ container.style.top = "-100px";
+ }
+ var holder = document.createElement("div");
+ holder.id = "webSocketFlash";
+ container.appendChild(holder);
+ document.body.appendChild(container);
+ // See this article for hasPriority:
+ // http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
+ swfobject.embedSWF(
+ WEB_SOCKET_SWF_LOCATION,
+ "webSocketFlash",
+ "1" /* width */,
+ "1" /* height */,
+ "9.0.0" /* SWF version */,
+ null,
+ null,
+ {hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
+ null,
+ function(e) {
+ if (!e.success) {
+ logger.error("[WebSocket] swfobject.embedSWF failed");
+ }
+ }
+ );
+
+ };
+
+ /**
+ * Called by Flash to notify JS that it's fully loaded and ready
+ * for communication.
+ */
+ WebSocket.__onFlashInitialized = function() {
+ // We need to set a timeout here to avoid round-trip calls
+ // to flash during the initialization process.
+ setTimeout(function() {
+ WebSocket.__flash = document.getElementById("webSocketFlash");
+ WebSocket.__flash.setCallerUrl(location.href);
+ WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
+ for (var i = 0; i < WebSocket.__tasks.length; ++i) {
+ WebSocket.__tasks[i]();
+ }
+ WebSocket.__tasks = [];
+ }, 0);
+ };
+
+ /**
+ * Called by Flash to notify WebSockets events are fired.
+ */
+ WebSocket.__onFlashEvent = function() {
+ setTimeout(function() {
+ try {
+ // Gets events using receiveEvents() instead of getting it from event object
+ // of Flash event. This is to make sure to keep message order.
+ // It seems sometimes Flash events don't arrive in the same order as they are sent.
+ var events = WebSocket.__flash.receiveEvents();
+ for (var i = 0; i < events.length; ++i) {
+ WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
+ }
+ } catch (e) {
+ logger.error(e);
+ }
+ }, 0);
+ return true;
+ };
+
+ // Called by Flash.
+ WebSocket.__log = function(message) {
+ logger.log(decodeURIComponent(message));
+ };
+
+ // Called by Flash.
+ WebSocket.__error = function(message) {
+ logger.error(decodeURIComponent(message));
+ };
+
+ WebSocket.__addTask = function(task) {
+ if (WebSocket.__flash) {
+ task();
+ } else {
+ WebSocket.__tasks.push(task);
+ }
+ };
+
+ /**
+ * Test if the browser is running flash lite.
+ * @return {boolean} True if flash lite is running, false otherwise.
+ */
+ WebSocket.__isFlashLite = function() {
+ if (!window.navigator || !window.navigator.mimeTypes) {
+ return false;
+ }
+ var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
+ if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
+ return false;
+ }
+ return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
+ };
+
+ if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
+ // NOTE:
+ // This fires immediately if web_socket.js is dynamically loaded after
+ // the document is loaded.
+ swfobject.addDomLoadEvent(function() {
+ WebSocket.__initialize();
+ });
+ }
+
+})();
View
479 src/p1pp.js
@@ -0,0 +1,479 @@
+/**
+ * P1PP library
+ * Author: Eric Cestari <ecestari@process-one.net>
+ *
+ * This library will open an XMPP socket using the best transport available.
+ * Once connection has been established, it will suscribe to PubSub nodes on the given service
+ * For each publication event, it will call the user provided `publish(id, item, delayed)` function.
+ *
+ */
+
+/**
+ * P1PP. The main class
+ *
+ **/
+P1PP.prototype = {
+ /**
+ * @param {Object} params the configuration parameters. See Readme for usage
+ * @param {Boolean} fallback Used internally. if connection fails and timeout code is triggered it will set this to true.
+ */
+ connect: function(fallback){
+ var self = this,
+ params = this.params;
+ if(!!this.connection && this.connection.connected){
+ return;
+ }
+
+ this._check_rebind();
+ var _connect = function(){
+ self.retries ++;
+ if (self.retries > params.connectretry){
+ self.console.log(params.connectretry + " connect retry exceeded");
+ return;
+ }
+ self.timeout_id = setTimeout(function(){
+ self.connect(true);
+ }, params.connect_timeout);
+ if(!fallback && params.ws_url && window.WebSocket){
+ self.current_protocol = "WEBSOCKET";
+ self.websocket();
+ } else {
+ self.current_protocol = "BOSH";
+ self.bosh();
+ }
+ };
+ // Setting up connection delay
+ if((this.retries == 0) && !fallback){
+ setTimeout(_connect.bind(this), params.connect_delay);
+ } else {
+ _connect();
+ }
+ },
+ /**
+ * Closes connection to server
+ */
+ disconnect: function(){
+ this.closing=true;
+ window.clearTimeout(this.timeout_id);
+ this.rebind_delete();
+ if(this.connection){
+ this.connection.deleteTimedHandler(this.bosh_rebind_id);
+ this.connection.disconnect();
+ }
+ },
+
+ /**
+ * BOSH connection code.
+ * Will attempt to re-attach is param is set and attached value stored on the client.
+ */
+ bosh: function(){
+ try{
+ this.connection = new Strophe.Connection(this.params.bosh_url);
+ /* Reattach needs to be fixed on BOSH */
+ var cookie = this.rebind_fetch();
+ if(!!cookie && this.params.rebind){ //there's a cookie
+ prev_connection = cookie.split(" ");
+ if(prev_connection[0] == "BOSH"){ //Only BOSH cookies accepted
+ this.connection.attach(prev_connection[1],
+ prev_connection[2],
+ prev_connection[3],
+ this.conn_callback.bind(this))
+ } else {
+ this._transport_connect();
+ }
+ } else {
+ this._transport_connect();
+ }
+ } catch (e){
+ this.connect();
+ }
+
+ },
+
+ /**
+ * WebSocket connection code
+ * Will attempt to use fast rebind is param is set and rebind data is availble on client.
+ * @private
+ */
+ websocket: function(){
+ try{
+ this.connection = new Strophe.WebSocket(this.params.ws_url);
+ var cookie = this.rebind_fetch();
+ if(!!cookie && this.params.rebind){
+ prev_connection = cookie.split(" ");
+ this.connection.rebind(prev_connection[1],
+ prev_connection[2],
+ this.conn_callback.bind(this))
+ } else {
+ this._transport_connect();
+ }
+ } catch (e){
+ this.rebind_delete();
+ this.connect();
+ }
+ },
+
+ _transport_connect: function(){
+ var jid = this.params.jid ? this.params.jid : this.params.domain;
+ var password = this.params.password ? this.params.password : ""
+ this.connection.connect(jid,
+ password, this.conn_callback.bind(this));
+ },
+ /**
+ * Checks if rebind can be safely called
+ * Only useful in case of non-anon connections
+ */
+ _check_rebind: function(){
+ if(!this.params.rebind){
+ //rebind not active
+ return;
+ }
+ var cookie = this.rebind_fetch();
+ if(!!cookie && this.params.jid && this.params.jid !== ""){
+ var prev = cookie.split(" ");
+ var jid = Strophe.getBareJidFromJid(this.params.jid);
+ var prev_jid = Strophe.getBareJidFromJid(cookie.split(" ")[1]);
+ if(jid != prev_jid){
+ this.rebind_delete();
+ }
+ }
+ },
+ /**
+ * Main connection callback
+ * @param {Integer} status The StropheJS status of the connection.
+ * @private
+ */
+ conn_callback: function(status){
+ var that = this;
+ if(this.params.debug){
+ this.connection.rawOutput = function(elem){
+ that.console.log("out -> " + elem);
+ }
+ this.connection.rawInput = function(elem){
+ that.console.log("in <- " + elem);
+ }
+ }
+ // Connection established
+ window.clearTimeout(this.timeout_id);
+ this.bosh_rebind_id = this.connection.addTimedHandler(2000, function(){
+ if(that.current_protocol === "BOSH" && !that.closing){
+ var c = ["BOSH", that.connection.jid, that.connection.sid, that.connection.rid].join(" ");
+ that.rebind_store(c);
+ }
+ return true;
+ });
+ this.params.on_strophe_event(status, this.connection);
+ var login_required_cb = function(jid, pass){
+ that.params.jid=jid;
+ that.params.password=pass;
+ that.connect();
+ }
+ if (status === Strophe.Status.CONNECTED) {
+ this.params.on_connected();
+ this.retries = 0;
+ if(this.params.rebind == true
+ && this.current_protocol == "WEBSOCKET"){
+ this.connection.save(function(){
+ var id = ["WEBSOCKET",that.connection.jid, that.connection.streamId].join(" ")
+ that.rebind_store(id);
+ }, function(){});
+ }
+ this.subscribe();
+ }
+ // Connection re-attached (or rebound)
+ else if (status === Strophe.Status.ATTACHED){
+ // Forcing subscription. there is a problem with rebinding and anon subs in ejabberd
+ if(this.current_protocol == "WEBSOCKET"){
+ this.subscribe();
+ }
+ else{
+ // For some reason, I have to wait the second HTTP POST to actually get the result.
+ // ejabberd says data is sent, the browser says no, it's not.
+ // One of them is lying. Or both.
+ // In the meantime, a short timeout will do the trick.
+ setTimeout(function(){
+ nodes = that.params.nodes;
+ for(var i in nodes){
+ if(typeof nodes[i] == "string"){ // IE6 fix
+ // In case of attach, we need to set up the handlers again.
+ if(that.params.num_old > 0){
+ that.connection.pubsub.items(that.connection.jid, that.params.pubsub_domain, nodes[i], that.params.num_old, function(message){
+ var items = message.getElementsByTagName("item")
+ for(var i = 0; i < items.length; i++){
+ id = items[i].getAttribute("id");
+ that.params.publish(id, items[i].firstChild);
+ }
+ })
+ }
+ }
+ }
+ that.connection.addHandler(that.on_event.bind(that),
+ null,'message', null, null, that.params.pubsub_domain);
+ }, 100);
+
+ }
+
+ }
+ // Connection problem or reattach failed. Will attempt to reconnect after a random wait
+ else if (!this.closing
+ && (status === Strophe.Status.CONNFAIL
+ || status === Strophe.Status.DISCONNECTED)) {
+ this.connection.deleteTimedHandler(this.bosh_rebind_id);
+ this.rebind_delete();
+ //login is required. Give user code a chance to fetch jid and password
+ if(!this.params.jid && this.params.login_required){
+ this.params.on_login_required(login_required_cb)
+ } else {
+ var retry_time = Math.round(Math.random() * this.params.connect_timeout * (this.retries+1));
+ this.timeout_id = setTimeout(function(){
+ that.connect();
+ }, retry_time);
+ }
+ }
+ else if (status === Strophe.Status.DISCONNECTED){
+ this.connection.reset();
+ this.closing = false;
+ this.params.on_disconnect();
+ }
+ // WebSocket rebind failed. Removing user data and reconnecting
+ else if (status === Strophe.Status.REBINDFAILED){
+ this.rebind_delete();
+ this.connection = null;
+ this.connect();
+ }
+ else if (status === Strophe.Status.AUTHFAIL){
+ delete this.connection;
+ this.params.on_login_required(login_required_cb);
+ }
+ },
+ /**
+ * Subscribe to PubSub nodes
+ * If num_old is set, fetch older nodes.
+ * Warning: The last_published item is delivered twice, if both send_last_item configured on node and num_old >= 1
+ * @private
+ */
+ subscribe: function(nodes){
+ if(nodes === undefined){
+ nodes = this.params.nodes;
+ }
+ for(var i in nodes){
+ if(typeof nodes[i] == "string"){ // IE6 fix
+ if(this.params.num_old > 0){
+ var that = this;
+ this.connection.pubsub.items(this.connection.jid, this.params.pubsub_domain, nodes[i], this.params.num_old, function(message){
+ var items = message.getElementsByTagName("item")
+ for(var i = 0; i < items.length; i++){
+ id = items[i].getAttribute("id");
+ that.params.publish(id, items[i].firstChild);
+ }
+ })
+ }
+ this.connection.pubsub.subscribe(this.connection.jid,
+ this.params.pubsub_domain,
+ nodes[i],[],
+ function(){});
+ }
+ }
+ this.connection.addHandler(this.on_event.bind(this),
+ null,'message', null, null, this.params.pubsub_domain);
+ },
+
+ unsubscribe: function(nodes){
+ if(nodes === undefined){
+ nodes = this.params.nodes;
+ }
+ for(var i in nodes){
+ if(typeof nodes[i] == "string"){ // IE6 fix
+ this.connection.pubsub.unsubscribe(this.connection.jid,
+ this.params.pubsub_domain,
+ nodes[i],
+ function(){});
+ }
+ }
+ },
+
+ _extract_error_code: function(stanza) {
+ var error = stanza.getElementsByTagNameNS("http://jabber.org/protocol/pubsub#errors", "*");
+
+ if (!error.length)
+ error = stanza.getElementsByTagNameNS("urn:ietf:params:xml:ns:xmpp-stanzas", "*");
+
+ return error.length ? error[0].localName : null;
+ },
+
+ _publish: function(node, id, value, callback) {
+ var that = this;
+
+ if (id == null)
+ id = this.connection.getUniqueId("publish");
+
+ this.connection.pubsub.publish(this.connection.jid, this.params.pubsub_domain,
+ node, [{id: id, value: [value]}], function(stanza) {
+ that._done_publish(stanza, callback);
+ });
+ return id;
+ },
+
+ _done_publish: function(stanza, callback) {
+ var items = stanza.getElementsByTagName("item");
+ var id = items.length ? items[0].getAttribute("id") : null;
+
+ if (stanza.getAttribute("type") == "result")
+ callback(id, "ok");
+ else
+ callback(id, this._extract_error_code(stanza) || "error")
+ },
+
+ _deleteNode: function(node, callback) {
+ var that = this;
+
+ this.connection.pubsub.deleteNode(this.connection.jid, this.params.pubsub_domain,
+ node, function(stanza) {
+ that._done_delete(stanza, callback);
+ });
+ },
+
+ _done_delete: function(stanza, callback) {
+ if (stanza.getAttribute("type") == "result")
+ callback("ok");
+ else
+ callback(this._extract_error_code(stanza) || "error")
+ },
+
+ /**
+ * pubsub message handling.
+ * Triggered everytime there is an event coming from the server (publication or retraction of items)
+ * @param {DOMElement} msg the message from the server
+ * @private
+ **/
+ on_event: function(msg){
+ var retracts = msg.getElementsByTagName("retract");
+ var length =retracts.length
+ for(var i = 0; i < length; i++){
+ this.params.retract(retracts[i].getAttribute("id"));
+ }
+ var delay_time = undefined;
+ var delay = msg.getElementsByTagName("delay");
+ if(delay.length){
+ delay_time = delay[0].getAttribute("stamp");
+ }
+ var items = msg.getElementsByTagName("items");
+ length = items.length;
+ var node_name, node_items, ilength;
+ for(var i = 0; i < length; i++){
+ node_name = items[i].getAttribute("node");
+ node_items = items[i].getElementsByTagName("item");
+ ilength = node_items.length;
+ for(var i = 0; i < ilength; i++){
+ this.params.publish(node_items[i].getAttribute("id"),
+ node_items[i].firstChild,
+ node_name,
+ delay_time);
+ }
+ }
+ return true;
+ },
+
+ /**
+ * Cookie management code, taken from jquery.cookie.js <https://github.com/carhartl/jquery-cookie>
+ * @private
+ */
+ cookie: function(key, value){
+ if (typeof value != 'undefined'){
+ options = this.params.cookie_opts;
+ if (value === null) {
+ value = '';
+ options.expires = -1;
+ }
+ var expires = '';
+ if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
+ var date;
+ if (typeof options.expires == 'number') {
+ date = new Date();
+ date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
+ } else {
+ date = options.expires;
+ }
+ expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
+ }
+ // CAUTION: Needed to parenthesize options.path and options.domain
+ // in the following expressions, otherwise they evaluate to undefined
+ // in the packed version for some reason...
+ var path = options.path ? '; path=' + (options.path) : '';
+ var domain = options.domain ? '; domain=' + (options.domain) : '';
+ var secure = options.secure ? '; secure' : '';
+ document.cookie = [key, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
+
+ } else { // only name given, get cookie
+ var cookieValue = null;
+ if (document.cookie && document.cookie != '') {
+ var cookies = document.cookie.split(';');
+ for (var i = 0; i < cookies.length; i++) {
+ var trim = function( text ) {
+ if ( typeof String.trim === "function" ) {
+ return ( text || "" ).trim();
+ }
+ return (text || "").replace( /^\s\s*/, "" ).replace( /\s\s*$/, "" );
+ }
+ var cookie = trim(cookies[i]);
+ // Does this cookie string begin with the name we want?
+ if (cookie.substring(0, key.length + 1) == (key + '=')) {
+ cookieValue = decodeURIComponent(cookie.substring(key.length + 1));
+ break;
+ }
+ }
+ }
+ return cookieValue;
+ }
+
+ },
+ /**
+ * stores connection information in sessionStorage if available or cookie
+ * @private
+ */
+ rebind_store: function(value){
+ var key = this._build_key()
+ if(window.sessionStorage){
+ sessionStorage[key]=value;
+ } else {
+ this.cookie(key, value);
+ }
+ },
+ /**
+ * deletes connection information in sessionStorage if available or cookie
+ * @private
+ */
+ rebind_delete: function(){
+ var key = this._build_key()
+ if(window.sessionStorage){
+ sessionStorage.removeItem(key)
+ } else {
+ this.cookie(key, null);
+ }
+ },
+ /**
+ * fetchs connection information in sessionStorage if available or cookie
+ * @private
+ */
+ rebind_fetch: function(){
+ var key = this._build_key();
+ if(window.sessionStorage){
+ return sessionStorage[key];
+ } else {
+ return this.cookie(key);
+ }
+ },
+ /**
+ * Build key for protocol.
+ * BOSH connections can be scoped, for a different set of nodes
+ * Not necessary for WS as the client subscribes on reattach.
+ */
+ _build_key: function(){
+ var key = P1PP.COOKIE+ "_" + this.current_protocol;
+ if(this.current_protocol === "BOSH"){
+ key = key + "_" + this.scope;
+ }
+ return key;
+ }
+};
+
View
213 src/p1pp_defs.js
@@ -0,0 +1,213 @@
+var WEB_SOCKET_DEBUG = false;
+var WEB_SOCKET_DISABLE_AUTO_INITIALIZATION = true;
+
+var P1PP = function(params){
+ this.jid= null; //for non-anonymous connections
+ this.password= null; //
+ this.connection= null; // the strophe connection
+ this.params= null; // merge between default params and user provided params
+ this.timeout_id= null; // points to the timeout function triggering the BOSH connection
+ //if WS connection failed
+ this.retries=0; // How many retries already ?
+ this.closing=false;
+ this.defaults= {
+ flash_location: "WebSocketMain.swf",
+ domain: "p1pp.net",
+ ws_url: "ws://p1pp.net:5280/xmpp",
+ bosh_url: "ws://p1pp.net:5280/http-bind",
+ connect_timeout: 15000, //How long should we wait before trying BOSH ?
+ connect_delay: 0, //Connection will not be done before this number of ms
+ connect_retry: 10, //Connection max attempts
+ rebind: true, // attempt to reuse previous connection
+ debug: false, // Dump traffic in console
+ num_old: 0, // How many old items should be fetch ?
+ on_strophe_event: function(){}, // Connect callback. if additional XMPP exchanges are to be done with the server.
+ on_login_required: null,
+ publish: function(){}, // User provided call back.Called everytime an event is published.
+ retract: function(){}, // User provided call back. Called when an item is retracted.
+ on_disconnected: function(){}, // User Provided callback
+ on_connected: function(){},
+ cookie_opts: {},
+ nodes: []
+ };
+ var merge = function (o,ob) {var i = 0;for (var z in ob) {if (ob.hasOwnProperty(z)) {o[z] = ob[z];}}return o;}
+ this.params = merge(this.defaults, params);
+ if(!this.params.pubsub_domain){
+ this.params.pubsub_domain = "pubsub."+this.params.domain;
+ }
+
+ var nodes = this.params.nodes;
+ if(nodes.length > 0){
+ this.scope = MD5.hexdigest(nodes.join("-"));
+ }
+
+ if (!window.console || !window.console.log || !window.console.error) {
+ this.console = {log: function(){ }, error: function(){ }};
+ } else {
+ if(this.params.debug){
+ this.console = window.console;
+ }
+ }
+ if(this.params.debug){
+ window.WEB_SOCKET_DEBUG = true;
+ }
+ window.WEB_SOCKET_SWF_LOCATION=this.params.flash_location;
+ //initializing flash websockets (if necessary)
+ if(window.WebSocket && window.WebSocket.__initialize){
+ window.WebSocket.__initialize();
+ }
+ return this;
+}
+/**
+ * Connects to server and subscribes to select channels.
+ * @param {Object} params JSON object with configuration options (see below for attributes)
+ * @returns {Object} the P1PP instance used for the connection
+ *
+ * <h2>Parameters and their default value</h2>
+ * jid: "" if set, will connect with this JID and password instead anonymous
+ * password: "" see above
+ * ws_url: "ws://gitlive.com:5280/xmpp", websocket URL
+ * bosh_url: "http://gitlive.com:5280/http-bind", BOSH URL
+ * domain: "gitlive.com", Domain to logon to
+ * rebind: true, should use rebind if possible
+ * nodes: [], list of nodes to subscribe to
+ * num_old: 0, maximum number of old items to fetch
+ * flash_location: "WebSocketMain.swf", Location of the WebSocket flash file. Can be an URL
+ * connect_delay: 0 The client will attempt connection after this milliseconds
+ * connect_timeout: 3000 How long should we wait before fallback to BOSH ,
+ * connect_retry: 10, How many times should we try connecting
+ * pubsub_domain: "pubsub.gitlive.com", pubsub service url. defaults to pubsub.domain
+ * debug: false, Will dump traffic in console if true..
+ * publish: function(){}, publish callback
+ * retract: function(){} retract callback
+ * on_strophe_event: function(){} Access to StropheJS API events
+ * cookie_opts: {path: false, domain: false, expire:false, secure: false} cookies options if cookies are used
+ *
+ */
+P1PP.connect = function(params){
+ if(!this.push_client){
+ this.push_client = new P1PP(params);
+ this.push_client.connect();
+ } else if(this.push_client.connection.connected == false){
+ this.push_client.connect();
+ }
+ return this.push_client;
+ }
+
+/**
+ * Disconnect client
+ */
+P1PP.disconnect = function(){
+ var that = this;
+ this.push_client.disconnect()
+ }
+/**
+ * Subscribe to a channel or channels
+ * @param channels a string or an array of string each being a node to subscribe to
+ */
+P1PP.addChannel = function(channels){
+ if(this.push_client){
+ if(typeof channels === "string"){
+ channels = [channels];
+ }
+ var nodes = this.push_client.params.nodes;
+ this.merge(nodes, channels)
+ if(nodes.length > 0){
+ this.push_client.scope = MD5.hexdigest(nodes.join("-"));
+ }
+ this.push_client.subscribe(channels)
+ }
+}
+/**
+ * Unsubscribe from a channel or channels
+ * @param channels a string or an array of string each being a node to unsubscribe from
+ */
+P1PP.removeChannel = function(channels){
+ if(this.push_client){
+ if(typeof channels === "string"){
+ channels = [channels];
+ }
+ var nodes = P1PP.diff(this.push_client.params.nodes, channel);
+ if(nodes.length > 0){
+ this.push_client.scope = MD5.hexdigest(nodes.join("-"));
+ }
+ this.push_client.unsubscribe(channels)
+ }
+}
+
+/**
+ * Publishes data under given node
+ *
+ * @param {String} node - Name of node which should be used for
+ * publishing
+ * @param {String} id - Id of published data, if null random name is
+ * generated
+ * @param {DOMElement} data - Data to store
+ * @param {Function} callback - Callback called with (id, status_code)
+ * after receiving response from server, status_code "ok" is used for
+ * successfull operation
+ * @returns null if connection is not yet established, Id of published data otherwise
+ */
+P1PP.publish = function(node, id, value, callback) {
+ if (this.push_client)
+ return this.push_client._publish(node, id, value, callback);
+ return null;
+},
+
+/**
+ * Publishes data under given node
+ *
+ * @param {String} node - Name of node which should be used for
+ * publishing
+ * @param {String} id - Id of published data, if null random name is
+ * generated
+ * @param {DOMElement} data - Data to store
+ * @param {Function} callback - Callback called with (id, status_code)
+ * after receiving response from server, status_code "ok" is used for
+ * successfull operation
+ * @returns Id of published data
+ */
+P1PP.deleteNode = function(node, callback) {
+ if (this.push_client)
+ return this.push_client._deleteNode(node, callback);
+ return null;
+},
+
+P1PP.COOKIE = "session"; // Cookie or sessionstorage key used to store attach or fast rebind data.
+P1PP.couldRebind = function(){
+ var protocols = ["WEBSOCKET", "BOSH"];
+ for(p in protocols){
+ var key = P1PP.COOKIE+ "_" + protocols[p];
+ if(window.sessionStorage){
+ return !!sessionStorage[key];
+ } else {
+ return !!this.cookie(key);
+ }
+ }
+ }
+
+/**
+ * merges two arrays
+ * Taken from jQuery 1.5
+ */
+P1PP.merge = function( first, second ) {
+ var i = first.length,
+ j = 0;
+ if ( typeof second.length === "number" ) {
+ for ( var l = second.length; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+ first.length = i;
+ return first;
+ }
+/**
+ * Array diff
+ */
+P1PP.diff = function(first, second){
+ return first.filter(function(i) {return !(second.indexOf(i) > -1);});
+ }
View
1,824 src/strophe/strophe.bosh.js
@@ -0,0 +1,1824 @@
+/** Class: Strophe.Connection
+ * XMPP Connection manager.
+ *
+ * Thie class is the main part of Strophe. It manages a BOSH connection
+ * to an XMPP server and dispatches events to the user callbacks as
+ * data arrives. It supports SASL PLAIN, SASL DIGEST-MD5, and legacy
+ * authentication.
+ *
+ * After creating a Strophe.Connection object, the user will typically
+ * call connect() with a user supplied callback to handle connection level
+ * events like authentication failure, disconnection, or connection
+ * complete.
+ *
+ * The user will also have several event handlers defined by using
+ * addHandler() and addTimedHandler(). These will allow the user code to
+ * respond to interesting stanzas or do something periodically with the
+ * connection. These handlers will be active once authentication is
+ * finished.
+ *
+ * To send data to the connection, use send().
+ */
+
+/** Constructor: Strophe.Connection
+ * Create and initialize a Strophe.Connection object.
+ *
+ * Parameters:
+ * (String) service - The BOSH service URL.
+ *
+ * Returns:
+ * A new Strophe.Connection object.
+ */
+Strophe.Connection = function (service)
+{
+ /* The path to the httpbind service. */
+ this.service = service;
+ /* The connected JID. */
+ this.jid = "";
+ /* request id for body tags */
+ this.rid = Math.floor(Math.random() * 4294967295);
+ /* The current session ID. */
+ this.sid = null;
+ this.streamId = null;
+
+ // SASL
+ this.do_session = false;
+ this.do_bind = false;
+
+ // handler lists
+ this.timedHandlers = [];
+ this.handlers = [];
+ this.removeTimeds = [];
+ this.removeHandlers = [];
+ this.addTimeds = [];
+ this.addHandlers = [];
+
+ this._idleTimeout = null;
+ this._disconnectTimeout = null;
+
+ this.authenticated = false;
+ this.disconnecting = false;
+ this.connected = false;
+
+ this.errors = 0;
+
+ this.paused = false;
+
+ // default BOSH values
+ this.hold = 1;
+ this.wait = 60;
+ this.window = 5;
+
+ this._data = [];
+ this._requests = [];
+ this._uniqueId = Math.round(Math.random() * 10000);
+
+ this._sasl_success_handler = null;
+ this._sasl_failure_handler = null;
+ this._sasl_challenge_handler = null;
+
+ // setup onIdle callback every 1/10th of a second
+ this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);
+
+ // initialize plugins
+ for (var k in Strophe._connectionPlugins) {
+ if (Strophe._connectionPlugins.hasOwnProperty(k)) {
+ var ptype = Strophe._connectionPlugins[k];
+ // jslint complaints about the below line, but this is fine
+ var F = function () {};
+ F.prototype = ptype;
+ this[k] = new F();
+ this[k].init(this);
+ }
+ }
+};
+
+Strophe.Connection.prototype = {
+ /** Function: reset
+ * Reset the connection.
+ *
+ * This function should be called after a connection is disconnected
+ * before that connection is reused.
+ */
+ reset: function ()
+ {
+ this.rid = Math.floor(Math.random() * 4294967295);
+
+ this.sid = null;
+ this.streamId = null;
+
+ // SASL
+ this.do_session = false;
+ this.do_bind = false;
+
+ // handler lists
+ this.timedHandlers = [];
+ this.handlers = [];
+ this.removeTimeds = [];
+ this.removeHandlers = [];
+ this.addTimeds = [];
+ this.addHandlers = [];
+
+ this.authenticated = false;
+ this.disconnecting = false;
+ this.connected = false;
+
+ this.errors = 0;
+
+ this._requests = [];
+ this._uniqueId = Math.round(Math.random()*10000);
+ },
+
+ /** Function: pause
+ * Pause the request manager.
+ *
+ * This will prevent Strophe from sending any more requests to the
+ * server. This is very useful for temporarily pausing while a lot
+ * of send() calls are happening quickly. This causes Strophe to
+ * send the data in a single request, saving many request trips.
+ */
+ pause: function ()
+ {
+ this.paused = true;
+ },
+
+ /** Function: resume
+ * Resume the request manager.
+ *
+ * This resumes after pause() has been called.
+ */
+ resume: function ()
+ {
+ this.paused = false;
+ },
+
+ /** Function: getUniqueId
+ * Generate a unique ID for use in <iq/> elements.
+ *
+ * All <iq/> stanzas are required to have unique id attributes. This
+ * function makes creating these easy. Each connection instance has
+ * a counter which starts from zero, and the value of this counter
+ * plus a colon followed by the suffix becomes the unique id. If no
+ * suffix is supplied, the counter is used as the unique id.
+ *
+ * Suffixes are used to make debugging easier when reading the stream
+ * data, and their use is recommended. The counter resets to 0 for
+ * every new connection for the same reason. For connections to the
+ * same server that authenticate the same way, all the ids should be
+ * the same, which makes it easy to see changes. This is useful for
+ * automated testing as well.
+ *
+ * Parameters:
+ * (String) suffix - A optional suffix to append to the id.
+ *
+ * Returns:
+ * A unique string to be used for the id attribute.
+ */
+ getUniqueId: function (suffix)
+ {
+ if (typeof(suffix) == "string" || typeof(suffix) == "number") {
+ return ++this._uniqueId + ":" + suffix;
+ } else {
+ return ++this._uniqueId + "";
+ }
+ },
+
+ /** Function: connect
+ * Starts the connection process.
+ *
+ * As the connection process proceeds, the user supplied callback will
+ * be triggered multiple times with status updates. The callback
+ * should take two arguments - the status code and the error condition.
+ *
+ * The status code will be one of the values in the Strophe.Status
+ * constants. The error condition will be one of the conditions
+ * defined in RFC 3920 or the condition 'strophe-parsererror'.
+ *
+ * Please see XEP 124 for a more detailed explanation of the optional
+ * parameters below.
+ *
+ * Parameters:
+ * (String) jid - The user's JID. This may be a bare JID,
+ * or a full JID. If a node is not supplied, SASL ANONYMOUS
+ * authentication will be attempted.
+ * (String) pass - The user's password.
+ * (Function) callback The connect callback function.
+ * (Integer) wait - The optional HTTPBIND wait value. This is the
+ * time the server will wait before returning an empty result for
+ * a request. The default setting of 60 seconds is recommended.
+ * Other settings will require tweaks to the Strophe.TIMEOUT value.
+ * (Integer) hold - The optional HTTPBIND hold value. This is the
+ * number of connections the server will hold at one time. This
+ * should almost always be set to 1 (the default).
+ */
+ connect: function (jid, pass, callback, wait, hold)
+ {
+ this.jid = jid;
+ this.pass = pass;
+ this.connect_callback = callback;
+ this.disconnecting = false;
+ this.connected = false;
+ this.authenticated = false;
+ this.errors = 0;
+
+ this.wait = wait || this.wait;
+ this.hold = hold || this.hold;
+
+ // parse jid for domain and resource
+ this.domain = Strophe.getDomainFromJid(this.jid);
+
+ // build the body tag
+ var body = this._buildBody().attrs({
+ to: this.domain,
+ "xml:lang": "en",
+ wait: this.wait,
+ hold: this.hold,
+ content: "text/xml; charset=utf-8",
+ ver: "1.6",
+ "xmpp:version": "1.0",
+ "xmlns:xmpp": Strophe.NS.BOSH
+ });
+
+ this._changeConnectStatus(Strophe.Status.CONNECTING, null);
+ this._requests.push(
+ new Strophe.Request(body.tree(),
+ this._onRequestStateChange.bind(this)
+ .prependArg(this._connect_cb.bind(this)),
+ body.tree().rid));
+ this._throttledRequestHandler();
+ },
+
+ /** Function: attach
+ * Attach to an already created and authenticated BOSH session.
+ *
+ * This function is provided to allow Strophe to attach to BOSH
+ * sessions which have been created externally, perhaps by a Web
+ * application. This is often used to support auto-login type features
+ * without putting user credentials into the page.
+ *
+ * Parameters:
+ * (String) jid - The full JID that is bound by the session.
+ * (String) sid - The SID of the BOSH session.
+ * (String) rid - The current RID of the BOSH session. This RID
+ * will be used by the next request.
+ * (Function) callback The connect callback function.
+ * (Integer) wait - The optional HTTPBIND wait value. This is the
+ * time the server will wait before returning an empty result for
+ * a request. The default setting of 60 seconds is recommended.
+ * Other settings will require tweaks to the Strophe.TIMEOUT value.
+ * (Integer) hold - The optional HTTPBIND hold value. This is the
+ * number of connections the server will hold at one time. This
+ * should almost always be set to 1 (the default).
+ * (Integer) wind - The optional HTTBIND window value. This is the
+ * allowed range of request ids that are valid. The default is 5.
+ */
+ attach: function (jid, sid, rid, callback, wait, hold, wind)
+ {
+ this.jid = jid;
+ this.sid = sid;
+ this.rid = rid;
+ this.connect_callback = callback;
+
+ this.domain = Strophe.getDomainFromJid(this.jid);
+
+ this.authenticated = true;
+ this.connected = true;
+
+ this.wait = wait || this.wait;
+ this.hold = hold || this.hold;
+ this.window = wind || this.window;
+
+ this._changeConnectStatus(Strophe.Status.ATTACHED, null);
+ },
+
+ /** Function: xmlInput
+ * User overrideable function that receives XML data coming into the
+ * connection.
+ *
+ * The default function does nothing. User code can override this with
+ * > Strophe.Connection.xmlInput = function (elem) {
+ * > (user code)
+ * > };
+ *
+ * Parameters:
+ * (XMLElement) elem - The XML data received by the connection.
+ */
+ xmlInput: function (elem)
+ {
+ return;
+ },
+
+ /** Function: xmlOutput
+ * User overrideable function that receives XML data sent to the
+ * connection.
+ *
+ * The default function does nothing. User code can override this with
+ * > Strophe.Connection.xmlOutput = function (elem) {
+ * > (user code)
+ * > };
+ *
+ * Parameters:
+ * (XMLElement) elem - The XMLdata sent by the connection.
+ */
+ xmlOutput: function (elem)
+ {
+ return;
+ },
+
+ /** Function: rawInput
+ * User overrideable function that receives raw data coming into the
+ * connection.
+ *
+ * The default function does nothing. User code can override this with
+ * > Strophe.Connection.rawInput = function (data) {
+ * > (user code)
+ * > };
+ *
+ * Parameters:
+ * (String) data - The data received by the connection.
+ */
+ rawInput: function (data)
+ {
+ return;
+ },
+
+ /** Function: rawOutput
+ * User overrideable function that receives raw data sent to the
+ * connection.
+ *
+ * The default function does nothing. User code can override this with
+ * > Strophe.Connection.rawOutput = function (data) {
+ * > (user code)
+ * > };
+ *
+ * Parameters:
+ * (String) data - The data sent by the connection.
+ */
+ rawOutput: function (data)
+ {
+ return;
+ },
+
+ /** Function: send
+ * Send a stanza.
+ *
+ * This function is called to push data onto the send queue to
+ * go out over the wire. Whenever a request is sent to the BOSH
+ * server, all pending data is sent and the queue is flushed.
+ *
+ * Parameters:
+ * (XMLElement |
+ * [XMLElement] |
+ * Strophe.Builder) elem - The stanza to send.
+ */
+ send: function (elem)
+ {
+ if (elem === null) { return ; }
+ if (typeof(elem.sort) === "function") {
+ for (var i = 0; i < elem.length; i++) {
+ this._queueData(elem[i]);
+ }
+ } else if (typeof(elem.tree) === "function") {
+ this._queueData(elem.tree());
+ } else {
+ this._queueData(elem);
+ }
+
+ this._throttledRequestHandler();
+ clearTimeout(this._idleTimeout);
+ this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);
+ },
+
+ /** Function: flush
+ * Immediately send any pending outgoing data.
+ *
+ * Normally send() queues outgoing data until the next idle period
+ * (100ms), which optimizes network use in the common cases when
+ * several send()s are called in succession. flush() can be used to
+ * immediately send all pending data.
+ */
+ flush: function ()
+ {
+ // cancel the pending idle period and run the idle function
+ // immediately
+ clearTimeout(this._idleTimeout);
+ this._onIdle();
+ },
+
+ /** Function: sendIQ
+ * Helper function to send IQ stanzas.
+ *
+ * Parameters:
+ * (XMLElement) elem - The stanza to send.
+ * (Function) callback - The callback function for a successful request.
+ * (Function) errback - The callback function for a failed or timed
+ * out request. On timeout, the stanza will be null.
+ * (Integer) timeout - The time specified in milliseconds for a
+ * timeout to occur.
+ *
+ * Returns:
+ * The id used to send the IQ.
+ */
+ sendIQ: function(elem, callback, errback, timeout) {
+ var timeoutHandler = null;
+ var that = this;
+
+ if (typeof(elem.tree) === "function") {
+ elem = elem.tree();
+ }
+ var id = elem.getAttribute('id');
+
+ // inject id if not found
+ if (!id) {
+ id = this.getUniqueId("sendIQ");
+ elem.setAttribute("id", id);
+ }
+ var handler = this.addHandler(function (stanza) {
+ // remove timeout handler if there is one
+ if (timeoutHandler) {
+ that.deleteTimedHandler(timeoutHandler);
+ }
+ var iqtype = stanza.getAttribute('type');
+ if (iqtype == 'result') {
+ if (callback) {
+ callback(stanza);
+ }
+ } else if (iqtype == 'error') {
+ if (errback) {
+ errback(stanza);
+ }
+ } else {
+ throw {
+ name: "StropheError",
+ message: "Got bad IQ type of " + iqtype
+ };
+ }
+ }, null, 'iq', null, id);
+
+ // if timeout specified, setup timeout handler.
+ if (timeout) {
+ timeoutHandler = this.addTimedHandler(timeout, function () {
+ // get rid of normal handler
+ that.deleteHandler(handler);
+
+ // call errback on timeout with null stanza
+ if (errback) {
+ errback(null);
+ }
+ return false;
+ });
+ }
+
+ this.send(elem);
+
+ return id;
+ },
+
+ /** PrivateFunction: _queueData
+ * Queue outgoing data for later sending. Also ensures that the data
+ * is a DOMElement.
+ */
+ _queueData: function (element) {
+ if (element === null ||
+ !element.tagName ||
+ !element.childNodes) {
+ throw {
+ name: "StropheError",
+ message: "Cannot queue non-DOMElement."
+ };
+ }
+
+ this._data.push(element);
+ },
+
+ /** PrivateFunction: _sendRestart
+ * Send an xmpp:restart stanza.
+ */
+ _sendRestart: function ()
+ {
+ this._data.push("restart");
+
+ this._throttledRequestHandler();
+ clearTimeout(this._idleTimeout);
+ this._idleTimeout = setTimeout(this._onIdle.bind(this), 100);
+ },
+
+ /** Function: addTimedHandler
+ * Add a timed handler to the connection.
+ *
+ * This function adds a timed handler. The provided handler will
+ * be called every period milliseconds until it returns false,
+ * the connection is terminated, or the handler is removed. Handlers
+ * that wish to continue being invoked should return true.
+ *
+ * Because of method binding it is necessary to save the result of
+ * this function if you wish to remove a handler with
+ * deleteTimedHandler().
+ *
+ * Note that user handlers are not active until authentication is
+ * successful.
+ *
+ * Parameters:
+ * (Integer) period - The period of the handler.
+ * (Function) handler - The callback function.
+ *
+ * Returns:
+ * A reference to the handler that can be used to remove it.
+ */
+ addTimedHandler: function (period, handler)
+ {
+ var thand = new Strophe.TimedHandler(period, handler);
+ this.addTimeds.push(thand);
+ return thand;
+ },
+
+ /** Function: deleteTimedHandler
+ * Delete a timed handler for a connection.
+ *
+ * This function removes a timed handler from the connection. The
+ * handRef parameter is *not* the function passed to addTimedHandler(),
+ * but is the reference returned from addTimedHandler().
+ *
+ * Parameters:
+ * (Strophe.TimedHandler) handRef - The handler reference.
+ */
+ deleteTimedHandler: function (handRef)
+ {
+ // this must be done in the Idle loop so that we don't change
+ // the handlers during iteration
+ this.removeTimeds.push(handRef);
+ },
+
+ /** Function: addHandler
+ * Add a stanza handler for the connection.
+ *
+ * This function adds a stanza handler to the connection. The
+ * handler callback will be called for any stanza that matches
+ * the parameters. Note that if multiple parameters are supplied,
+ * they must all match for the handler to be invoked.
+ *
+ * The handler will receive the stanza that triggered it as its argument.
+ * The handler should return true if it is to be invoked again;
+ * returning false will remove the handler after it returns.
+ *
+ * As a convenience, the ns parameters applies to the top level element
+ * and also any of its immediate children. This is primarily to make
+ * matching /iq/query elements easy.
+ *
+ * The options argument contains handler matching flags that affect how
+ * matches are determined. Currently the only flag is matchBare (a
+ * boolean). When matchBare is true, the from parameter and the from
+ * attribute on the stanza will be matched as bare JIDs instead of
+ * full JIDs. To use this, pass {matchBare: true} as the value of
+ * options. The default value for matchBare is false.
+ *
+ * The return value should be saved if you wish to remove the handler
+ * with deleteHandler().
+ *
+ * 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.
+ */
+ addHandler: function (handler, ns, name, type, id, from, options)
+ {
+ var hand = new Strophe.Handler(handler, ns, name, type, id, from, options);
+ this.addHandlers.push(hand);
+ return hand;
+ },
+
+ /** Function: deleteHandler
+ * Delete a stanza handler for a connection.
+ *
+ * This function removes a stanza handler from the connection. The
+ * handRef parameter is *not* the function passed to addHandler(),
+ * but is the reference returned from addHandler().
+ *
+ * Parameters:
+ * (Strophe.Handler) handRef - The handler reference.
+ */
+ deleteHandler: function (handRef)
+ {
+ // this must be done in the Idle loop so that we don't change
+ // the handlers during iteration
+ this.removeHandlers.push(handRef);
+ },
+
+ /** Function: disconnect
+ * Start the graceful disconnection process.
+ *
+ * This function starts the disconnection process. This process starts
+ * by sending unavailable presence and sending BOSH body of type
+ * terminate. A timeout handler makes sure that disconnection happens
+ * even if the BOSH server does not respond.
+ *
+ * The user supplied connection callback will be notified of the
+ * progress as this process happens.
+ *
+ * Parameters:
+ * (String) reason - The reason the disconnect is occuring.
+ */
+ disconnect: function (reason)
+ {
+ this._changeConnectStatus(Strophe.Status.DISCONNECTING, reason);
+
+ Strophe.info("Disconnect was called because: " + reason);
+ if (this.connected) {
+ // setup timeout handler
+ this._disconnectTimeout = this._addSysTimedHandler(
+ 3000, this._onDisconnectTimeout.bind(this));
+ this._sendTerminate();
+ }
+ },
+
+ /** PrivateFunction: _changeConnectStatus
+ * _Private_ helper function that makes sure plugins and the user's
+ * callback are notified of connection status changes.
+ *
+ * Parameters:
+ * (Integer) status - the new connection status, one of the values
+ * in Strophe.Status
+ * (String) condition - the error condition or null
+ */
+ _changeConnectStatus: function (status, condition)
+ {
+ // notify all plugins listening for status changes
+ for (var k in Strophe._connectionPlugins) {
+ if (Strophe._connectionPlugins.hasOwnProperty(k)) {
+ var plugin = this[k];
+ if (plugin.statusChanged) {
+ try {
+ plugin.statusChanged(status, condition);
+ } catch (err) {
+ Strophe.error("" + k + " plugin caused an exception " +
+ "changing status: " + err);
+ }
+ }
+ }
+ }
+
+ // notify the user's callback
+ if (this.connect_callback) {
+ try {
+ this.connect_callback(status, condition);
+ } catch (e) {
+ Strophe.error("User connection callback caused an " +
+ "exception: " + e);
+ }
+ }
+ },
+
+ /** PrivateFunction: _buildBody
+ * _Private_ helper function to generate the <body/> wrapper for BOSH.
+ *
+ * Returns:
+ * A Strophe.Builder with a <body/> element.
+ */
+ _buildBody: function ()
+ {
+ var bodyWrap = $build('body', {
+ rid: this.rid++,
+ xmlns: Strophe.NS.HTTPBIND
+ });
+
+ if (this.sid !== null) {
+ bodyWrap.attrs({sid: this.sid});
+ }
+
+ return bodyWrap;
+ },
+
+ /** PrivateFunction: _removeRequest
+ * _Private_ function to remove a request from the queue.
+ *
+ * Parameters:
+ * (Strophe.Request) req - The request to remove.
+ */
+ _removeRequest: function (req)
+ {
+ Strophe.debug("removing request");
+
+ var i;
+ for (i = this._requests.length - 1; i >= 0; i--) {
+ if (req == this._requests[i]) {
+ this._requests.splice(i, 1);
+ }
+ }
+
+ // IE6 fails on setting to null, so set to empty function
+ req.xhr.onreadystatechange = function () {};
+
+ this._throttledRequestHandler();
+ },
+
+ /** PrivateFunction: _restartRequest
+ * _Private_ function to restart a request that is presumed dead.
+ *
+ * Parameters:
+ * (Integer) i - The index of the request in the queue.
+ */
+ _restartRequest: function (i)
+ {
+ var req = this._requests[i];
+ if (req.dead === null) {
+ req.dead = new Date();
+ }
+
+ this._processRequest(i);
+ },
+
+ /** PrivateFunction: _processRequest
+ * _Private_ function to process a request in the queue.
+ *
+ * This function takes requests off the queue and sends them and
+ * restarts dead requests.
+ *
+ * Parameters:
+ * (Integer) i - The index of the request in the queue.
+ */
+ _processRequest: function (i)
+ {
+ var req = this._requests[i];
+ var reqStatus = -1;
+
+ try {
+ if (req.xhr.readyState == 4) {
+ reqStatus = req.xhr.status;
+ }
+ } catch (e) {
+ Strophe.error("caught an error in _requests[" + i +
+ "], reqStatus: " + reqStatus);
+ }
+
+ if (typeof(reqStatus) == "undefined") {
+ reqStatus = -1;
+ }
+
+ var time_elapsed = req.age();
+ var primaryTimeout = (!isNaN(time_elapsed) &&
+ time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait));
+ var secondaryTimeout = (req.dead !== null &&
+ req.timeDead() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait));
+ var requestCompletedWithServerError = (req.xhr.readyState == 4 &&
+ (reqStatus < 1 ||
+ reqStatus >= 500));
+ if (primaryTimeout || secondaryTimeout ||
+ requestCompletedWithServerError) {
+ if (secondaryTimeout) {
+ Strophe.error("Request " +
+ this._requests[i].id +
+ " timed out (secondary), restarting");
+ }
+ req.abort = true;
+ req.xhr.abort();
+ // setting to null fails on IE6, so set to empty function
+ req.xhr.onreadystatechange = function () {};
+ this._requests[i] = new Strophe.Request(req.xmlData,
+ req.origFunc,
+ req.rid,
+ req.sends);
+ req = this._requests[i];
+ }
+
+ if (req.xhr.readyState === 0) {
+ Strophe.debug("request id " + req.id +
+ "." + req.sends + " posting");
+
+ req.date = new Date();
+ try {
+ req.xhr.open("POST", this.service, true);
+ } catch (e2) {
+ Strophe.error("XHR open failed.");
+ if (!this.connected) {
+ this._changeConnectStatus(Strophe.Status.CONNFAIL,
+ "bad-service");
+ }
+ this.disconnect();
+ return;
+ }
+
+ // Fires the XHR request -- may be invoked immediately
+ // or on a gradually expanding retry window for reconnects
+ var sendFunc = function () {
+ req.xhr.send(req.data);
+ };
+
+ // Implement progressive backoff for reconnects --
+ // First retry (send == 1) should also be instantaneous
+ if (req.sends > 1) {
+ // Using a cube of the retry number creats a nicely
+ // expanding retry window
+ var backoff = Math.pow(req.sends, 3) * 1000;
+ setTimeout(sendFunc, backoff);
+ } else {
+ sendFunc();
+ }
+
+ req.sends++;
+
+ this.xmlOutput(req.xmlData);
+ this.rawOutput(req.data);
+ } else {
+ Strophe.debug("_processRequest: " +
+ (i === 0 ? "first" : "second") +
+ " request has readyState of " +
+ req.xhr.readyState);
+ }
+ },
+
+ /** PrivateFunction: _throttledRequestHandler
+ * _Private_ function to throttle requests to the connection window.
+ *
+ * This function makes sure we don't send requests so fast that the
+ * request ids overflow the connection window in the case that one
+ * request died.
+ */
+ _throttledRequestHandler: function ()
+ {
+ if (!this._requests) {
+ Strophe.debug("_throttledRequestHandler called with " +
+ "undefined requests");
+ } else {
+ Strophe.debug("_throttledRequestHandler called with " +
+ this._requests.length + " requests");
+ }
+
+ if (!this._requests || this._requests.length === 0) {
+ return;
+ }
+
+ if (this._requests.length > 0) {
+ this._processRequest(0);
+ }
+
+ if (this._requests.length > 1 &&
+ Math.abs(this._requests[0].rid -
+ this._requests[1].rid) < this.window - 1) {
+ this._processRequest(1);
+ }
+ },
+
+ /** PrivateFunction: _onRequestStateChange
+ * _Private_ handler for Strophe.Request state changes.
+ *
+ * This function is called when the XMLHttpRequest readyState changes.
+ * It contains a lot of error handling logic for the many ways that
+ * requests can fail, and calls the request callback when requests
+ * succeed.
+ *
+ * Parameters:
+ * (Function) func - The handler for the request.
+ * (Strophe.Request) req - The request that is changing readyState.
+ */
+ _onRequestStateChange: function (func, req)
+ {
+ Strophe.debug("request id " + req.id +
+ "." + req.sends + " state changed to " +
+ req.xhr.readyState);
+
+ if (req.abort) {
+ req.abort = false;
+ return;
+ }
+
+ // request complete
+ var reqStatus;
+ if (req.xhr.readyState == 4) {
+ reqStatus = 0;
+ try {
+ reqStatus = req.xhr.status;
+ } catch (e) {
+ // ignore errors from undefined status attribute. works
+ // around a browser bug
+ }
+
+ if (typeof(reqStatus) == "undefined") {
+ reqStatus = 0;
+ }
+
+ if (this.disconnecting) {
+ if (reqStatus >= 400) {
+ this._hitError(reqStatus);
+ return;
+ }
+ }
+
+ var reqIs0 = (this._requests[0] == req);
+ var reqIs1 = (this._requests[1] == req);
+
+ if ((reqStatus > 0 && reqStatus < 500) || req.sends > 5) {
+ // remove from internal queue
+ this._removeRequest(req);
+ Strophe.debug("request id " +
+ req.id +
+ " should now be removed");
+ }
+
+ // request succeeded
+ if (reqStatus == 200) {
+ // if request 1 finished, or request 0 finished and request
+ // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to
+ // restart the other - both will be in the first spot, as the