· 6 years ago · Jul 13, 2019, 10:44 AM
1
2 // Start thirdparty/jquery/jquery-1.5.min.js
3/*!
4 * jQuery JavaScript Library v1.5
5 * http://jquery.com/
6 *
7 * Copyright 2011, John Resig
8 * Dual licensed under the MIT or GPL Version 2 licenses.
9 * http://jquery.org/license
10 *
11 * Includes Sizzle.js
12 * http://sizzlejs.com/
13 * Copyright 2011, The Dojo Foundation
14 * Released under the MIT, BSD, and GPL Licenses.
15 *
16 * Date: Mon Jan 31 08:31:29 2011 -0500
17 */
18(function(a,b){function b$(a){return d.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}
19function bX(a){if(!bR[a]){var b=d("<"+a+">").appendTo("body"),c=b.css("display");b.remove();if(c==="none"||c==="")c="block";bR[a]=c}
20return bR[a]}
21function bW(a,b){var c={};d.each(bV.concat.apply([],bV.slice(0,b)),function(){c[this]=a});return c}
22function bJ(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var e=a.dataTypes,f=a.converters,g,h=e.length,i,j=e[0],k,l,m,n,o;for(g=1;g<h;g++){k=j,j=e[g];if(j==="*")j=k;else if(k!=="*"&&k!==j){l=k+" "+j,m=f[l]||f["* "+j];if(!m){o=b;for(n in f){i=n.split(" ");if(i[0]===k||i[0]==="*"){o=f[i[1]+" "+j];if(o){n=f[n],n===!0?m=o:o===!0&&(m=n);break}}}}!m&&!o&&d.error("No conversion from "+l.replace(" "," to ")),m!==!0&&(c=m?m(c):o(n(c)))}}
23return c}
24function bI(a,c,d){var e=a.contents,f=a.dataTypes,g=a.responseFields,h,i,j,k;for(i in g)i in d&&(c[g[i]]=d[i]);while(f[0]==="*")f.shift(),h===b&&(h=c.getResponseHeader("content-type"));if(h)for(i in e)if(e[i]&&e[i].test(h)){f.unshift(i);break}
25if(f[0]in d)j=f[0];else{for(i in d){if(!f[0]||a.converters[i+" "+f[0]]){j=i;break}
26k||(k=i)}
27j=j||k}
28if(j){j!==f[0]&&f.unshift(j);return d[j]}}
29function bH(a,b,c,e){d.isArray(b)&&b.length?d.each(b,function(b,f){c||bp.test(a)?e(a,f):bH(a+"["+(typeof f==="object"||d.isArray(f)?b:"")+"]",f,c,e)}):c||b==null||typeof b!=="object"?e(a,b):d.isArray(b)||d.isEmptyObject(b)?e(a,""):d.each(b,function(b,d){bH(a+"["+b+"]",d,c,e)})}
30function bG(a,c,d,e,f,g){f=f||c.dataTypes[0],g=g||{},g[f]=!0;var h=a[f],i=0,j=h?h.length:0,k=a===bD,l;for(;i<j&&(k||!l);i++)l=h[i](c,d,e),typeof l==="string"&&(g[l]?l=b:(c.dataTypes.unshift(l),l=bG(a,c,d,e,l,g)));(k||!l)&&!g["*"]&&(l=bG(a,c,d,e,"*",g));return l}
31function bF(a){return function(b,c){typeof b!=="string"&&(c=b,b="*");if(d.isFunction(c)){var e=b.toLowerCase().split(bz),f=0,g=e.length,h,i,j;for(;f<g;f++)h=e[f],j=/^\+/.test(h),j&&(h=h.substr(1)||"*"),i=a[h]=a[h]||[],i[j?"unshift":"push"](c)}}}
32function bn(a,b,c){var e=b==="width"?bh:bi,f=b==="width"?a.offsetWidth:a.offsetHeight;if(c==="border")return f;d.each(e,function(){c||(f-=parseFloat(d.css(a,"padding"+this))||0),c==="margin"?f+=parseFloat(d.css(a,"margin"+this))||0:f-=parseFloat(d.css(a,"border"+this+"Width"))||0});return f}
33function _(a,b){b.src?d.ajax({url:b.src,async:!1,dataType:"script"}):d.globalEval(b.text||b.textContent||b.innerHTML||""),b.parentNode&&b.parentNode.removeChild(b)}
34function $(a,b){if(b.nodeType===1){var c=b.nodeName.toLowerCase();b.clearAttributes(),b.mergeAttributes(a);if(c==="object")b.outerHTML=a.outerHTML;else if(c!=="input"||a.type!=="checkbox"&&a.type!=="radio"){if(c==="option")b.selected=a.defaultSelected;else if(c==="input"||c==="textarea")b.defaultValue=a.defaultValue}else a.checked&&(b.defaultChecked=b.checked=a.checked),b.value!==a.value&&(b.value=a.value);b.removeAttribute(d.expando)}}
35function Z(a,b){if(b.nodeType===1&&d.hasData(a)){var c=d.expando,e=d.data(a),f=d.data(b,e);if(e=e[c]){var g=e.events;f=f[c]=d.extend({},e);if(g){delete f.handle,f.events={};for(var h in g)for(var i=0,j=g[h].length;i<j;i++)d.event.add(b,h,g[h][i],g[h][i].data)}}}}
36function Y(a,b){return d.nodeName(a,"table")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}
37function O(a,b,c){if(d.isFunction(b))return d.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return d.grep(a,function(a,d){return a===b===c});if(typeof b==="string"){var e=d.grep(a,function(a){return a.nodeType===1});if(J.test(b))return d.filter(b,e,!c);b=d.filter(b,e)}
38return d.grep(a,function(a,e){return d.inArray(a,b)>=0===c})}
39function N(a){return!a||!a.parentNode||a.parentNode.nodeType===11}
40function F(a,b){return(a&&a!=="*"?a+".":"")+b.replace(q,"`").replace(r,"&")}
41function E(a){var b,c,e,f,g,h,i,j,k,l,m,n,p,q=[],r=[],s=d._data(this,u);typeof s==="function"&&(s=s.events);if(a.liveFired!==this&&s&&s.live&&!a.target.disabled&&(!a.button||a.type!=="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var t=s.live.slice(0);for(i=0;i<t.length;i++)g=t[i],g.origType.replace(o,"")===a.type?r.push(g.selector):t.splice(i--,1);f=d(a.target).closest(r,a.currentTarget);for(j=0,k=f.length;j<k;j++){m=f[j];for(i=0;i<t.length;i++){g=t[i];if(m.selector===g.selector&&(!n||n.test(g.namespace))){h=m.elem,e=null;if(g.preType==="mouseenter"||g.preType==="mouseleave")a.type=g.preType,e=d(a.relatedTarget).closest(g.selector)[0];(!e||e!==h)&&q.push({elem:h,handleObj:g,level:m.level})}}}
42for(j=0,k=q.length;j<k;j++){f=q[j];if(c&&f.level>c)break;a.currentTarget=f.elem,a.data=f.handleObj.data,a.handleObj=f.handleObj,p=f.handleObj.origHandler.apply(f.elem,arguments);if(p===!1||a.isPropagationStopped()){c=f.level,p===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}
43return b}}
44function C(a,b,c){c[0].type=a;return d.event.handle.apply(b,c)}
45function w(){return!0}
46function v(){return!1}
47function f(a,c,f){if(f===b&&a.nodeType===1){f=a.getAttribute("data-"+c);if(typeof f==="string"){try{f=f==="true"?!0:f==="false"?!1:f==="null"?null:d.isNaN(f)?e.test(f)?d.parseJSON(f):f:parseFloat(f)}catch(g){}
48d.data(a,c,f)}else f=b}
49return f}
50var c=a.document,d=function(){function I(){if(!d.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(I,1);return}
51d.ready()}}
52var d=function(a,b){return new d.fn.init(a,b,g)},e=a.jQuery,f=a.$,g,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,i=/\S/,j=/^\s+/,k=/\s+$/,l=/\d/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=navigator.userAgent,w,x=!1,y,z="then done fail isResolved isRejected promise".split(" "),A,B=Object.prototype.toString,C=Object.prototype.hasOwnProperty,D=Array.prototype.push,E=Array.prototype.slice,F=String.prototype.trim,G=Array.prototype.indexOf,H={};d.fn=d.prototype={constructor:d,init:function(a,e,f){var g,i,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}
53if(a==="body"&&!e&&c.body){this.context=c,this[0]=c.body,this.selector="body",this.length=1;return this}
54if(typeof a==="string"){g=h.exec(a);if(!g||!g[1]&&e)return!e||e.jquery?(e||f).find(a):this.constructor(e).find(a);if(g[1]){e=e instanceof d?e[0]:e,k=e?e.ownerDocument||e:c,j=m.exec(a),j?d.isPlainObject(e)?(a=[c.createElement(j[1])],d.fn.attr.call(a,e,!0)):a=[k.createElement(j[1])]:(j=d.buildFragment([g[1]],[k]),a=(j.cacheable?d.clone(j.fragment):j.fragment).childNodes);return d.merge(this,a)}
55i=c.getElementById(g[2]);if(i&&i.parentNode){if(i.id!==g[2])return f.find(a);this.length=1,this[0]=i}
56this.context=c,this.selector=a;return this}
57if(d.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return d.makeArray(a,this)},selector:"",jquery:"1.5",length:0,size:function(){return this.length},toArray:function(){return E.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var e=this.constructor();d.isArray(a)?D.apply(e,a):d.merge(e,a),e.prevObject=this,e.context=this.context,b==="find"?e.selector=this.selector+(this.selector?" ":"")+c:b&&(e.selector=this.selector+"."+b+"("+c+")");return e},each:function(a,b){return d.each(this,a,b)},ready:function(a){d.bindReady(),y.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(E.apply(this,arguments),"slice",E.call(arguments).join(","))},map:function(a){return this.pushStack(d.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:D,sort:[].sort,splice:[].splice},d.fn.init.prototype=d.fn,d.extend=d.fn.extend=function(){var a,c,e,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i==="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!=="object"&&!d.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j<k;j++)if((a=arguments[j])!=null)for(c in a){e=i[c],f=a[c];if(i===f)continue;l&&f&&(d.isPlainObject(f)||(g=d.isArray(f)))?(g?(g=!1,h=e&&d.isArray(e)?e:[]):h=e&&d.isPlainObject(e)?e:{},i[c]=d.extend(l,h,f)):f!==b&&(i[c]=f)}
58return i},d.extend({noConflict:function(b){a.$=f,b&&(a.jQuery=e);return d},isReady:!1,readyWait:1,ready:function(a){a===!0&&d.readyWait--;if(!d.readyWait||a!==!0&&!d.isReady){if(!c.body)return setTimeout(d.ready,1);d.isReady=!0;if(a!==!0&&--d.readyWait>0)return;y.resolveWith(c,[d]),d.fn.trigger&&d(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!x){x=!0;if(c.readyState==="complete")return setTimeout(d.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",A,!1),a.addEventListener("load",d.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",A),a.attachEvent("onload",d.ready);var b=!1;try{b=a.frameElement==null}catch(e){}
59c.documentElement.doScroll&&b&&I()}}},isFunction:function(a){return d.type(a)==="function"},isArray:Array.isArray||function(a){return d.type(a)==="array"},isWindow:function(a){return a&&typeof a==="object"&&"setInterval"in a},isNaN:function(a){return a==null||!l.test(a)||isNaN(a)},type:function(a){return a==null?String(a):H[B.call(a)]||"object"},isPlainObject:function(a){if(!a||d.type(a)!=="object"||a.nodeType||d.isWindow(a))return!1;if(a.constructor&&!C.call(a,"constructor")&&!C.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a){}
60return c===b||C.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!=="string"||!b)return null;b=d.trim(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return a.JSON&&a.JSON.parse?a.JSON.parse(b):(new Function("return "+b))();d.error("Invalid JSON: "+b)},parseXML:function(b,c,e){a.DOMParser?(e=new DOMParser,c=e.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),e=c.documentElement,(!e||!e.nodeName||e.nodeName==="parsererror")&&d.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(a){if(a&&i.test(a)){var b=c.getElementsByTagName("head")[0]||c.documentElement,e=c.createElement("script");e.type="text/javascript",d.support.scriptEval()?e.appendChild(c.createTextNode(a)):e.text=a,b.insertBefore(e,b.firstChild),b.removeChild(e)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,e){var f,g=0,h=a.length,i=h===b||d.isFunction(a);if(e){if(i){for(f in a)if(c.apply(a[f],e)===!1)break}else for(;g<h;)if(c.apply(a[g++],e)===!1)break}else if(i){for(f in a)if(c.call(a[f],f,a[f])===!1)break}else for(var j=a[0];g<h&&c.call(j,g,j)!==!1;j=a[++g]){}
61return a},trim:F?function(a){return a==null?"":F.call(a)}:function(a){return a==null?"":(a+"").replace(j,"").replace(k,"")},makeArray:function(a,b){var c=b||[];if(a!=null){var e=d.type(a);a.length==null||e==="string"||e==="function"||e==="regexp"||d.isWindow(a)?D.call(c,a):d.merge(c,a)}
62return c},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var c=0,d=b.length;c<d;c++)if(b[c]===a)return c;return-1},merge:function(a,c){var d=a.length,e=0;if(typeof c.length==="number")for(var f=c.length;e<f;e++)a[d++]=c[e];else while(c[e]!==b)a[d++]=c[e++];a.length=d;return a},grep:function(a,b,c){var d=[],e;c=!!c;for(var f=0,g=a.length;f<g;f++)e=!!b(a[f],f),c!==e&&d.push(a[f]);return d},map:function(a,b,c){var d=[],e;for(var f=0,g=a.length;f<g;f++)e=b(a[f],f,c),e!=null&&(d[d.length]=e);return d.concat.apply([],d)},guid:1,proxy:function(a,c,e){arguments.length===2&&(typeof c==="string"?(e=a,a=e[c],c=b):c&&!d.isFunction(c)&&(e=c,c=b)),!c&&a&&(c=function(){return a.apply(e||this,arguments)}),a&&(c.guid=a.guid=a.guid||c.guid||d.guid++);return c},access:function(a,c,e,f,g,h){var i=a.length;if(typeof c==="object"){for(var j in c)d.access(a,j,c[j],f,g,e);return a}
63if(e!==b){f=!h&&f&&d.isFunction(e);for(var k=0;k<i;k++)g(a[k],c,f?e.call(a[k],k,g(a[k],c)):e,h);return a}
64return i?g(a[0],c):b},now:function(){return(new Date).getTime()},_Deferred:function(){var a=[],b,c,e,f={done:function(){if(!e){var c=arguments,g,h,i,j,k;b&&(k=b,b=0);for(g=0,h=c.length;g<h;g++)i=c[g],j=d.type(i),j==="array"?f.done.apply(f,i):j==="function"&&a.push(i);k&&f.resolveWith(k[0],k[1])}
65return this},resolveWith:function(d,f){if(!e&&!b&&!c){c=1;try{while(a[0])a.shift().apply(d,f)}finally{b=[d,f],c=0}}
66return this},resolve:function(){f.resolveWith(d.isFunction(this.promise)?this.promise():this,arguments);return this},isResolved:function(){return c||b},cancel:function(){e=1,a=[];return this}};return f},Deferred:function(a){var b=d._Deferred(),c=d._Deferred(),e;d.extend(b,{then:function(a,c){b.done(a).fail(c);return this},fail:c.done,rejectWith:c.resolveWith,reject:c.resolve,isRejected:c.isResolved,promise:function(a,c){if(a==null){if(e)return e;e=a={}}
67c=z.length;while(c--)a[z[c]]=b[z[c]];return a}}),b.then(c.cancel,b.cancel),delete b.cancel,a&&a.call(b,b);return b},when:function(a){var b=arguments,c=b.length,e=c<=1&&a&&d.isFunction(a.promise)?a:d.Deferred(),f=e.promise(),g;c>1?(g=Array(c),d.each(b,function(a,b){d.when(b).then(function(b){g[a]=arguments.length>1?E.call(arguments,0):b,--c||e.resolveWith(f,g)},e.reject)})):e!==a&&e.resolve(a);return f},uaMatch:function(a){a=a.toLowerCase();var b=r.exec(a)||s.exec(a)||t.exec(a)||a.indexOf("compatible")<0&&u.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},sub:function(){function a(b,c){return new a.fn.init(b,c)}
68d.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.subclass=this.subclass,a.fn.init=function b(b,c){c&&c instanceof d&&!(c instanceof a)&&(c=a(c));return d.fn.init.call(this,b,c,e)},a.fn.init.prototype=a.fn;var e=a(c);return a},browser:{}}),y=d._Deferred(),d.each("Boolean Number String Function Array Date RegExp Object".split(" "),function(a,b){H["[object "+b+"]"]=b.toLowerCase()}),w=d.uaMatch(v),w.browser&&(d.browser[w.browser]=!0,d.browser.version=w.version),d.browser.webkit&&(d.browser.safari=!0),G&&(d.inArray=function(a,b){return G.call(b,a)}),i.test(" ")&&(j=/^[\s\xA0]+/,k=/[\s\xA0]+$/),g=d(c),c.addEventListener?A=function(){c.removeEventListener("DOMContentLoaded",A,!1),d.ready()}:c.attachEvent&&(A=function(){c.readyState==="complete"&&(c.detachEvent("onreadystatechange",A),d.ready())});return a.jQuery=a.$=d}();(function(){d.support={};var b=c.createElement("div");b.style.display="none",b.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";var e=b.getElementsByTagName("*"),f=b.getElementsByTagName("a")[0],g=c.createElement("select"),h=g.appendChild(c.createElement("option"));if(e&&e.length&&f){d.support={leadingWhitespace:b.firstChild.nodeType===3,tbody:!b.getElementsByTagName("tbody").length,htmlSerialize:!!b.getElementsByTagName("link").length,style:/red/.test(f.getAttribute("style")),hrefNormalized:f.getAttribute("href")==="/a",opacity:/^0.55$/.test(f.style.opacity),cssFloat:!!f.style.cssFloat,checkOn:b.getElementsByTagName("input")[0].value==="on",optSelected:h.selected,deleteExpando:!0,optDisabled:!1,checkClone:!1,_scriptEval:null,noCloneEvent:!0,boxModel:null,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableHiddenOffsets:!0},g.disabled=!0,d.support.optDisabled=!h.disabled,d.support.scriptEval=function(){if(d.support._scriptEval===null){var b=c.documentElement,e=c.createElement("script"),f="script"+d.now();e.type="text/javascript";try{e.appendChild(c.createTextNode("window."+f+"=1;"))}catch(g){}
69b.insertBefore(e,b.firstChild),a[f]?(d.support._scriptEval=!0,delete a[f]):d.support._scriptEval=!1,b.removeChild(e),b=e=f=null}
70return d.support._scriptEval};try{delete b.test}catch(i){d.support.deleteExpando=!1}
71b.attachEvent&&b.fireEvent&&(b.attachEvent("onclick",function j(){d.support.noCloneEvent=!1,b.detachEvent("onclick",j)}),b.cloneNode(!0).fireEvent("onclick")),b=c.createElement("div"),b.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";var k=c.createDocumentFragment();k.appendChild(b.firstChild),d.support.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,d(function(){var a=c.createElement("div"),b=c.getElementsByTagName("body")[0];if(b){a.style.width=a.style.paddingLeft="1px",b.appendChild(a),d.boxModel=d.support.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,d.support.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="<div style='width:4px;'></div>",d.support.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";var e=a.getElementsByTagName("td");d.support.reliableHiddenOffsets=e[0].offsetHeight===0,e[0].style.display="",e[1].style.display="none",d.support.reliableHiddenOffsets=d.support.reliableHiddenOffsets&&e[0].offsetHeight===0,a.innerHTML="",b.removeChild(a).style.display="none",a=e=null}});var l=function(a){var b=c.createElement("div");a="on"+a;if(!b.attachEvent)return!0;var d=a in b;d||(b.setAttribute(a,"return;"),d=typeof b[a]==="function"),b=null;return d};d.support.submitBubbles=l("submit"),d.support.changeBubbles=l("change"),b=e=f=null}})();var e=/^(?:\{.*\}|\[.*\])$/;d.extend({cache:{},uuid:0,expando:"jQuery"+(d.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?d.cache[a[d.expando]]:a[d.expando];return!!a&&!d.isEmptyObject(a)},data:function(a,c,e,f){if(d.acceptData(a)){var g=d.expando,h=typeof c==="string",i,j=a.nodeType,k=j?d.cache:a,l=j?a[d.expando]:a[d.expando]&&d.expando;if((!l||f&&l&&!k[l][g])&&h&&e===b)return;l||(j?a[d.expando]=l=++d.uuid:l=d.expando),k[l]||(k[l]={}),typeof c==="object"&&(f?k[l][g]=d.extend(k[l][g],c):k[l]=d.extend(k[l],c)),i=k[l],f&&(i[g]||(i[g]={}),i=i[g]),e!==b&&(i[c]=e);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[c]:i}},removeData:function(b,c,e){if(d.acceptData(b)){var f=d.expando,g=b.nodeType,h=g?d.cache:b,i=g?b[d.expando]:d.expando;if(!h[i])return;if(c){var j=e?h[i][f]:h[i];if(j){delete j[c];if(!d.isEmptyObject(j))return}}
72if(e){delete h[i][f];if(!d.isEmptyObject(h[i]))return}
73var k=h[i][f];d.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},h[i][f]=k):g&&(d.support.deleteExpando?delete b[d.expando]:b.removeAttribute?b.removeAttribute(d.expando):b[d.expando]=null)}},_data:function(a,b,c){return d.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=d.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}
74return!0}}),d.fn.extend({data:function(a,c){var e=null;if(typeof a==="undefined"){if(this.length){e=d.data(this[0]);if(this[0].nodeType===1){var g=this[0].attributes,h;for(var i=0,j=g.length;i<j;i++)h=g[i].name,h.indexOf("data-")===0&&(h=h.substr(5),f(this[0],h,e[h]))}}
75return e}
76if(typeof a==="object")return this.each(function(){d.data(this,a)});var k=a.split(".");k[1]=k[1]?"."+k[1]:"";if(c===b){e=this.triggerHandler("getData"+k[1]+"!",[k[0]]),e===b&&this.length&&(e=d.data(this[0],a),e=f(this[0],a,e));return e===b&&k[1]?this.data(k[0]):e}
77return this.each(function(){var b=d(this),e=[k[0],c];b.triggerHandler("setData"+k[1]+"!",e),d.data(this,a,c),b.triggerHandler("changeData"+k[1]+"!",e)})},removeData:function(a){return this.each(function(){d.removeData(this,a)})}}),d.extend({queue:function(a,b,c){if(a){b=(b||"fx")+"queue";var e=d._data(a,b);if(!c)return e||[];!e||d.isArray(c)?e=d._data(a,b,d.makeArray(c)):e.push(c);return e}},dequeue:function(a,b){b=b||"fx";var c=d.queue(a,b),e=c.shift();e==="inprogress"&&(e=c.shift()),e&&(b==="fx"&&c.unshift("inprogress"),e.call(a,function(){d.dequeue(a,b)})),c.length||d.removeData(a,b+"queue",!0)}}),d.fn.extend({queue:function(a,c){typeof a!=="string"&&(c=a,a="fx");if(c===b)return d.queue(this[0],a);return this.each(function(b){var e=d.queue(this,a,c);a==="fx"&&e[0]!=="inprogress"&&d.dequeue(this,a)})},dequeue:function(a){return this.each(function(){d.dequeue(this,a)})},delay:function(a,b){a=d.fx?d.fx.speeds[a]||a:a,b=b||"fx";return this.queue(b,function(){var c=this;setTimeout(function(){d.dequeue(c,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var g=/[\n\t\r]/g,h=/\s+/,i=/\r/g,j=/^(?:href|src|style)$/,k=/^(?:button|input)$/i,l=/^(?:button|input|object|select|textarea)$/i,m=/^a(?:rea)?$/i,n=/^(?:radio|checkbox)$/i;d.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"},d.fn.extend({attr:function(a,b){return d.access(this,a,b,!0,d.attr)},removeAttr:function(a,b){return this.each(function(){d.attr(this,a,""),this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.addClass(a.call(this,b,c.attr("class")))});if(a&&typeof a==="string"){var b=(a||"").split(h);for(var c=0,e=this.length;c<e;c++){var f=this[c];if(f.nodeType===1)if(f.className){var g=" "+f.className+" ",i=f.className;for(var j=0,k=b.length;j<k;j++)g.indexOf(" "+b[j]+" ")<0&&(i+=" "+b[j]);f.className=d.trim(i)}else f.className=a}}
78return this},removeClass:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.removeClass(a.call(this,b,c.attr("class")))});if(a&&typeof a==="string"||a===b){var c=(a||"").split(h);for(var e=0,f=this.length;e<f;e++){var i=this[e];if(i.nodeType===1&&i.className)if(a){var j=(" "+i.className+" ").replace(g," ");for(var k=0,l=c.length;k<l;k++)j=j.replace(" "+c[k]+" "," ");i.className=d.trim(j)}else i.className=""}}
79return this},toggleClass:function(a,b){var c=typeof a,e=typeof b==="boolean";if(d.isFunction(a))return this.each(function(c){var e=d(this);e.toggleClass(a.call(this,c,e.attr("class"),b),b)});return this.each(function(){if(c==="string"){var f,g=0,i=d(this),j=b,k=a.split(h);while(f=k[g++])j=e?j:!i.hasClass(f),i[j?"addClass":"removeClass"](f)}else if(c==="undefined"||c==="boolean")this.className&&d._data(this,"__className__",this.className),this.className=this.className||a===!1?"":d._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ";for(var c=0,d=this.length;c<d;c++)if((" "+this[c].className+" ").replace(g," ").indexOf(b)>-1)return!0;return!1},val:function(a){if(!arguments.length){var c=this[0];if(c){if(d.nodeName(c,"option")){var e=c.attributes.value;return!e||e.specified?c.value:c.text}
80if(d.nodeName(c,"select")){var f=c.selectedIndex,g=[],h=c.options,j=c.type==="select-one";if(f<0)return null;for(var k=j?f:0,l=j?f+1:h.length;k<l;k++){var m=h[k];if(m.selected&&(d.support.optDisabled?!m.disabled:m.getAttribute("disabled")===null)&&(!m.parentNode.disabled||!d.nodeName(m.parentNode,"optgroup"))){a=d(m).val();if(j)return a;g.push(a)}}
81return g}
82if(n.test(c.type)&&!d.support.checkOn)return c.getAttribute("value")===null?"on":c.value;return(c.value||"").replace(i,"")}
83return b}
84var o=d.isFunction(a);return this.each(function(b){var c=d(this),e=a;if(this.nodeType===1){o&&(e=a.call(this,b,c.val())),e==null?e="":typeof e==="number"?e+="":d.isArray(e)&&(e=d.map(e,function(a){return a==null?"":a+""}));if(d.isArray(e)&&n.test(this.type))this.checked=d.inArray(c.val(),e)>=0;else if(d.nodeName(this,"select")){var f=d.makeArray(e);d("option",this).each(function(){this.selected=d.inArray(d(this).val(),f)>=0}),f.length||(this.selectedIndex=-1)}else this.value=e}})}}),d.extend({attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,e,f){if(!a||a.nodeType===3||a.nodeType===8||a.nodeType===2)return b;if(f&&c in d.attrFn)return d(a)[c](e);var g=a.nodeType!==1||!d.isXMLDoc(a),h=e!==b;c=g&&d.props[c]||c;if(a.nodeType===1){var i=j.test(c);if(c==="selected"&&!d.support.optSelected){var n=a.parentNode;n&&(n.selectedIndex,n.parentNode&&n.parentNode.selectedIndex)}
85if((c in a||a[c]!==b)&&g&&!i){h&&(c==="type"&&k.test(a.nodeName)&&a.parentNode&&d.error("type property can't be changed"),e===null?a.nodeType===1&&a.removeAttribute(c):a[c]=e);if(d.nodeName(a,"form")&&a.getAttributeNode(c))return a.getAttributeNode(c).nodeValue;if(c==="tabIndex"){var o=a.getAttributeNode("tabIndex");return o&&o.specified?o.value:l.test(a.nodeName)||m.test(a.nodeName)&&a.href?0:b}
86return a[c]}
87if(!d.support.style&&g&&c==="style"){h&&(a.style.cssText=""+e);return a.style.cssText}
88h&&a.setAttribute(c,""+e);if(!a.attributes[c]&&(a.hasAttribute&&!a.hasAttribute(c)))return b;var p=!d.support.hrefNormalized&&g&&i?a.getAttribute(c,2):a.getAttribute(c);return p===null?b:p}
89h&&(a[c]=e);return a[c]}});var o=/\.(.*)$/,p=/^(?:textarea|input|select)$/i,q=/\./g,r=/ /g,s=/[^\w\s.|`]/g,t=function(a){return a.replace(s,"\\$&")},u="events";d.event={add:function(c,e,f,g){if(c.nodeType!==3&&c.nodeType!==8){d.isWindow(c)&&(c!==a&&!c.frameElement)&&(c=a);if(f===!1)f=v;else if(!f)return;var h,i;f.handler&&(h=f,f=h.handler),f.guid||(f.guid=d.guid++);var j=d._data(c);if(!j)return;var k=j[u],l=j.handle;typeof k==="function"?(l=k.handle,k=k.events):k||(c.nodeType||(j[u]=j=function(){}),j.events=k={}),l||(j.handle=l=function(){return typeof d!=="undefined"&&!d.event.triggered?d.event.handle.apply(l.elem,arguments):b}),l.elem=c,e=e.split(" ");var m,n=0,o;while(m=e[n++]){i=h?d.extend({},h):{handler:f,data:g},m.indexOf(".")>-1?(o=m.split("."),m=o.shift(),i.namespace=o.slice(0).sort().join(".")):(o=[],i.namespace=""),i.type=m,i.guid||(i.guid=f.guid);var p=k[m],q=d.event.special[m]||{};if(!p){p=k[m]=[];if(!q.setup||q.setup.call(c,g,o,l)===!1)c.addEventListener?c.addEventListener(m,l,!1):c.attachEvent&&c.attachEvent("on"+m,l)}
90q.add&&(q.add.call(c,i),i.handler.guid||(i.handler.guid=f.guid)),p.push(i),d.event.global[m]=!0}
91c=null}},global:{},remove:function(a,c,e,f){if(a.nodeType!==3&&a.nodeType!==8){e===!1&&(e=v);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=d.hasData(a)&&d._data(a),w=s&&s[u];if(!s||!w)return;typeof w==="function"&&(s=w,w=w.events),c&&c.type&&(e=c.handler,c=c.type);if(!c||typeof c==="string"&&c.charAt(0)==="."){c=c||"";for(h in w)d.event.remove(a,h+c);return}
92c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+d.map(m.slice(0).sort(),t).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=w[h];if(!p)continue;if(!e){for(j=0;j<p.length;j++){q=p[j];if(l||n.test(q.namespace))d.event.remove(a,r,q.handler,j),p.splice(j--,1)}
93continue}
94o=d.event.special[h]||{};for(j=f||0;j<p.length;j++){q=p[j];if(e.guid===q.guid){if(l||n.test(q.namespace))f==null&&p.splice(j--,1),o.remove&&o.remove.call(a,q);if(f!=null)break}}
95if(p.length===0||f!=null&&p.length===1)(!o.teardown||o.teardown.call(a,m)===!1)&&d.removeEvent(a,h,s.handle),g=null,delete w[h]}
96if(d.isEmptyObject(w)){var x=s.handle;x&&(x.elem=null),delete s.events,delete s.handle,typeof s==="function"?d.removeData(a,u,!0):d.isEmptyObject(s)&&d.removeData(a,b,!0)}}},trigger:function(a,c,e){var f=a.type||a,g=arguments[3];if(!g){a=typeof a==="object"?a[d.expando]?a:d.extend(d.Event(f),a):d.Event(f),f.indexOf("!")>=0&&(a.type=f=f.slice(0,-1),a.exclusive=!0),e||(a.stopPropagation(),d.event.global[f]&&d.each(d.cache,function(){var b=d.expando,e=this[b];e&&e.events&&e.events[f]&&d.event.trigger(a,c,e.handle.elem)}));if(!e||e.nodeType===3||e.nodeType===8)return b;a.result=b,a.target=e,c=d.makeArray(c),c.unshift(a)}
97a.currentTarget=e;var h=e.nodeType?d._data(e,"handle"):(d._data(e,u)||{}).handle;h&&h.apply(e,c);var i=e.parentNode||e.ownerDocument;try{e&&e.nodeName&&d.noData[e.nodeName.toLowerCase()]||e["on"+f]&&e["on"+f].apply(e,c)===!1&&(a.result=!1,a.preventDefault())}catch(j){}
98if(!a.isPropagationStopped()&&i)d.event.trigger(a,c,i,!0);else if(!a.isDefaultPrevented()){var k,l=a.target,m=f.replace(o,""),n=d.nodeName(l,"a")&&m==="click",p=d.event.special[m]||{};if((!p._default||p._default.call(e,a)===!1)&&!n&&!(l&&l.nodeName&&d.noData[l.nodeName.toLowerCase()])){try{l[m]&&(k=l["on"+m],k&&(l["on"+m]=null),d.event.triggered=!0,l[m]())}catch(q){}
99k&&(l["on"+m]=k),d.event.triggered=!1}}},handle:function(c){var e,f,g,h,i,j=[],k=d.makeArray(arguments);c=k[0]=d.event.fix(c||a.event),c.currentTarget=this,e=c.type.indexOf(".")<0&&!c.exclusive,e||(g=c.type.split("."),c.type=g.shift(),j=g.slice(0).sort(),h=new RegExp("(^|\\.)"+j.join("\\.(?:.*\\.)?")+"(\\.|$)")),c.namespace=c.namespace||j.join("."),i=d._data(this,u),typeof i==="function"&&(i=i.events),f=(i||{})[c.type];if(i&&f){f=f.slice(0);for(var l=0,m=f.length;l<m;l++){var n=f[l];if(e||h.test(n.namespace)){c.handler=n.handler,c.data=n.data,c.handleObj=n;var o=n.handler.apply(this,k);o!==b&&(c.result=o,o===!1&&(c.preventDefault(),c.stopPropagation()));if(c.isImmediatePropagationStopped())break}}}
100return c.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),fix:function(a){if(a[d.expando])return a;var e=a;a=d.Event(e);for(var f=this.props.length,g;f;)g=this.props[--f],a[g]=e[g];a.target||(a.target=a.srcElement||c),a.target.nodeType===3&&(a.target=a.target.parentNode),!a.relatedTarget&&a.fromElement&&(a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement);if(a.pageX==null&&a.clientX!=null){var h=c.documentElement,i=c.body;a.pageX=a.clientX+(h&&h.scrollLeft||i&&i.scrollLeft||0)-(h&&h.clientLeft||i&&i.clientLeft||0),a.pageY=a.clientY+(h&&h.scrollTop||i&&i.scrollTop||0)-(h&&h.clientTop||i&&i.clientTop||0)}
101a.which==null&&(a.charCode!=null||a.keyCode!=null)&&(a.which=a.charCode!=null?a.charCode:a.keyCode),!a.metaKey&&a.ctrlKey&&(a.metaKey=a.ctrlKey),!a.which&&a.button!==b&&(a.which=a.button&1?1:a.button&2?3:a.button&4?2:0);return a},guid:1e8,proxy:d.proxy,special:{ready:{setup:d.bindReady,teardown:d.noop},live:{add:function(a){d.event.add(this,F(a.origType,a.selector),d.extend({},a,{handler:E,guid:a.handler.guid}))},remove:function(a){d.event.remove(this,F(a.origType,a.selector),a)}},beforeunload:{setup:function(a,b,c){d.isWindow(this)&&(this.onbeforeunload=c)},teardown:function(a,b){this.onbeforeunload===b&&(this.onbeforeunload=null)}}}},d.removeEvent=c.removeEventListener?function(a,b,c){a.removeEventListener&&a.removeEventListener(b,c,!1)}:function(a,b,c){a.detachEvent&&a.detachEvent("on"+b,c)},d.Event=function(a){if(!this.preventDefault)return new d.Event(a);a&&a.type?(this.originalEvent=a,this.type=a.type,this.isDefaultPrevented=a.defaultPrevented||a.returnValue===!1||a.getPreventDefault&&a.getPreventDefault()?w:v):this.type=a,this.timeStamp=d.now(),this[d.expando]=!0},d.Event.prototype={preventDefault:function(){this.isDefaultPrevented=w;var a=this.originalEvent;a&&(a.preventDefault?a.preventDefault():a.returnValue=!1)},stopPropagation:function(){this.isPropagationStopped=w;var a=this.originalEvent;a&&(a.stopPropagation&&a.stopPropagation(),a.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=w,this.stopPropagation()},isDefaultPrevented:v,isPropagationStopped:v,isImmediatePropagationStopped:v};var x=function(a){var b=a.relatedTarget;try{while(b&&b!==this)b=b.parentNode;b!==this&&(a.type=a.data,d.event.handle.apply(this,arguments))}catch(c){}},y=function(a){a.type=a.data,d.event.handle.apply(this,arguments)};d.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){d.event.special[a]={setup:function(c){d.event.add(this,b,c&&c.selector?y:x,a)},teardown:function(a){d.event.remove(this,b,a&&a.selector?y:x)}}}),d.support.submitBubbles||(d.event.special.submit={setup:function(a,c){if(this.nodeName&&this.nodeName.toLowerCase()!=="form")d.event.add(this,"click.specialSubmit",function(a){var c=a.target,e=c.type;if((e==="submit"||e==="image")&&d(c).closest("form").length){a.liveFired=b;return C("submit",this,arguments)}}),d.event.add(this,"keypress.specialSubmit",function(a){var c=a.target,e=c.type;if((e==="text"||e==="password")&&d(c).closest("form").length&&a.keyCode===13){a.liveFired=b;return C("submit",this,arguments)}});else return!1},teardown:function(a){d.event.remove(this,".specialSubmit")}});if(!d.support.changeBubbles){var z,A=function(a){var b=a.type,c=a.value;b==="radio"||b==="checkbox"?c=a.checked:b==="select-multiple"?c=a.selectedIndex>-1?d.map(a.options,function(a){return a.selected}).join("-"):"":a.nodeName.toLowerCase()==="select"&&(c=a.selectedIndex);return c},B=function B(a){var c=a.target,e,f;if(p.test(c.nodeName)&&!c.readOnly){e=d._data(c,"_change_data"),f=A(c),(a.type!=="focusout"||c.type!=="radio")&&d._data(c,"_change_data",f);if(e===b||f===e)return;if(e!=null||f){a.type="change",a.liveFired=b;return d.event.trigger(a,arguments[1],c)}}};d.event.special.change={filters:{focusout:B,beforedeactivate:B,click:function(a){var b=a.target,c=b.type;if(c==="radio"||c==="checkbox"||b.nodeName.toLowerCase()==="select")return B.call(this,a)},keydown:function(a){var b=a.target,c=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")return B.call(this,a)},beforeactivate:function(a){var b=a.target;d._data(b,"_change_data",A(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in z)d.event.add(this,c+".specialChange",z[c]);return p.test(this.nodeName)},teardown:function(a){d.event.remove(this,".specialChange");return p.test(this.nodeName)}},z=d.event.special.change.filters,z.focus=z.beforeactivate}
102c.addEventListener&&d.each({focus:"focusin",blur:"focusout"},function(a,b){function c(a){a=d.event.fix(a),a.type=b;return d.event.handle.call(this,a)}
103d.event.special[b]={setup:function(){this.addEventListener(a,c,!0)},teardown:function(){this.removeEventListener(a,c,!0)}}}),d.each(["bind","one"],function(a,c){d.fn[c]=function(a,e,f){if(typeof a==="object"){for(var g in a)this[c](g,e,a[g],f);return this}
104if(d.isFunction(e)||e===!1)f=e,e=b;var h=c==="one"?d.proxy(f,function(a){d(this).unbind(a,h);return f.apply(this,arguments)}):f;if(a==="unload"&&c!=="one")this.one(a,e,f);else for(var i=0,j=this.length;i<j;i++)d.event.add(this[i],a,h,e);return this}}),d.fn.extend({unbind:function(a,b){if(typeof a!=="object"||a.preventDefault)for(var e=0,f=this.length;e<f;e++)d.event.remove(this[e],a,b);else for(var c in a)this.unbind(c,a[c]);return this},delegate:function(a,b,c,d){return this.live(b,c,d,a)},undelegate:function(a,b,c){return arguments.length===0?this.unbind("live"):this.die(b,null,c,a)},trigger:function(a,b){return this.each(function(){d.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){var c=d.Event(a);c.preventDefault(),c.stopPropagation(),d.event.trigger(c,b,this[0]);return c.result}},toggle:function(a){var b=arguments,c=1;while(c<b.length)d.proxy(a,b[c++]);return this.click(d.proxy(a,function(e){var f=(d._data(this,"lastToggle"+a.guid)||0)%c;d._data(this,"lastToggle"+a.guid,f+1),e.preventDefault();return b[f].apply(this,arguments)||!1}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var D={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};d.each(["live","die"],function(a,c){d.fn[c]=function(a,e,f,g){var h,i=0,j,k,l,m=g||this.selector,n=g?this:d(this.context);if(typeof a==="object"&&!a.preventDefault){for(var p in a)n[c](p,e,a[p],m);return this}
105d.isFunction(e)&&(f=e,e=b),a=(a||"").split(" ");while((h=a[i++])!=null){j=o.exec(h),k="",j&&(k=j[0],h=h.replace(o,""));if(h==="hover"){a.push("mouseenter"+k,"mouseleave"+k);continue}
106l=h,h==="focus"||h==="blur"?(a.push(D[h]+k),h=h+k):h=(D[h]||h)+k;if(c==="live")for(var q=0,r=n.length;q<r;q++)d.event.add(n[q],"live."+F(h,m),{data:e,selector:m,handler:f,origType:h,origHandler:f,preType:l});else n.unbind("live."+F(h,m),f)}
107return this}}),d.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),function(a,b){d.fn[b]=function(a,c){c==null&&(c=a,a=null);return arguments.length>0?this.bind(b,a,c):this.trigger(b)},d.attrFn&&(d.attrFn[b]=!0)}),function(){function s(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var j=d[g];if(j){var k=!1;j=j[a];while(j){if(j.sizcache===c){k=d[j.sizset];break}
108if(j.nodeType===1){f||(j.sizcache=c,j.sizset=g);if(typeof b!=="string"){if(j===b){k=!0;break}}else if(i.filter(b,[j]).length>0){k=j;break}}
109j=j[a]}
110d[g]=k}}}
111function r(a,b,c,d,e,f){for(var g=0,h=d.length;g<h;g++){var i=d[g];if(i){var j=!1;i=i[a];while(i){if(i.sizcache===c){j=d[i.sizset];break}
112i.nodeType===1&&!f&&(i.sizcache=c,i.sizset=g);if(i.nodeName.toLowerCase()===b){j=i;break}
113i=i[a]}
114d[g]=j}}}
115var a=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,g=!1,h=!0;[0,0].sort(function(){h=!1;return 0});var i=function(b,d,e,g){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!=="string")return e;var l,m,o,p,q,r,s,u,v=!0,w=i.isXML(d),x=[],y=b;do{a.exec(""),l=a.exec(y);if(l){y=l[3],x.push(l[1]);if(l[2]){p=l[3];break}}}
116while(l);if(x.length>1&&k.exec(b))if(x.length===2&&j.relative[x[0]])m=t(x[0]+x[1],d);else{m=j.relative[x[0]]?[d]:i(x.shift(),d);while(x.length)b=x.shift(),j.relative[b]&&(b+=x.shift()),m=t(b,m)}else{!g&&x.length>1&&d.nodeType===9&&!w&&j.match.ID.test(x[0])&&!j.match.ID.test(x[x.length-1])&&(q=i.find(x.shift(),d,w),d=q.expr?i.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:n(g)}:i.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),m=q.expr?i.filter(q.expr,q.set):q.set,x.length>0?o=n(m):v=!1;while(x.length)r=x.pop(),s=r,j.relative[r]?s=x.pop():r="",s==null&&(s=d),j.relative[r](o,s,w)}else o=x=[]}
117o||(o=m),o||i.error(r||b);if(f.call(o)==="[object Array]")if(v)if(d&&d.nodeType===1)for(u=0;o[u]!=null;u++)o[u]&&(o[u]===!0||o[u].nodeType===1&&i.contains(d,o[u]))&&e.push(m[u]);else for(u=0;o[u]!=null;u++)o[u]&&o[u].nodeType===1&&e.push(m[u]);else e.push.apply(e,o);else n(o,e);p&&(i(p,h,e,g),i.uniqueSort(e));return e};i.uniqueSort=function(a){if(p){g=h,a.sort(p);if(g)for(var b=1;b<a.length;b++)a[b]===a[b-1]&&a.splice(b--,1)}
118return a},i.matches=function(a,b){return i(a,null,null,b)},i.matchesSelector=function(a,b){return i(b,null,null,[a]).length>0},i.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=j.order.length;e<f;e++){var g,h=j.order[e];if(g=j.leftMatch[h].exec(a)){var i=g[1];g.splice(1,1);if(i.substr(i.length-1)!=="\\"){g[1]=(g[1]||"").replace(/\\/g,""),d=j.find[h](g,b,c);if(d!=null){a=a.replace(j.match[h],"");break}}}}
119d||(d=typeof b.getElementsByTagName!=="undefined"?b.getElementsByTagName("*"):[]);return{set:d,expr:a}},i.filter=function(a,c,d,e){var f,g,h=a,k=[],l=c,m=c&&c[0]&&i.isXML(c[0]);while(a&&c.length){for(var n in j.filter)if((f=j.leftMatch[n].exec(a))!=null&&f[2]){var o,p,q=j.filter[n],r=f[1];g=!1,f.splice(1,1);if(r.substr(r.length-1)==="\\")continue;l===k&&(k=[]);if(j.preFilter[n]){f=j.preFilter[n](f,l,d,k,e,m);if(f){if(f===!0)continue}else g=o=!0}
120if(f)for(var s=0;(p=l[s])!=null;s++)if(p){o=q(p,f,s,l);var t=e^!!o;d&&o!=null?t?g=!0:l[s]=!1:t&&(k.push(p),g=!0)}
121if(o!==b){d||(l=k),a=a.replace(j.match[n],"");if(!g)return[];break}}
122if(a===h)if(g==null)i.error(a);else break;h=a}
123return l},i.error=function(a){throw"Syntax error, unrecognized expression: "+a};var j=i.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(a){return a.getAttribute("href")}},relative:{"+":function(a,b){var c=typeof b==="string",d=c&&!/\W/.test(b),e=c&&!d;d&&(b=b.toLowerCase());for(var f=0,g=a.length,h;f<g;f++)if(h=a[f]){while((h=h.previousSibling)&&h.nodeType!==1){}
124a[f]=e||h&&h.nodeName.toLowerCase()===b?h||!1:h===b}
125e&&i.filter(b,a,!0)},">":function(a,b){var c,d=typeof b==="string",e=0,f=a.length;if(d&&!/\W/.test(b)){b=b.toLowerCase();for(;e<f;e++){c=a[e];if(c){var g=c.parentNode;a[e]=g.nodeName.toLowerCase()===b?g:!1}}}else{for(;e<f;e++)c=a[e],c&&(a[e]=d?c.parentNode:c.parentNode===b);d&&i.filter(b,a,!0)}},"":function(a,b,c){var d,f=e++,g=s;typeof b==="string"&&!/\W/.test(b)&&(b=b.toLowerCase(),d=b,g=r),g("parentNode",b,f,a,d,c)},"~":function(a,b,c){var d,f=e++,g=s;typeof b==="string"&&!/\W/.test(b)&&(b=b.toLowerCase(),d=b,g=r),g("previousSibling",b,f,a,d,c)}},find:{ID:function(a,b,c){if(typeof b.getElementById!=="undefined"&&!c){var d=b.getElementById(a[1]);return d&&d.parentNode?[d]:[]}},NAME:function(a,b){if(typeof b.getElementsByName!=="undefined"){var c=[],d=b.getElementsByName(a[1]);for(var e=0,f=d.length;e<f;e++)d[e].getAttribute("name")===a[1]&&c.push(d[e]);return c.length===0?null:c}},TAG:function(a,b){if(typeof b.getElementsByTagName!=="undefined")return b.getElementsByTagName(a[1])}},preFilter:{CLASS:function(a,b,c,d,e,f){a=" "+a[1].replace(/\\/g,"")+" ";if(f)return a;for(var g=0,h;(h=b[g])!=null;g++)h&&(e^(h.className&&(" "+h.className+" ").replace(/[\t\n\r]/g," ").indexOf(a)>=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(/\\/g,"")},TAG:function(a,b){return a[1].toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||i.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&i.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(/\\/g,"");!f&&j.attrMap[g]&&(a[1]=j.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(/\\/g,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=i(b[3],null,null,c);else{var g=i.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(j.match.POS.test(b[0])||j.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!i(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){return"text"===a.type},radio:function(a){return"radio"===a.type},checkbox:function(a){return"checkbox"===a.type},file:function(a){return"file"===a.type},password:function(a){return"password"===a.type},submit:function(a){return"submit"===a.type},image:function(a){return"image"===a.type},reset:function(a){return"reset"===a.type},button:function(a){return"button"===a.type||a.nodeName.toLowerCase()==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return b<c[3]-0},gt:function(a,b,c){return b>c[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=j.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||i.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,k=g.length;h<k;h++)if(g[h]===a)return!1;return!0}
126i.error(e)},CHILD:function(a,b){var c=b[1],d=a;switch(c){case"only":case"first":while(d=d.previousSibling)if(d.nodeType===1)return!1;if(c==="first")return!0;d=a;case"last":while(d=d.nextSibling)if(d.nodeType===1)return!1;return!0;case"nth":var e=b[2],f=b[3];if(e===1&&f===0)return!0;var g=b[0],h=a.parentNode;if(h&&(h.sizcache!==g||!a.nodeIndex)){var i=0;for(d=h.firstChild;d;d=d.nextSibling)d.nodeType===1&&(d.nodeIndex=++i);h.sizcache=g}
127var j=a.nodeIndex-f;return e===0?j===0:j%e===0&&j/e>=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=j.attrHandle[c]?j.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=j.setFilters[e];if(f)return f(a,c,b,d)}}},k=j.match.POS,l=function(a,b){return"\\"+(b-0+1)};for(var m in j.match)j.match[m]=new RegExp(j.match[m].source+/(?![^\[]*\])(?![^\(]*\))/.source),j.leftMatch[m]=new RegExp(/(^(?:.|\r|\n)*?)/.source+j.match[m].source.replace(/\\(\d+)/g,l));var n=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}
128return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(o){n=function(a,b){var c=0,d=b||[];if(f.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length==="number")for(var e=a.length;c<e;c++)d.push(a[c]);else for(;a[c];c++)d.push(a[c]);return d}}
129var p,q;c.documentElement.compareDocumentPosition?p=function(a,b){if(a===b){g=!0;return 0}
130if(!a.compareDocumentPosition||!b.compareDocumentPosition)return a.compareDocumentPosition?-1:1;return a.compareDocumentPosition(b)&4?-1:1}:(p=function(a,b){var c,d,e=[],f=[],h=a.parentNode,i=b.parentNode,j=h;if(a===b){g=!0;return 0}
131if(h===i)return q(a,b);if(!h)return-1;if(!i)return 1;while(j)e.unshift(j),j=j.parentNode;j=i;while(j)f.unshift(j),j=j.parentNode;c=e.length,d=f.length;for(var k=0;k<c&&k<d;k++)if(e[k]!==f[k])return q(e[k],f[k]);return k===c?q(a,f[k],-1):q(e[k],b,1)},q=function(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}
132return 1}),i.getText=function(a){var b="",c;for(var d=0;a[d];d++)c=a[d],c.nodeType===3||c.nodeType===4?b+=c.nodeValue:c.nodeType!==8&&(b+=i.getText(c.childNodes));return b},function(){var a=c.createElement("div"),d="script"+(new Date).getTime(),e=c.documentElement;a.innerHTML="<a name='"+d+"'/>",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(j.find.ID=function(a,c,d){if(typeof c.getElementById!=="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!=="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},j.filter.ID=function(a,b){var c=typeof a.getAttributeNode!=="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(j.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}
133return c}),a.innerHTML="<a href='#'></a>",a.firstChild&&typeof a.firstChild.getAttribute!=="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(j.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=i,b=c.createElement("div"),d="__sizzle__";b.innerHTML="<p class='TEST'></p>";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){i=function(b,e,f,g){e=e||c;if(!g&&!i.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return n(e.getElementsByTagName(b),f);if(h[2]&&j.find.CLASS&&e.getElementsByClassName)return n(e.getElementsByClassName(h[2]),f)}
134if(e.nodeType===9){if(b==="body"&&e.body)return n([e.body],f);if(h&&h[3]){var k=e.getElementById(h[3]);if(!k||!k.parentNode)return n([],f);if(k.id===h[3])return n([k],f)}
135try{return n(e.querySelectorAll(b),f)}catch(l){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e.getAttribute("id"),o=m||d,p=e.parentNode,q=/^\s*[+~]/.test(b);m?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),q&&p&&(e=e.parentNode);try{if(!q||p)return n(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(r){}finally{m||e.removeAttribute("id")}}}
136return a(b,e,f,g)};for(var e in a)i[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector,d=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(e){d=!0}
137b&&(i.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!i.isXML(a))try{if(d||!j.match.PSEUDO.test(c)&&!/!=/.test(c))return b.call(a,c)}catch(e){}
138return i(c,null,null,[a]).length>0})}(),function(){var a=c.createElement("div");a.innerHTML="<div class='test e'></div><div class='test'></div>";if(a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;j.order.splice(1,0,"CLASS"),j.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!=="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?i.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?i.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:i.contains=function(){return!1},i.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var t=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=j.match.PSEUDO.exec(a))e+=c[0],a=a.replace(j.match.PSEUDO,"");a=j.relative[a]?a+"*":a;for(var g=0,h=f.length;g<h;g++)i(a,f[g],d);return i.filter(e,d)};d.find=i,d.expr=i.selectors,d.expr[":"]=d.expr.filters,d.unique=i.uniqueSort,d.text=i.getText,d.isXMLDoc=i.isXML,d.contains=i.contains}();var G=/Until$/,H=/^(?:parents|prevUntil|prevAll)/,I=/,/,J=/^.[^:#\[\.,]*$/,K=Array.prototype.slice,L=d.expr.match.POS,M={children:!0,contents:!0,next:!0,prev:!0};d.fn.extend({find:function(a){var b=this.pushStack("","find",a),c=0;for(var e=0,f=this.length;e<f;e++){c=b.length,d.find(a,this[e],b);if(e>0)for(var g=c;g<b.length;g++)for(var h=0;h<c;h++)if(b[h]===b[g]){b.splice(g--,1);break}}
139return b},has:function(a){var b=d(a);return this.filter(function(){for(var a=0,c=b.length;a<c;a++)if(d.contains(this,b[a]))return!0})},not:function(a){return this.pushStack(O(this,a,!1),"not",a)},filter:function(a){return this.pushStack(O(this,a,!0),"filter",a)},is:function(a){return!!a&&d.filter(a,this).length>0},closest:function(a,b){var c=[],e,f,g=this[0];if(d.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(e=0,f=a.length;e<f;e++)i=a[e],j[i]||(j[i]=d.expr.match.POS.test(i)?d(i,b||this.context):i);while(g&&g.ownerDocument&&g!==b){for(i in j)h=j[i],(h.jquery?h.index(g)>-1:d(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}
140return c}
141var l=L.test(a)?d(a,b||this.context):null;for(e=0,f=this.length;e<f;e++){g=this[e];while(g){if(l?l.index(g)>-1:d.find.matchesSelector(g,a)){c.push(g);break}
142g=g.parentNode;if(!g||!g.ownerDocument||g===b)break}}
143c=c.length>1?d.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a==="string")return d.inArray(this[0],a?d(a):this.parent().children());return d.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a==="string"?d(a,b):d.makeArray(a),e=d.merge(this.get(),c);return this.pushStack(N(c[0])||N(e[0])?e:d.unique(e))},andSelf:function(){return this.add(this.prevObject)}}),d.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return d.dir(a,"parentNode")},parentsUntil:function(a,b,c){return d.dir(a,"parentNode",c)},next:function(a){return d.nth(a,2,"nextSibling")},prev:function(a){return d.nth(a,2,"previousSibling")},nextAll:function(a){return d.dir(a,"nextSibling")},prevAll:function(a){return d.dir(a,"previousSibling")},nextUntil:function(a,b,c){return d.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return d.dir(a,"previousSibling",c)},siblings:function(a){return d.sibling(a.parentNode.firstChild,a)},children:function(a){return d.sibling(a.firstChild)},contents:function(a){return d.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:d.makeArray(a.childNodes)}},function(a,b){d.fn[a]=function(c,e){var f=d.map(this,b,c),g=K.call(arguments);G.test(a)||(e=c),e&&typeof e==="string"&&(f=d.filter(e,f)),f=this.length>1&&!M[a]?d.unique(f):f,(this.length>1||I.test(e))&&H.test(a)&&(f=f.reverse());return this.pushStack(f,a,g.join(","))}}),d.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?d.find.matchesSelector(b[0],a)?[b[0]]:[]:d.find.matches(a,b)},dir:function(a,c,e){var f=[],g=a[c];while(g&&g.nodeType!==9&&(e===b||g.nodeType!==1||!d(g).is(e)))g.nodeType===1&&f.push(g),g=g[c];return f},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var P=/ jQuery\d+="(?:\d+|null)"/g,Q=/^\s+/,R=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,S=/<([\w:]+)/,T=/<tbody/i,U=/<|&#?\w+;/,V=/<(?:script|object|embed|option|style)/i,W=/checked\s*(?:[^=]|=\s*.checked.)/i,X={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};X.optgroup=X.option,X.tbody=X.tfoot=X.colgroup=X.caption=X.thead,X.th=X.td,d.support.htmlSerialize||(X._default=[1,"div<div>","</div>"]),d.fn.extend({text:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.text(a.call(this,b,c.text()))});if(typeof a!=="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return d.text(this)},wrapAll:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapAll(a.call(this,b))});if(this[0]){var b=d(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}
144return this},wrapInner:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapInner(a.call(this,b))});return this.each(function(){var b=d(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){d(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){d.nodeName(this,"body")||d(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=d(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,d(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,e;(e=this[c])!=null;c++)if(!a||d.filter(a,[e]).length)!b&&e.nodeType===1&&(d.cleanData(e.getElementsByTagName("*")),d.cleanData([e])),e.parentNode&&e.parentNode.removeChild(e);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&d.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}
145return this},clone:function(a,b){a=a==null?!0:a,b=b==null?a:b;return this.map(function(){return d.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(P,""):null;if(typeof a!=="string"||V.test(a)||!d.support.leadingWhitespace&&Q.test(a)||X[(S.exec(a)||["",""])[1].toLowerCase()])d.isFunction(a)?this.each(function(b){var c=d(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);else{a=a.replace(R,"<$1></$2>");try{for(var c=0,e=this.length;c<e;c++)this[c].nodeType===1&&(d.cleanData(this[c].getElementsByTagName("*")),this[c].innerHTML=a)}catch(f){this.empty().append(a)}}
146return this},replaceWith:function(a){if(this[0]&&this[0].parentNode){if(d.isFunction(a))return this.each(function(b){var c=d(this),e=c.html();c.replaceWith(a.call(this,b,e))});typeof a!=="string"&&(a=d(a).detach());return this.each(function(){var b=this.nextSibling,c=this.parentNode;d(this).remove(),b?d(b).before(a):d(c).append(a)})}
147return this.pushStack(d(d.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,!0)},domManip:function(a,c,e){var f,g,h,i,j=a[0],k=[];if(!d.support.checkClone&&arguments.length===3&&typeof j==="string"&&W.test(j))return this.each(function(){d(this).domManip(a,c,e,!0)});if(d.isFunction(j))return this.each(function(f){var g=d(this);a[0]=j.call(this,f,c?g.html():b),g.domManip(a,c,e)});if(this[0]){i=j&&j.parentNode,d.support.parentNode&&i&&i.nodeType===11&&i.childNodes.length===this.length?f={fragment:i}:f=d.buildFragment(a,this,k),h=f.fragment,h.childNodes.length===1?g=h=h.firstChild:g=h.firstChild;if(g){c=c&&d.nodeName(g,"tr");for(var l=0,m=this.length,n=m-1;l<m;l++)e.call(c?Y(this[l],g):this[l],f.cacheable||m>1&&l<n?d.clone(h,!0,!0):h)}
148k.length&&d.each(k,_)}
149return this}}),d.buildFragment=function(a,b,e){var f,g,h,i=b&&b[0]?b[0].ownerDocument||b[0]:c;a.length===1&&typeof a[0]==="string"&&a[0].length<512&&i===c&&a[0].charAt(0)==="<"&&!V.test(a[0])&&(d.support.checkClone||!W.test(a[0]))&&(g=!0,h=d.fragments[a[0]],h&&(h!==1&&(f=h))),f||(f=i.createDocumentFragment(),d.clean(a,i,f,e)),g&&(d.fragments[a[0]]=h?f:1);return{fragment:f,cacheable:g}},d.fragments={},d.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){d.fn[a]=function(c){var e=[],f=d(c),g=this.length===1&&this[0].parentNode;if(g&&g.nodeType===11&&g.childNodes.length===1&&f.length===1){f[b](this[0]);return this}
150for(var h=0,i=f.length;h<i;h++){var j=(h>0?this.clone(!0):this).get();d(f[h])[b](j),e=e.concat(j)}
151return this.pushStack(e,a,f.selector)}}),d.extend({clone:function(a,b,c){var e=a.cloneNode(!0),f,g,h;if(!d.support.noCloneEvent&&(a.nodeType===1||a.nodeType===11)&&!d.isXMLDoc(a)){f=a.getElementsByTagName("*"),g=e.getElementsByTagName("*");for(h=0;f[h];++h)$(f[h],g[h]);$(a,e)}
152if(b){Z(a,e);if(c&&"getElementsByTagName"in a){f=a.getElementsByTagName("*"),g=e.getElementsByTagName("*");if(f.length)for(h=0;f[h];++h)Z(f[h],g[h])}}
153return e},clean:function(a,b,e,f){b=b||c,typeof b.createElement==="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var g=[];for(var h=0,i;(i=a[h])!=null;h++){typeof i==="number"&&(i+="");if(!i)continue;if(typeof i!=="string"||U.test(i)){if(typeof i==="string"){i=i.replace(R,"<$1></$2>");var j=(S.exec(i)||["",""])[1].toLowerCase(),k=X[j]||X._default,l=k[0],m=b.createElement("div");m.innerHTML=k[1]+i+k[2];while(l--)m=m.lastChild;if(!d.support.tbody){var n=T.test(i),o=j==="table"&&!n?m.firstChild&&m.firstChild.childNodes:k[1]==="<table>"&&!n?m.childNodes:[];for(var p=o.length-1;p>=0;--p)d.nodeName(o[p],"tbody")&&!o[p].childNodes.length&&o[p].parentNode.removeChild(o[p])}!d.support.leadingWhitespace&&Q.test(i)&&m.insertBefore(b.createTextNode(Q.exec(i)[0]),m.firstChild),i=m.childNodes}}else i=b.createTextNode(i);i.nodeType?g.push(i):g=d.merge(g,i)}
154if(e)for(h=0;g[h];h++)!f||!d.nodeName(g[h],"script")||g[h].type&&g[h].type.toLowerCase()!=="text/javascript"?(g[h].nodeType===1&&g.splice.apply(g,[h+1,0].concat(d.makeArray(g[h].getElementsByTagName("script")))),e.appendChild(g[h])):f.push(g[h].parentNode?g[h].parentNode.removeChild(g[h]):g[h]);return g},cleanData:function(a){var b,c,e=d.cache,f=d.expando,g=d.event.special,h=d.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&d.noData[j.nodeName.toLowerCase()])continue;c=j[d.expando];if(c){b=e[c]&&e[c][f];if(b&&b.events){for(var k in b.events)g[k]?d.event.remove(j,k):d.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}
155h?delete j[d.expando]:j.removeAttribute&&j.removeAttribute(d.expando),delete e[c]}}}});var ba=/alpha\([^)]*\)/i,bb=/opacity=([^)]*)/,bc=/-([a-z])/ig,bd=/([A-Z])/g,be=/^-?\d+(?:px)?$/i,bf=/^-?\d/,bg={position:"absolute",visibility:"hidden",display:"block"},bh=["Left","Right"],bi=["Top","Bottom"],bj,bk,bl,bm=function(a,b){return b.toUpperCase()};d.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return d.access(this,a,c,!0,function(a,c,e){return e!==b?d.style(a,c,e):d.css(a,c)})},d.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bj(a,"opacity","opacity");return c===""?"1":c}
156return a.style.opacity}}},cssNumber:{zIndex:!0,fontWeight:!0,opacity:!0,zoom:!0,lineHeight:!0},cssProps:{"float":d.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,e,f){if(a&&a.nodeType!==3&&a.nodeType!==8&&a.style){var g,h=d.camelCase(c),i=a.style,j=d.cssHooks[h];c=d.cssProps[h]||h;if(e===b){if(j&&"get"in j&&(g=j.get(a,!1,f))!==b)return g;return i[c]}
157if(typeof e==="number"&&isNaN(e)||e==null)return;typeof e==="number"&&!d.cssNumber[h]&&(e+="px");if(!j||!("set"in j)||(e=j.set(a,e))!==b)try{i[c]=e}catch(k){}}},css:function(a,c,e){var f,g=d.camelCase(c),h=d.cssHooks[g];c=d.cssProps[g]||g;if(h&&"get"in h&&(f=h.get(a,!0,e))!==b)return f;if(bj)return bj(a,c,g)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]},camelCase:function(a){return a.replace(bc,bm)}}),d.curCSS=d.css,d.each(["height","width"],function(a,b){d.cssHooks[b]={get:function(a,c,e){var f;if(c){a.offsetWidth!==0?f=bn(a,b,e):d.swap(a,bg,function(){f=bn(a,b,e)});if(f<=0){f=bj(a,b,b),f==="0px"&&bl&&(f=bl(a,b,b));if(f!=null)return f===""||f==="auto"?"0px":f}
158if(f<0||f==null){f=a.style[b];return f===""||f==="auto"?"0px":f}
159return typeof f==="string"?f:f+"px"}},set:function(a,b){if(!be.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),d.support.opacity||(d.cssHooks.opacity={get:function(a,b){return bb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style;c.zoom=1;var e=d.isNaN(b)?"":"alpha(opacity="+b*100+")",f=c.filter||"";c.filter=ba.test(f)?f.replace(ba,e):c.filter+" "+e}}),c.defaultView&&c.defaultView.getComputedStyle&&(bk=function(a,c,e){var f,g,h;e=e.replace(bd,"-$1").toLowerCase();if(!(g=a.ownerDocument.defaultView))return b;if(h=g.getComputedStyle(a,null))f=h.getPropertyValue(e),f===""&&!d.contains(a.ownerDocument.documentElement,a)&&(f=d.style(a,e));return f}),c.documentElement.currentStyle&&(bl=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!be.test(d)&&bf.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bj=bk||bl,d.expr&&d.expr.filters&&(d.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!d.support.reliableHiddenOffsets&&(a.style.display||d.css(a,"display"))==="none"},d.expr.filters.visible=function(a){return!d.expr.filters.hidden(a)});var bo=/%20/g,bp=/\[\]$/,bq=/\r?\n/g,br=/#.*$/,bs=/^(.*?):\s*(.*?)\r?$/mg,bt=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bu=/^(?:GET|HEAD)$/,bv=/^\/\//,bw=/\?/,bx=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,by=/^(?:select|textarea)/i,bz=/\s+/,bA=/([?&])_=[^&]*/,bB=/^(\w+:)\/\/([^\/?#:]+)(?::(\d+))?/,bC=d.fn.load,bD={},bE={};d.fn.extend({load:function(a,b,c){if(typeof a!=="string"&&bC)return bC.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}
160var g="GET";b&&(d.isFunction(b)?(c=b,b=null):typeof b==="object"&&(b=d.param(b,d.ajaxSettings.traditional),g="POST"));var h=this;d.ajax({url:a,type:g,dataType:"html",data:b,complete:function(a,b,e){e=a.responseText,a.isResolved()&&(a.done(function(a){e=a}),h.html(f?d("<div>").append(e.replace(bx,"")).find(f):e)),c&&h.each(c,[e,b,a])}});return this},serialize:function(){return d.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?d.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||by.test(this.nodeName)||bt.test(this.type))}).map(function(a,b){var c=d(this).val();return c==null?null:d.isArray(c)?d.map(c,function(a,c){return{name:b.name,value:a.replace(bq,"\r\n")}}):{name:b.name,value:c.replace(bq,"\r\n")}}).get()}}),d.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){d.fn[b]=function(a){return this.bind(b,a)}}),d.each(["get","post"],function(a,b){d[b]=function(a,c,e,f){d.isFunction(c)&&(f=f||e,e=c,c=null);return d.ajax({type:b,url:a,data:c,success:e,dataType:f})}}),d.extend({getScript:function(a,b){return d.get(a,null,b,"script")},getJSON:function(a,b,c){return d.get(a,b,c,"json")},ajaxSetup:function(a){d.extend(!0,d.ajaxSettings,a),a.context&&(d.ajaxSettings.context=a.context)},ajaxSettings:{url:location.href,global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":d.parseJSON,"text xml":d.parseXML}},ajaxPrefilter:bF(bD),ajaxTransport:bF(bE),ajax:function(a,e){function w(a,c,e,l){if(t!==2){t=2,p&&clearTimeout(p),o=b,m=l||"",v.readyState=a?4:0;var n,q,r,s=e?bI(f,v,e):b,u,w;if(a>=200&&a<300||a===304){if(f.ifModified){if(u=v.getResponseHeader("Last-Modified"))d.lastModified[f.url]=u;if(w=v.getResponseHeader("Etag"))d.etag[f.url]=w}
161if(a===304)c="notmodified",n=!0;else try{q=bJ(f,s),c="success",n=!0}catch(x){c="parsererror",r=x}}else r=c,a&&(c="error",a<0&&(a=0));v.status=a,v.statusText=c,n?i.resolveWith(g,[q,c,v]):i.rejectWith(g,[v,c,r]),v.statusCode(k),k=b,f.global&&h.trigger("ajax"+(n?"Success":"Error"),[v,f,n?q:r]),j.resolveWith(g,[v,c]),f.global&&(h.trigger("ajaxComplete",[v,f]),--d.active||d.event.trigger("ajaxStop"))}}
162typeof e!=="object"&&(e=a,a=b),e=e||{};var f=d.extend(!0,{},d.ajaxSettings,e),g=(f.context=("context"in e?e:d.ajaxSettings).context)||f,h=g===f?d.event:d(g),i=d.Deferred(),j=d._Deferred(),k=f.statusCode||{},l={},m,n,o,p,q=c.location,r=q.protocol||"http:",s,t=0,u,v={readyState:0,setRequestHeader:function(a,b){t===0&&(l[a.toLowerCase()]=b);return this},getAllResponseHeaders:function(){return t===2?m:null},getResponseHeader:function(a){var b;if(t===2){if(!n){n={};while(b=bs.exec(m))n[b[1].toLowerCase()]=b[2]}
163b=n[a.toLowerCase()]}
164return b||null},abort:function(a){a=a||"abort",o&&o.abort(a),w(0,a);return this}};i.promise(v),v.success=v.done,v.error=v.fail,v.complete=j.done,v.statusCode=function(a){if(a){var b;if(t<2)for(b in a)k[b]=[k[b],a[b]];else b=a[v.status],v.then(b,b)}
165return this},f.url=(""+(a||f.url)).replace(br,"").replace(bv,r+"//"),f.dataTypes=d.trim(f.dataType||"*").toLowerCase().split(bz),f.crossDomain||(s=bB.exec(f.url.toLowerCase()),f.crossDomain=s&&(s[1]!=r||s[2]!=q.hostname||(s[3]||(s[1]==="http:"?80:443))!=(q.port||(r==="http:"?80:443)))),f.data&&f.processData&&typeof f.data!=="string"&&(f.data=d.param(f.data,f.traditional)),bG(bD,f,e,v),f.type=f.type.toUpperCase(),f.hasContent=!bu.test(f.type),f.global&&d.active++===0&&d.event.trigger("ajaxStart");if(!f.hasContent){f.data&&(f.url+=(bw.test(f.url)?"&":"?")+f.data);if(f.cache===!1){var x=d.now(),y=f.url.replace(bA,"$1_="+x);f.url=y+(y===f.url?(bw.test(f.url)?"&":"?")+"_="+x:"")}}
166if(f.data&&f.hasContent&&f.contentType!==!1||e.contentType)l["content-type"]=f.contentType;f.ifModified&&(d.lastModified[f.url]&&(l["if-modified-since"]=d.lastModified[f.url]),d.etag[f.url]&&(l["if-none-match"]=d.etag[f.url])),l.accept=f.dataTypes[0]&&f.accepts[f.dataTypes[0]]?f.accepts[f.dataTypes[0]]+(f.dataTypes[0]!=="*"?", */*; q=0.01":""):f.accepts["*"];for(u in f.headers)l[u.toLowerCase()]=f.headers[u];if(!f.beforeSend||f.beforeSend.call(g,v,f)!==!1&&t!==2){for(u in{success:1,error:1,complete:1})v[u](f[u]);o=bG(bE,f,e,v);if(o){t=v.readyState=1,f.global&&h.trigger("ajaxSend",[v,f]),f.async&&f.timeout>0&&(p=setTimeout(function(){v.abort("timeout")},f.timeout));try{o.send(l,w)}catch(z){status<2?w(-1,z):d.error(z)}}else w(-1,"No Transport")}else w(0,"abort"),v=!1;return v},param:function(a,c){var e=[],f=function(a,b){b=d.isFunction(b)?b():b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=d.ajaxSettings.traditional);if(d.isArray(a)||a.jquery)d.each(a,function(){f(this.name,this.value)});else for(var g in a)bH(g,a[g],c,f);return e.join("&").replace(bo,"+")}}),d.extend({active:0,lastModified:{},etag:{}});var bK=d.now(),bL=/(\=)\?(&|$)|()\?\?()/i;d.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return d.expando+"_"+bK++}}),d.ajaxPrefilter("json jsonp",function(b,c,e){e=typeof b.data==="string";if(b.dataTypes[0]==="jsonp"||c.jsonpCallback||c.jsonp!=null||b.jsonp!==!1&&(bL.test(b.url)||e&&bL.test(b.data))){var f,g=b.jsonpCallback=d.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,h=a[g],i=b.url,j=b.data,k="$1"+g+"$2";b.jsonp!==!1&&(i=i.replace(bL,k),b.url===i&&(e&&(j=j.replace(bL,k)),b.data===j&&(i+=(/\?/.test(i)?"&":"?")+b.jsonp+"="+g))),b.url=i,b.data=j,a[g]=function(a){f=[a]},b.complete=[function(){a[g]=h;if(h)f&&d.isFunction(h)&&a[g](f[0]);else try{delete a[g]}catch(b){}},b.complete],b.converters["script json"]=function(){f||d.error(g+" was not called");return f[0]},b.dataTypes[0]="json";return"script"}}),d.ajaxSetup({accepts:{script:"text/javascript, application/javascript"},contents:{script:/javascript/},converters:{"text script":function(a){d.globalEval(a);return a}}}),d.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),d.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var bM=d.now(),bN={},bO,bP;d.ajaxSettings.xhr=a.ActiveXObject?function(){if(a.location.protocol!=="file:")try{return new a.XMLHttpRequest}catch(b){}
167try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(c){}}:function(){return new a.XMLHttpRequest};try{bP=d.ajaxSettings.xhr()}catch(bQ){}
168d.support.ajax=!!bP,d.support.cors=bP&&"withCredentials"in bP,bP=b,d.support.ajax&&d.ajaxTransport(function(b){if(!b.crossDomain||d.support.cors){var c;return{send:function(e,f){bO||(bO=1,d(a).bind("unload",function(){d.each(bN,function(a,b){b.onreadystatechange&&b.onreadystatechange(1)})}));var g=b.xhr(),h;b.username?g.open(b.type,b.url,b.async,b.username,b.password):g.open(b.type,b.url,b.async),(!b.crossDomain||b.hasContent)&&!e["x-requested-with"]&&(e["x-requested-with"]="XMLHttpRequest");try{d.each(e,function(a,b){g.setRequestHeader(a,b)})}catch(i){}
169g.send(b.hasContent&&b.data||null),c=function(a,e){if(c&&(e||g.readyState===4)){c=0,h&&(g.onreadystatechange=d.noop,delete bN[h]);if(e)g.readyState!==4&&g.abort();else{var i=g.status,j,k=g.getAllResponseHeaders(),l={},m=g.responseXML;m&&m.documentElement&&(l.xml=m),l.text=g.responseText;try{j=g.statusText}catch(n){j=""}
170i=i===0?!b.crossDomain||j?k?304:0:302:i==1223?204:i,f(i,j,l,k)}}},b.async&&g.readyState!==4?(h=bM++,bN[h]=g,g.onreadystatechange=c):c()},abort:function(){c&&c(0,1)}}}});var bR={},bS=/^(?:toggle|show|hide)$/,bT=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,bU,bV=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];d.fn.extend({show:function(a,b,c){var e,f;if(a||a===0)return this.animate(bW("show",3),a,b,c);for(var g=0,h=this.length;g<h;g++)e=this[g],f=e.style.display,!d._data(e,"olddisplay")&&f==="none"&&(f=e.style.display=""),f===""&&d.css(e,"display")==="none"&&d._data(e,"olddisplay",bX(e.nodeName));for(g=0;g<h;g++){e=this[g],f=e.style.display;if(f===""||f==="none")e.style.display=d._data(e,"olddisplay")||""}
171return this},hide:function(a,b,c){if(a||a===0)return this.animate(bW("hide",3),a,b,c);for(var e=0,f=this.length;e<f;e++){var g=d.css(this[e],"display");g!=="none"&&!d._data(this[e],"olddisplay")&&d._data(this[e],"olddisplay",g)}
172for(e=0;e<f;e++)this[e].style.display="none";return this},_toggle:d.fn.toggle,toggle:function(a,b,c){var e=typeof a==="boolean";d.isFunction(a)&&d.isFunction(b)?this._toggle.apply(this,arguments):a==null||e?this.each(function(){var b=e?a:d(this).is(":hidden");d(this)[b?"show":"hide"]()}):this.animate(bW("toggle",3),a,b,c);return this},fadeTo:function(a,b,c,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,c,d)},animate:function(a,b,c,e){var f=d.speed(b,c,e);if(d.isEmptyObject(a))return this.each(f.complete);return this[f.queue===!1?"each":"queue"](function(){var b=d.extend({},f),c,e=this.nodeType===1,g=e&&d(this).is(":hidden"),h=this;for(c in a){var i=d.camelCase(c);c!==i&&(a[i]=a[c],delete a[c],c=i);if(a[c]==="hide"&&g||a[c]==="show"&&!g)return b.complete.call(this);if(e&&(c==="height"||c==="width")){b.overflow=[this.style.overflow,this.style.overflowX,this.style.overflowY];if(d.css(this,"display")==="inline"&&d.css(this,"float")==="none")if(d.support.inlineBlockNeedsLayout){var j=bX(this.nodeName);j==="inline"?this.style.display="inline-block":(this.style.display="inline",this.style.zoom=1)}else this.style.display="inline-block"}
173d.isArray(a[c])&&((b.specialEasing=b.specialEasing||{})[c]=a[c][1],a[c]=a[c][0])}
174b.overflow!=null&&(this.style.overflow="hidden"),b.curAnim=d.extend({},a),d.each(a,function(c,e){var f=new d.fx(h,b,c);if(bS.test(e))f[e==="toggle"?g?"show":"hide":e](a);else{var i=bT.exec(e),j=f.cur()||0;if(i){var k=parseFloat(i[2]),l=i[3]||"px";l!=="px"&&(d.style(h,c,(k||1)+l),j=(k||1)/f.cur()*j,d.style(h,c,j+l)),i[1]&&(k=(i[1]==="-="?-1:1)*k+j),f.custom(j,k,l)}else f.custom(j,e,"")}});return!0})},stop:function(a,b){var c=d.timers;a&&this.queue([]),this.each(function(){for(var a=c.length-1;a>=0;a--)c[a].elem===this&&(b&&c[a](!0),c.splice(a,1))}),b||this.dequeue();return this}}),d.each({slideDown:bW("show",1),slideUp:bW("hide",1),slideToggle:bW("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){d.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),d.extend({speed:function(a,b,c){var e=a&&typeof a==="object"?d.extend({},a):{complete:c||!c&&b||d.isFunction(a)&&a,duration:a,easing:c&&b||b&&!d.isFunction(b)&&b};e.duration=d.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in d.fx.speeds?d.fx.speeds[e.duration]:d.fx.speeds._default,e.old=e.complete,e.complete=function(){e.queue!==!1&&d(this).dequeue(),d.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig||(b.orig={})}}),d.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(d.fx.step[this.prop]||d.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(d.css(this.elem,this.prop));return a||0},custom:function(a,b,c){function g(a){return e.step(a)}
175var e=this,f=d.fx;this.startTime=d.now(),this.start=a,this.end=b,this.unit=c||this.unit||"px",this.now=this.start,this.pos=this.state=0,g.elem=this.elem,g()&&d.timers.push(g)&&!bU&&(bU=setInterval(f.tick,f.interval))},show:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),d(this.elem).show()},hide:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=d.now(),c=!0;if(a||b>=this.options.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),this.options.curAnim[this.prop]=!0;for(var e in this.options.curAnim)this.options.curAnim[e]!==!0&&(c=!1);if(c){if(this.options.overflow!=null&&!d.support.shrinkWrapBlocks){var f=this.elem,g=this.options;d.each(["","X","Y"],function(a,b){f.style["overflow"+b]=g.overflow[a]})}
176this.options.hide&&d(this.elem).hide();if(this.options.hide||this.options.show)for(var h in this.options.curAnim)d.style(this.elem,h,this.options.orig[h]);this.options.complete.call(this.elem)}
177return!1}
178var i=b-this.startTime;this.state=i/this.options.duration;var j=this.options.specialEasing&&this.options.specialEasing[this.prop],k=this.options.easing||(d.easing.swing?"swing":"linear");this.pos=d.easing[j||k](this.state,i,0,1,this.options.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update();return!0}},d.extend(d.fx,{tick:function(){var a=d.timers;for(var b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||d.fx.stop()},interval:13,stop:function(){clearInterval(bU),bU=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){d.style(a.elem,"opacity",a.now)},_default:function(a){a.elem.style&&a.elem.style[a.prop]!=null?a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit:a.elem[a.prop]=a.now}}}),d.expr&&d.expr.filters&&(d.expr.filters.animated=function(a){return d.grep(d.timers,function(b){return a===b.elem}).length});var bY=/^t(?:able|d|h)$/i,bZ=/^(?:body|html)$/i;"getBoundingClientRect"in c.documentElement?d.fn.offset=function(a){var b=this[0],c;if(a)return this.each(function(b){d.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return d.offset.bodyOffset(b);try{c=b.getBoundingClientRect()}catch(e){}
179var f=b.ownerDocument,g=f.documentElement;if(!c||!d.contains(g,b))return c?{top:c.top,left:c.left}:{top:0,left:0};var h=f.body,i=b$(f),j=g.clientTop||h.clientTop||0,k=g.clientLeft||h.clientLeft||0,l=i.pageYOffset||d.support.boxModel&&g.scrollTop||h.scrollTop,m=i.pageXOffset||d.support.boxModel&&g.scrollLeft||h.scrollLeft,n=c.top+l-j,o=c.left+m-k;return{top:n,left:o}}:d.fn.offset=function(a){var b=this[0];if(a)return this.each(function(b){d.offset.setOffset(this,a,b)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return d.offset.bodyOffset(b);d.offset.initialize();var c,e=b.offsetParent,f=b,g=b.ownerDocument,h=g.documentElement,i=g.body,j=g.defaultView,k=j?j.getComputedStyle(b,null):b.currentStyle,l=b.offsetTop,m=b.offsetLeft;while((b=b.parentNode)&&b!==i&&b!==h){if(d.offset.supportsFixedPosition&&k.position==="fixed")break;c=j?j.getComputedStyle(b,null):b.currentStyle,l-=b.scrollTop,m-=b.scrollLeft,b===e&&(l+=b.offsetTop,m+=b.offsetLeft,d.offset.doesNotAddBorder&&(!d.offset.doesAddBorderForTableAndCells||!bY.test(b.nodeName))&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),f=e,e=b.offsetParent),d.offset.subtractsBorderForOverflowNotVisible&&c.overflow!=="visible"&&(l+=parseFloat(c.borderTopWidth)||0,m+=parseFloat(c.borderLeftWidth)||0),k=c}
180if(k.position==="relative"||k.position==="static")l+=i.offsetTop,m+=i.offsetLeft;d.offset.supportsFixedPosition&&k.position==="fixed"&&(l+=Math.max(h.scrollTop,i.scrollTop),m+=Math.max(h.scrollLeft,i.scrollLeft));return{top:l,left:m}},d.offset={initialize:function(){var a=c.body,b=c.createElement("div"),e,f,g,h,i=parseFloat(d.css(a,"marginTop"))||0,j="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";d.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),e=b.firstChild,f=e.firstChild,h=e.nextSibling.firstChild.firstChild,this.doesNotAddBorder=f.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,f.style.position="fixed",f.style.top="20px",this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15,f.style.position=f.style.top="",e.style.overflow="hidden",e.style.position="relative",this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),a=b=e=f=g=h=null,d.offset.initialize=d.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;d.offset.initialize(),d.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(d.css(a,"marginTop"))||0,c+=parseFloat(d.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var e=d.css(a,"position");e==="static"&&(a.style.position="relative");var f=d(a),g=f.offset(),h=d.css(a,"top"),i=d.css(a,"left"),j=e==="absolute"&&d.inArray("auto",[h,i])>-1,k={},l={},m,n;j&&(l=f.position()),m=j?l.top:parseInt(h,10)||0,n=j?l.left:parseInt(i,10)||0,d.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):f.css(k)}},d.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),e=bZ.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(d.css(a,"marginTop"))||0,c.left-=parseFloat(d.css(a,"marginLeft"))||0,e.top+=parseFloat(d.css(b[0],"borderTopWidth"))||0,e.left+=parseFloat(d.css(b[0],"borderLeftWidth"))||0;return{top:c.top-e.top,left:c.left-e.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&(!bZ.test(a.nodeName)&&d.css(a,"position")==="static"))a=a.offsetParent;return a})}}),d.each(["Left","Top"],function(a,c){var e="scroll"+c;d.fn[e]=function(c){var f=this[0],g;if(!f)return null;if(c!==b)return this.each(function(){g=b$(this),g?g.scrollTo(a?d(g).scrollLeft():c,a?c:d(g).scrollTop()):this[e]=c});g=b$(f);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:d.support.boxModel&&g.document.documentElement[e]||g.document.body[e]:f[e]}}),d.each(["Height","Width"],function(a,c){var e=c.toLowerCase();d.fn["inner"+c]=function(){return this[0]?parseFloat(d.css(this[0],e,"padding")):null},d.fn["outer"+c]=function(a){return this[0]?parseFloat(d.css(this[0],e,a?"margin":"border")):null},d.fn[e]=function(a){var f=this[0];if(!f)return a==null?null:this;if(d.isFunction(a))return this.each(function(b){var c=d(this);c[e](a.call(this,b,c[e]()))});if(d.isWindow(f)){var g=f.document.documentElement["client"+c];return f.document.compatMode==="CSS1Compat"&&g||f.document.body["client"+c]||g}
181if(f.nodeType===9)return Math.max(f.documentElement["client"+c],f.body["scroll"+c],f.documentElement["scroll"+c],f.body["offset"+c],f.documentElement["offset"+c]);if(a===b){var h=d.css(f,e),i=parseFloat(h);return d.isNaN(i)?h:i}
182return this.css(e,typeof a==="string"?a:a+"px")}})})(window);
183
184 // End thirdparty/jquery/jquery-1.5.min.js
185
186 // Start thirdparty/util/converter.js
187/**
188 * @fileOverview A collection of conversion utilities.
189 * @author <a href="mailto:conrchan@cisco.com">Conrad Chan</a>
190 * @name Utilities.Converter
191 */
192
193/** @namespace */
194var finesse = finesse || {};
195
196/**
197 * @class
198 * Contains a collection of utility functions.
199 */
200finesse.Converter = (function () {
201 /** @scope finesse.clientservices.Utilities */
202 return {
203 /* This work is licensed under Creative Commons GNU LGPL License.
204
205 License: http://creativecommons.org/licenses/LGPL/2.1/
206 Version: 0.9
207 Author: Stefan Goessner/2006
208 Web: http://goessner.net/
209 */
210 xml2json: function (xml, tab) {
211 var X = {
212 toObj: function (xml) {
213 var o = {};
214 if (xml.nodeType === 1) {
215 // element node ..
216 if (xml.attributes.length)
217 // element with attributes ..
218 for (var i = 0; i < xml.attributes.length; i++)
219 o["@" + xml.attributes[i].nodeName] = (xml.attributes[i].nodeValue || "").toString();
220 if (xml.firstChild) {
221 // element has child nodes ..
222 var textChild = 0,
223 cdataChild = 0,
224 hasElementChild = false;
225 for (var n = xml.firstChild; n; n = n.nextSibling) {
226 if (n.nodeType == 1) hasElementChild = true;
227 else if (n.nodeType == 3 && n.nodeValue.match(/[^ \f\n\r\t\v]/)) textChild++;
228 // non-whitespace text
229 else if (n.nodeType == 4) cdataChild++;
230 // cdata section node
231 }
232 if (hasElementChild) {
233 if (textChild < 2 && cdataChild < 2) {
234 // structured element with evtl. a single text or/and cdata node ..
235 X.removeWhite(xml);
236 for (var n = xml.firstChild; n; n = n.nextSibling) {
237 if (n.nodeType == 3)
238 // text node
239 o["#text"] = X.escape(n.nodeValue);
240 else if (n.nodeType == 4)
241 // cdata node
242 o["#cdata"] = X.escape(n.nodeValue);
243 else if (o[n.nodeName]) {
244 // multiple occurence of element ..
245 if (o[n.nodeName] instanceof Array)
246 o[n.nodeName][o[n.nodeName].length] = X.toObj(n);
247 else
248 o[n.nodeName] = [o[n.nodeName], X.toObj(n)];
249 }
250 else
251 // first occurence of element..
252 o[n.nodeName] = X.toObj(n);
253 }
254 }
255 else {
256 // mixed content
257 if (!xml.attributes.length)
258 o = X.escape(X.innerXml(xml));
259 else
260 o["#text"] = X.escape(X.innerXml(xml));
261 }
262 }
263 else if (textChild) {
264 // pure text
265 if (!xml.attributes.length)
266 o = X.escape(X.innerXml(xml));
267 else
268 o["#text"] = X.escape(X.innerXml(xml));
269 }
270 else if (cdataChild) {
271 // cdata
272 if (cdataChild > 1)
273 o = X.escape(X.innerXml(xml));
274 else
275 for (var n = xml.firstChild; n; n = n.nextSibling)
276 o["#cdata"] = X.escape(n.nodeValue);
277 }
278 }
279 if (!xml.attributes.length && !xml.firstChild) o = null;
280 }
281 else if (xml.nodeType == 9) {
282 // document.node
283 o = X.toObj(xml.documentElement);
284 }
285 else
286 throw ("unhandled node type: " + xml.nodeType);
287 return o;
288 },
289 toJson: function(o, name, ind) {
290 var json = name ? ("\"" + name + "\"") : "";
291 if (o instanceof Array) {
292 for (var i = 0, n = o.length; i < n; i++)
293 o[i] = X.toJson(o[i], "", ind + "\t");
294 json += (name ? ":[": "[") + (o.length > 1 ? ("\n" + ind + "\t" + o.join(",\n" + ind + "\t") + "\n" + ind) : o.join("")) + "]";
295 }
296 else if (o == null)
297 json += (name && ":") + "null";
298 else if (typeof(o) == "object") {
299 var arr = [];
300 for (var m in o)
301 arr[arr.length] = X.toJson(o[m], m, ind + "\t");
302 json += (name ? ":{": "{") + (arr.length > 1 ? ("\n" + ind + "\t" + arr.join(",\n" + ind + "\t") + "\n" + ind) : arr.join("")) + "}";
303 }
304 else if (typeof(o) == "string")
305 json += (name && ":") + "\"" + o.toString() + "\"";
306 else
307 json += (name && ":") + o.toString();
308 return json;
309 },
310 innerXml: function(node) {
311 var s = "";
312 if ("innerHTML" in node)
313 s = node.innerHTML;
314 else {
315 var asXml = function(n) {
316 var s = "";
317 if (n.nodeType == 1) {
318 s += "<" + n.nodeName;
319 for (var i = 0; i < n.attributes.length; i++)
320 s += " " + n.attributes[i].nodeName + "=\"" + (n.attributes[i].nodeValue || "").toString() + "\"";
321 if (n.firstChild) {
322 s += ">";
323 for (var c = n.firstChild; c; c = c.nextSibling)
324 s += asXml(c);
325 s += "</" + n.nodeName + ">";
326 }
327 else
328 s += "/>";
329 }
330 else if (n.nodeType == 3)
331 s += n.nodeValue;
332 else if (n.nodeType == 4)
333 s += "<![CDATA[" + n.nodeValue + "]]>";
334 return s;
335 };
336 for (var c = node.firstChild; c; c = c.nextSibling)
337 s += asXml(c);
338 }
339 return s;
340 },
341 escape: function(txt) {
342 return txt.replace(/[\\]/g, "\\\\")
343 .replace(/[\"]/g, '\\"')
344 .replace(/[\n]/g, '\\n')
345 .replace(/[\r]/g, '\\r');
346 },
347 removeWhite: function(e) {
348 e.normalize();
349 for (var n = e.firstChild; n;) {
350 if (n.nodeType == 3) {
351 // text node
352 if (!n.nodeValue.match(/[^ \f\n\r\t\v]/)) {
353 // pure whitespace text node
354 var nxt = n.nextSibling;
355 e.removeChild(n);
356 n = nxt;
357 }
358 else
359 n = n.nextSibling;
360 }
361 else if (n.nodeType == 1) {
362 // element node
363 X.removeWhite(n);
364 n = n.nextSibling;
365 }
366 else
367 // any other node
368 n = n.nextSibling;
369 }
370 return e;
371 }
372 };
373 if (xml.nodeType == 9)
374 // document node
375 xml = xml.documentElement;
376 var json = X.toJson(X.toObj(X.removeWhite(xml)), xml.nodeName, "\t");
377 return "{\n" + tab + (tab ? json.replace(/\t/g, tab) : json.replace(/\t|\n/g, "")) + "\n}";
378 },
379
380 /* This work is licensed under Creative Commons GNU LGPL License.
381
382 License: http://creativecommons.org/licenses/LGPL/2.1/
383 Version: 0.9
384 Author: Stefan Goessner/2006
385 Web: http://goessner.net/
386 */
387 json2xml: function(o, tab) {
388 var toXml = function(v, name, ind) {
389 var xml = "";
390 if (v instanceof Array) {
391 for (var i = 0, n = v.length; i < n; i++)
392 xml += ind + toXml(v[i], name, ind + "\t") + "\n";
393 }
394 else if (typeof(v) == "object") {
395 var hasChild = false;
396 xml += ind + "<" + name;
397 for (var m in v) {
398 if (m.charAt(0) == "@")
399 xml += " " + m.substr(1) + "=\"" + v[m].toString() + "\"";
400 else
401 hasChild = true;
402 }
403 xml += hasChild ? ">": "/>";
404 if (hasChild) {
405 for (var m in v) {
406 if (m == "#text")
407 xml += v[m];
408 else if (m == "#cdata")
409 xml += "<![CDATA[" + v[m] + "]]>";
410 else if (m.charAt(0) != "@")
411 xml += toXml(v[m], m, ind + "\t");
412 }
413 xml += (xml.charAt(xml.length - 1) == "\n" ? ind: "") + "</" + name + ">";
414 }
415 }
416 else {
417 xml += ind + "<" + name + ">" + v.toString() + "</" + name + ">";
418 }
419 return xml;
420 },
421 xml = "";
422 for (var m in o)
423 xml += toXml(o[m], m, "");
424 return tab ? xml.replace(/\t/g, tab) : xml.replace(/\t|\n/g, "");
425 }
426 };
427})();
428
429 // End thirdparty/util/converter.js
430
431 // Start thirdparty/strophe/strophe.js
432/** File: strophe.js
433 * A JavaScript library for writing XMPP clients.
434 *
435 * This library uses either Bidirectional-streams Over Synchronous HTTP (BOSH)
436 * to emulate a persistent, stateful, two-way connection to an XMPP server or
437 * alternatively WebSockets.
438 *
439 * More information on BOSH can be found in XEP 124.
440 * For more information on XMPP-over WebSocket see this RFC:
441 * http://tools.ietf.org/html/rfc7395
442 */
443
444/* All of the Strophe globals are defined in this special function below so
445 * that references to the globals become closures. This will ensure that
446 * on page reload, these references will still be available to callbacks
447 * that are still executing.
448 */
449
450/* jshint ignore:start */
451(function (root, factory) {
452 if (typeof define === 'function' && define.amd) {
453 //Allow using this built library as an AMD module
454 //in another project. That other project will only
455 //see this AMD call, not the internal modules in
456 //the closure below.
457 define([], factory);
458 } else {
459 //Browser globals case.
460 var wrapper = factory();
461 root.Strophe = wrapper.Strophe;
462 root.$build = wrapper.$build;
463 root.$iq = wrapper.$iq;
464 root.$msg = wrapper.$msg;
465 root.$pres = wrapper.$pres;
466 root.SHA1 = wrapper.SHA1;
467 root.MD5 = wrapper.MD5;
468 root.b64_hmac_sha1 = wrapper.b64_hmac_sha1;
469 root.b64_sha1 = wrapper.b64_sha1;
470 root.str_hmac_sha1 = wrapper.str_hmac_sha1;
471 root.str_sha1 = wrapper.str_sha1;
472 }
473}(this, function () {
474 //almond, and your modules will be inlined here
475/* jshint ignore:end */
476/**
477 * @license almond 0.3.3 Copyright jQuery Foundation and other contributors.
478 * Released under MIT license, http://github.com/requirejs/almond/LICENSE
479 */
480//Going sloppy to avoid 'use strict' string cost, but strict practices should
481//be followed.
482/*global setTimeout: false */
483
484var requirejs, require, define;
485(function (undef) {
486 var main, req, makeMap, handlers,
487 defined = {},
488 waiting = {},
489 config = {},
490 defining = {},
491 hasOwn = Object.prototype.hasOwnProperty,
492 aps = [].slice,
493 jsSuffixRegExp = /\.js$/;
494
495 function hasProp(obj, prop) {
496 return hasOwn.call(obj, prop);
497 }
498
499 /**
500 * Given a relative module name, like ./something, normalize it to
501 * a real name that can be mapped to a path.
502 * @param {String} name the relative name
503 * @param {String} baseName a real name that the name arg is relative
504 * to.
505 * @returns {String} normalized name
506 */
507 function normalize(name, baseName) {
508 var nameParts, nameSegment, mapValue, foundMap, lastIndex,
509 foundI, foundStarMap, starI, i, j, part, normalizedBaseParts,
510 baseParts = baseName && baseName.split("/"),
511 map = config.map,
512 starMap = (map && map['*']) || {};
513
514 //Adjust any relative paths.
515 if (name) {
516 name = name.split('/');
517 lastIndex = name.length - 1;
518
519 // If wanting node ID compatibility, strip .js from end
520 // of IDs. Have to do this here, and not in nameToUrl
521 // because node allows either .js or non .js to map
522 // to same file.
523 if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
524 name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
525 }
526
527 // Starts with a '.' so need the baseName
528 if (name[0].charAt(0) === '.' && baseParts) {
529 //Convert baseName to array, and lop off the last part,
530 //so that . matches that 'directory' and not name of the baseName's
531 //module. For instance, baseName of 'one/two/three', maps to
532 //'one/two/three.js', but we want the directory, 'one/two' for
533 //this normalization.
534 normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
535 name = normalizedBaseParts.concat(name);
536 }
537
538 //start trimDots
539 for (i = 0; i < name.length; i++) {
540 part = name[i];
541 if (part === '.') {
542 name.splice(i, 1);
543 i -= 1;
544 } else if (part === '..') {
545 // If at the start, or previous value is still ..,
546 // keep them so that when converted to a path it may
547 // still work when converted to a path, even though
548 // as an ID it is less than ideal. In larger point
549 // releases, may be better to just kick out an error.
550 if (i === 0 || (i === 1 && name[2] === '..') || name[i - 1] === '..') {
551 continue;
552 } else if (i > 0) {
553 name.splice(i - 1, 2);
554 i -= 2;
555 }
556 }
557 }
558 //end trimDots
559
560 name = name.join('/');
561 }
562
563 //Apply map config if available.
564 if ((baseParts || starMap) && map) {
565 nameParts = name.split('/');
566
567 for (i = nameParts.length; i > 0; i -= 1) {
568 nameSegment = nameParts.slice(0, i).join("/");
569
570 if (baseParts) {
571 //Find the longest baseName segment match in the config.
572 //So, do joins on the biggest to smallest lengths of baseParts.
573 for (j = baseParts.length; j > 0; j -= 1) {
574 mapValue = map[baseParts.slice(0, j).join('/')];
575
576 //baseName segment has config, find if it has one for
577 //this name.
578 if (mapValue) {
579 mapValue = mapValue[nameSegment];
580 if (mapValue) {
581 //Match, update name to the new value.
582 foundMap = mapValue;
583 foundI = i;
584 break;
585 }
586 }
587 }
588 }
589
590 if (foundMap) {
591 break;
592 }
593
594 //Check for a star map match, but just hold on to it,
595 //if there is a shorter segment match later in a matching
596 //config, then favor over this star map.
597 if (!foundStarMap && starMap && starMap[nameSegment]) {
598 foundStarMap = starMap[nameSegment];
599 starI = i;
600 }
601 }
602
603 if (!foundMap && foundStarMap) {
604 foundMap = foundStarMap;
605 foundI = starI;
606 }
607
608 if (foundMap) {
609 nameParts.splice(0, foundI, foundMap);
610 name = nameParts.join('/');
611 }
612 }
613
614 return name;
615 }
616
617 function makeRequire(relName, forceSync) {
618 return function () {
619 //A version of a require function that passes a moduleName
620 //value for items that may need to
621 //look up paths relative to the moduleName
622 var args = aps.call(arguments, 0);
623
624 //If first arg is not require('string'), and there is only
625 //one arg, it is the array form without a callback. Insert
626 //a null so that the following concat is correct.
627 if (typeof args[0] !== 'string' && args.length === 1) {
628 args.push(null);
629 }
630 return req.apply(undef, args.concat([relName, forceSync]));
631 };
632 }
633
634 function makeNormalize(relName) {
635 return function (name) {
636 return normalize(name, relName);
637 };
638 }
639
640 function makeLoad(depName) {
641 return function (value) {
642 defined[depName] = value;
643 };
644 }
645
646 function callDep(name) {
647 if (hasProp(waiting, name)) {
648 var args = waiting[name];
649 delete waiting[name];
650 defining[name] = true;
651 main.apply(undef, args);
652 }
653
654 if (!hasProp(defined, name) && !hasProp(defining, name)) {
655 throw new Error('No ' + name);
656 }
657 return defined[name];
658 }
659
660 //Turns a plugin!resource to [plugin, resource]
661 //with the plugin being undefined if the name
662 //did not have a plugin prefix.
663 function splitPrefix(name) {
664 var prefix,
665 index = name ? name.indexOf('!') : -1;
666 if (index > -1) {
667 prefix = name.substring(0, index);
668 name = name.substring(index + 1, name.length);
669 }
670 return [prefix, name];
671 }
672
673 //Creates a parts array for a relName where first part is plugin ID,
674 //second part is resource ID. Assumes relName has already been normalized.
675 function makeRelParts(relName) {
676 return relName ? splitPrefix(relName) : [];
677 }
678
679 /**
680 * Makes a name map, normalizing the name, and using a plugin
681 * for normalization if necessary. Grabs a ref to plugin
682 * too, as an optimization.
683 */
684 makeMap = function (name, relParts) {
685 var plugin,
686 parts = splitPrefix(name),
687 prefix = parts[0],
688 relResourceName = relParts[1];
689
690 name = parts[1];
691
692 if (prefix) {
693 prefix = normalize(prefix, relResourceName);
694 plugin = callDep(prefix);
695 }
696
697 //Normalize according
698 if (prefix) {
699 if (plugin && plugin.normalize) {
700 name = plugin.normalize(name, makeNormalize(relResourceName));
701 } else {
702 name = normalize(name, relResourceName);
703 }
704 } else {
705 name = normalize(name, relResourceName);
706 parts = splitPrefix(name);
707 prefix = parts[0];
708 name = parts[1];
709 if (prefix) {
710 plugin = callDep(prefix);
711 }
712 }
713
714 //Using ridiculous property names for space reasons
715 return {
716 f: prefix ? prefix + '!' + name : name, //fullName
717 n: name,
718 pr: prefix,
719 p: plugin
720 };
721 };
722
723 function makeConfig(name) {
724 return function () {
725 return (config && config.config && config.config[name]) || {};
726 };
727 }
728
729 handlers = {
730 require: function (name) {
731 return makeRequire(name);
732 },
733 exports: function (name) {
734 var e = defined[name];
735 if (typeof e !== 'undefined') {
736 return e;
737 } else {
738 return (defined[name] = {});
739 }
740 },
741 module: function (name) {
742 return {
743 id: name,
744 uri: '',
745 exports: defined[name],
746 config: makeConfig(name)
747 };
748 }
749 };
750
751 main = function (name, deps, callback, relName) {
752 var cjsModule, depName, ret, map, i, relParts,
753 args = [],
754 callbackType = typeof callback,
755 usingExports;
756
757 //Use name if no relName
758 relName = relName || name;
759 relParts = makeRelParts(relName);
760
761 //Call the callback to define the module, if necessary.
762 if (callbackType === 'undefined' || callbackType === 'function') {
763 //Pull out the defined dependencies and pass the ordered
764 //values to the callback.
765 //Default to [require, exports, module] if no deps
766 deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
767 for (i = 0; i < deps.length; i += 1) {
768 map = makeMap(deps[i], relParts);
769 depName = map.f;
770
771 //Fast path CommonJS standard dependencies.
772 if (depName === "require") {
773 args[i] = handlers.require(name);
774 } else if (depName === "exports") {
775 //CommonJS module spec 1.1
776 args[i] = handlers.exports(name);
777 usingExports = true;
778 } else if (depName === "module") {
779 //CommonJS module spec 1.1
780 cjsModule = args[i] = handlers.module(name);
781 } else if (hasProp(defined, depName) ||
782 hasProp(waiting, depName) ||
783 hasProp(defining, depName)) {
784 args[i] = callDep(depName);
785 } else if (map.p) {
786 map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
787 args[i] = defined[depName];
788 } else {
789 throw new Error(name + ' missing ' + depName);
790 }
791 }
792
793 ret = callback ? callback.apply(defined[name], args) : undefined;
794
795 if (name) {
796 //If setting exports via "module" is in play,
797 //favor that over return value and exports. After that,
798 //favor a non-undefined return value over exports use.
799 if (cjsModule && cjsModule.exports !== undef &&
800 cjsModule.exports !== defined[name]) {
801 defined[name] = cjsModule.exports;
802 } else if (ret !== undef || !usingExports) {
803 //Use the return value from the function.
804 defined[name] = ret;
805 }
806 }
807 } else if (name) {
808 //May just be an object definition for the module. Only
809 //worry about defining if have a module name.
810 defined[name] = callback;
811 }
812 };
813
814 requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
815 if (typeof deps === "string") {
816 if (handlers[deps]) {
817 //callback in this case is really relName
818 return handlers[deps](callback);
819 }
820 //Just return the module wanted. In this scenario, the
821 //deps arg is the module name, and second arg (if passed)
822 //is just the relName.
823 //Normalize module name, if it contains . or ..
824 return callDep(makeMap(deps, makeRelParts(callback)).f);
825 } else if (!deps.splice) {
826 //deps is a config object, not an array.
827 config = deps;
828 if (config.deps) {
829 req(config.deps, config.callback);
830 }
831 if (!callback) {
832 return;
833 }
834
835 if (callback.splice) {
836 //callback is an array, which means it is a dependency list.
837 //Adjust args if there are dependencies
838 deps = callback;
839 callback = relName;
840 relName = null;
841 } else {
842 deps = undef;
843 }
844 }
845
846 //Support require(['a'])
847 callback = callback || function () {};
848
849 //If relName is a function, it is an errback handler,
850 //so remove it.
851 if (typeof relName === 'function') {
852 relName = forceSync;
853 forceSync = alt;
854 }
855
856 //Simulate async callback;
857 if (forceSync) {
858 main(undef, deps, callback, relName);
859 } else {
860 //Using a non-zero value because of concern for what old browsers
861 //do, and latest browsers "upgrade" to 4 if lower value is used:
862 //http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
863 //If want a value immediately, use require('id') instead -- something
864 //that works in almond on the global level, but not guaranteed and
865 //unlikely to work in other AMD implementations.
866 setTimeout(function () {
867 main(undef, deps, callback, relName);
868 }, 4);
869 }
870
871 return req;
872 };
873
874 /**
875 * Just drops the config on the floor, but returns req in case
876 * the config return value is used.
877 */
878 req.config = function (cfg) {
879 return req(cfg);
880 };
881
882 /**
883 * Expose module registry for debugging and tooling
884 */
885 requirejs._defined = defined;
886
887 define = function (name, deps, callback) {
888 if (typeof name !== 'string') {
889 throw new Error('See almond README: incorrect module build, no module name');
890 }
891
892 //This module may not have dependencies
893 if (!deps.splice) {
894 //deps is not an array, so probably means
895 //an object literal or factory function for
896 //the value. Adjust args.
897 callback = deps;
898 deps = [];
899 }
900
901 if (!hasProp(defined, name) && !hasProp(waiting, name)) {
902 waiting[name] = [name, deps, callback];
903 }
904 };
905
906 define.amd = {
907 jQuery: true
908 };
909}());
910
911define("node_modules/almond/almond.js", function(){});
912
913/*
914 This program is distributed under the terms of the MIT license.
915 Please see the LICENSE file for details.
916
917 Copyright 2006-2008, OGG, LLC
918*/
919/* jshint undef: true, unused: true:, noarg: true, latedef: true */
920/* global define */
921
922(function (root, factory) {
923 if (typeof define === 'function' && define.amd) {
924 define('strophe-polyfill',[], function () {
925 return factory(root);
926 });
927 } else {
928 // Browser globals
929 return factory(root);
930 }
931}(this, function (root) {
932
933/** Function: Function.prototype.bind
934 * Bind a function to an instance.
935 *
936 * This Function object extension method creates a bound method similar
937 * to those in Python. This means that the 'this' object will point
938 * to the instance you want. See <MDC's bind() documentation at https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind>
939 * and <Bound Functions and Function Imports in JavaScript at http://benjamin.smedbergs.us/blog/2007-01-03/bound-functions-and-function-imports-in-javascript/>
940 * for a complete explanation.
941 *
942 * This extension already exists in some browsers (namely, Firefox 3), but
943 * we provide it to support those that don't.
944 *
945 * Parameters:
946 * (Object) obj - The object that will become 'this' in the bound function.
947 * (Object) argN - An option argument that will be prepended to the
948 * arguments given for the function call
949 *
950 * Returns:
951 * The bound function.
952 */
953if (!Function.prototype.bind) {
954 Function.prototype.bind = function (obj /*, arg1, arg2, ... */) {
955 var func = this;
956 var _slice = Array.prototype.slice;
957 var _concat = Array.prototype.concat;
958 var _args = _slice.call(arguments, 1);
959 return function () {
960 return func.apply(obj ? obj : this, _concat.call(_args, _slice.call(arguments, 0)));
961 };
962 };
963}
964
965/** Function: Array.isArray
966 * This is a polyfill for the ES5 Array.isArray method.
967 */
968if (!Array.isArray) {
969 Array.isArray = function(arg) {
970 return Object.prototype.toString.call(arg) === '[object Array]';
971 };
972}
973
974/** Function: Array.prototype.indexOf
975 * Return the index of an object in an array.
976 *
977 * This function is not supplied by some JavaScript implementations, so
978 * we provide it if it is missing. This code is from:
979 * http://developer.mozilla.org/En/Core_JavaScript_1.5_Reference:Objects:Array:indexOf
980 *
981 * Parameters:
982 * (Object) elt - The object to look for.
983 * (Integer) from - The index from which to start looking. (optional).
984 *
985 * Returns:
986 * The index of elt in the array or -1 if not found.
987 */
988if (!Array.prototype.indexOf) {
989 Array.prototype.indexOf = function(elt /*, from*/) {
990 var len = this.length;
991 var from = Number(arguments[1]) || 0;
992 from = (from < 0) ? Math.ceil(from) : Math.floor(from);
993 if (from < 0) {
994 from += len;
995 }
996
997 for (; from < len; from++) {
998 if (from in this && this[from] === elt) {
999 return from;
1000 }
1001 }
1002 return -1;
1003 };
1004}
1005
1006/** Function: Array.prototype.forEach
1007 *
1008 * This function is not available in IE < 9
1009 *
1010 * See <forEach on developer.mozilla.org at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach>
1011 */
1012if (!Array.prototype.forEach) {
1013 Array.prototype.forEach = function(callback, thisArg) {
1014 var T, k;
1015 if (this === null) {
1016 throw new TypeError(' this is null or not defined');
1017 }
1018 // 1. Let O be the result of calling toObject() passing the
1019 // |this| value as the argument.
1020 var O = Object(this);
1021 // 2. Let lenValue be the result of calling the Get() internal
1022 // method of O with the argument "length".
1023 // 3. Let len be toUint32(lenValue).
1024 var len = O.length >>> 0;
1025 // 4. If isCallable(callback) is false, throw a TypeError exception.
1026 // See: http://es5.github.com/#x9.11
1027 if (typeof callback !== "function") {
1028 throw new TypeError(callback + ' is not a function');
1029 }
1030 // 5. If thisArg was supplied, let T be thisArg; else let
1031 // T be undefined.
1032 if (arguments.length > 1) {
1033 T = thisArg;
1034 }
1035 // 6. Let k be 0
1036 k = 0;
1037 // 7. Repeat, while k < len
1038 while (k < len) {
1039 var kValue;
1040 // a. Let Pk be ToString(k).
1041 // This is implicit for LHS operands of the in operator
1042 // b. Let kPresent be the result of calling the HasProperty
1043 // internal method of O with argument Pk.
1044 // This step can be combined with c
1045 // c. If kPresent is true, then
1046 if (k in O) {
1047 // i. Let kValue be the result of calling the Get internal
1048 // method of O with argument Pk.
1049 kValue = O[k];
1050 // ii. Call the Call internal method of callback with T as
1051 // the this value and argument list containing kValue, k, and O.
1052 callback.call(T, kValue, k, O);
1053 }
1054 // d. Increase k by 1.
1055 k++;
1056 }
1057 // 8. return undefined
1058 };
1059}
1060
1061// This code was written by Tyler Akins and has been placed in the
1062// public domain. It would be nice if you left this header intact.
1063// Base64 code from Tyler Akins -- http://rumkin.com
1064var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
1065if (!root.btoa) {
1066 root.btoa = function (input) {
1067 /**
1068 * Encodes a string in base64
1069 * @param {String} input The string to encode in base64.
1070 */
1071 var output = "";
1072 var chr1, chr2, chr3;
1073 var enc1, enc2, enc3, enc4;
1074 var i = 0;
1075 do {
1076 chr1 = input.charCodeAt(i++);
1077 chr2 = input.charCodeAt(i++);
1078 chr3 = input.charCodeAt(i++);
1079
1080 enc1 = chr1 >> 2;
1081 enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
1082 enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
1083 enc4 = chr3 & 63;
1084
1085 if (isNaN(chr2)) {
1086 enc2 = ((chr1 & 3) << 4);
1087 enc3 = enc4 = 64;
1088 } else if (isNaN(chr3)) {
1089 enc4 = 64;
1090 }
1091 output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) +
1092 keyStr.charAt(enc3) + keyStr.charAt(enc4);
1093 } while (i < input.length);
1094 return output;
1095 };
1096}
1097
1098if (!root.atob) {
1099 root.atob = function (input) {
1100 /**
1101 * Decodes a base64 string.
1102 * @param {String} input The string to decode.
1103 */
1104 var output = "";
1105 var chr1, chr2, chr3;
1106 var enc1, enc2, enc3, enc4;
1107 var i = 0;
1108 // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
1109 input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
1110 do {
1111 enc1 = keyStr.indexOf(input.charAt(i++));
1112 enc2 = keyStr.indexOf(input.charAt(i++));
1113 enc3 = keyStr.indexOf(input.charAt(i++));
1114 enc4 = keyStr.indexOf(input.charAt(i++));
1115
1116 chr1 = (enc1 << 2) | (enc2 >> 4);
1117 chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
1118 chr3 = ((enc3 & 3) << 6) | enc4;
1119
1120 output = output + String.fromCharCode(chr1);
1121
1122 if (enc3 !== 64) {
1123 output = output + String.fromCharCode(chr2);
1124 }
1125 if (enc4 !== 64) {
1126 output = output + String.fromCharCode(chr3);
1127 }
1128 } while (i < input.length);
1129 return output;
1130 };
1131}
1132}));
1133
1134/*
1135 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
1136 * in FIPS PUB 180-1
1137 * Version 2.1a Copyright Paul Johnston 2000 - 2002.
1138 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
1139 * Distributed under the BSD License
1140 * See http://pajhome.org.uk/crypt/md5 for details.
1141 */
1142
1143/* jshint undef: true, unused: true:, noarg: true, latedef: false */
1144/* global define */
1145
1146/* Some functions and variables have been stripped for use with Strophe */
1147
1148(function (root, factory) {
1149 if (typeof define === 'function' && define.amd) {
1150 define('strophe-sha1', [],function () {
1151 return factory();
1152 });
1153 } else if (typeof exports === 'object') {
1154 module.exports = factory();
1155 } else {
1156 // Browser globals
1157 root.SHA1 = factory();
1158 }
1159}(this, function () {
1160
1161/*
1162 * Calculate the SHA-1 of an array of big-endian words, and a bit length
1163 */
1164function core_sha1(x, len)
1165{
1166 /* append padding */
1167 x[len >> 5] |= 0x80 << (24 - len % 32);
1168 x[((len + 64 >> 9) << 4) + 15] = len;
1169
1170 var w = new Array(80);
1171 var a = 1732584193;
1172 var b = -271733879;
1173 var c = -1732584194;
1174 var d = 271733878;
1175 var e = -1009589776;
1176
1177 var i, j, t, olda, oldb, oldc, oldd, olde;
1178 for (i = 0; i < x.length; i += 16)
1179 {
1180 olda = a;
1181 oldb = b;
1182 oldc = c;
1183 oldd = d;
1184 olde = e;
1185
1186 for (j = 0; j < 80; j++)
1187 {
1188 if (j < 16) { w[j] = x[i + j]; }
1189 else { w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); }
1190 t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
1191 safe_add(safe_add(e, w[j]), sha1_kt(j)));
1192 e = d;
1193 d = c;
1194 c = rol(b, 30);
1195 b = a;
1196 a = t;
1197 }
1198
1199 a = safe_add(a, olda);
1200 b = safe_add(b, oldb);
1201 c = safe_add(c, oldc);
1202 d = safe_add(d, oldd);
1203 e = safe_add(e, olde);
1204 }
1205 return [a, b, c, d, e];
1206}
1207
1208/*
1209 * Perform the appropriate triplet combination function for the current
1210 * iteration
1211 */
1212function sha1_ft(t, b, c, d)
1213{
1214 if (t < 20) { return (b & c) | ((~b) & d); }
1215 if (t < 40) { return b ^ c ^ d; }
1216 if (t < 60) { return (b & c) | (b & d) | (c & d); }
1217 return b ^ c ^ d;
1218}
1219
1220/*
1221 * Determine the appropriate additive constant for the current iteration
1222 */
1223function sha1_kt(t)
1224{
1225 return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
1226 (t < 60) ? -1894007588 : -899497514;
1227}
1228
1229/*
1230 * Calculate the HMAC-SHA1 of a key and some data
1231 */
1232function core_hmac_sha1(key, data)
1233{
1234 var bkey = str2binb(key);
1235 if (bkey.length > 16) { bkey = core_sha1(bkey, key.length * 8); }
1236
1237 var ipad = new Array(16), opad = new Array(16);
1238 for (var i = 0; i < 16; i++)
1239 {
1240 ipad[i] = bkey[i] ^ 0x36363636;
1241 opad[i] = bkey[i] ^ 0x5C5C5C5C;
1242 }
1243
1244 var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * 8);
1245 return core_sha1(opad.concat(hash), 512 + 160);
1246}
1247
1248/*
1249 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
1250 * to work around bugs in some JS interpreters.
1251 */
1252function safe_add(x, y)
1253{
1254 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
1255 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
1256 return (msw << 16) | (lsw & 0xFFFF);
1257}
1258
1259/*
1260 * Bitwise rotate a 32-bit number to the left.
1261 */
1262function rol(num, cnt)
1263{
1264 return (num << cnt) | (num >>> (32 - cnt));
1265}
1266
1267/*
1268 * Convert an 8-bit or 16-bit string to an array of big-endian words
1269 * In 8-bit function, characters >255 have their hi-byte silently ignored.
1270 */
1271function str2binb(str)
1272{
1273 var bin = [];
1274 var mask = 255;
1275 for (var i = 0; i < str.length * 8; i += 8)
1276 {
1277 bin[i>>5] |= (str.charCodeAt(i / 8) & mask) << (24 - i%32);
1278 }
1279 return bin;
1280}
1281
1282/*
1283 * Convert an array of big-endian words to a string
1284 */
1285function binb2str(bin)
1286{
1287 var str = "";
1288 var mask = 255;
1289 for (var i = 0; i < bin.length * 32; i += 8)
1290 {
1291 str += String.fromCharCode((bin[i>>5] >>> (24 - i%32)) & mask);
1292 }
1293 return str;
1294}
1295
1296/*
1297 * Convert an array of big-endian words to a base-64 string
1298 */
1299function binb2b64(binarray)
1300{
1301 var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1302 var str = "";
1303 var triplet, j;
1304 for (var i = 0; i < binarray.length * 4; i += 3)
1305 {
1306 triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) |
1307 (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) |
1308 ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
1309 for (j = 0; j < 4; j++)
1310 {
1311 if (i * 8 + j * 6 > binarray.length * 32) { str += "="; }
1312 else { str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); }
1313 }
1314 }
1315 return str;
1316}
1317
1318/*
1319 * These are the functions you'll usually want to call
1320 * They take string arguments and return either hex or base-64 encoded strings
1321 */
1322return {
1323 b64_hmac_sha1: function (key, data){ return binb2b64(core_hmac_sha1(key, data)); },
1324 b64_sha1: function (s) { return binb2b64(core_sha1(str2binb(s),s.length * 8)); },
1325 binb2str: binb2str,
1326 core_hmac_sha1: core_hmac_sha1,
1327 str_hmac_sha1: function (key, data){ return binb2str(core_hmac_sha1(key, data)); },
1328 str_sha1: function (s) { return binb2str(core_sha1(str2binb(s),s.length * 8)); },
1329};
1330}));
1331
1332/*
1333 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
1334 * Digest Algorithm, as defined in RFC 1321.
1335 * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
1336 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
1337 * Distributed under the BSD License
1338 * See http://pajhome.org.uk/crypt/md5 for more info.
1339 */
1340/*
1341 * Everything that isn't used by Strophe has been stripped here!
1342 */
1343
1344(function (root, factory) {
1345 if (typeof define === 'function' && define.amd) {
1346 define('strophe-md5',[], function () {
1347 return factory();
1348 });
1349 } else if (typeof exports === 'object') {
1350 module.exports = factory();
1351 } else {
1352 // Browser globals
1353 root.MD5 = factory();
1354 }
1355}(this, function () {
1356 /*
1357 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
1358 * to work around bugs in some JS interpreters.
1359 */
1360 var safe_add = function (x, y) {
1361 var lsw = (x & 0xFFFF) + (y & 0xFFFF);
1362 var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
1363 return (msw << 16) | (lsw & 0xFFFF);
1364 };
1365
1366 /*
1367 * Bitwise rotate a 32-bit number to the left.
1368 */
1369 var bit_rol = function (num, cnt) {
1370 return (num << cnt) | (num >>> (32 - cnt));
1371 };
1372
1373 /*
1374 * Convert a string to an array of little-endian words
1375 */
1376 var str2binl = function (str) {
1377 var bin = [];
1378 for(var i = 0; i < str.length * 8; i += 8)
1379 {
1380 bin[i>>5] |= (str.charCodeAt(i / 8) & 255) << (i%32);
1381 }
1382 return bin;
1383 };
1384
1385 /*
1386 * Convert an array of little-endian words to a string
1387 */
1388 var binl2str = function (bin) {
1389 var str = "";
1390 for(var i = 0; i < bin.length * 32; i += 8)
1391 {
1392 str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & 255);
1393 }
1394 return str;
1395 };
1396
1397 /*
1398 * Convert an array of little-endian words to a hex string.
1399 */
1400 var binl2hex = function (binarray) {
1401 var hex_tab = "0123456789abcdef";
1402 var str = "";
1403 for(var i = 0; i < binarray.length * 4; i++)
1404 {
1405 str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
1406 hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
1407 }
1408 return str;
1409 };
1410
1411 /*
1412 * These functions implement the four basic operations the algorithm uses.
1413 */
1414 var md5_cmn = function (q, a, b, x, s, t) {
1415 return safe_add(bit_rol(safe_add(safe_add(a, q),safe_add(x, t)), s),b);
1416 };
1417
1418 var md5_ff = function (a, b, c, d, x, s, t) {
1419 return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
1420 };
1421
1422 var md5_gg = function (a, b, c, d, x, s, t) {
1423 return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
1424 };
1425
1426 var md5_hh = function (a, b, c, d, x, s, t) {
1427 return md5_cmn(b ^ c ^ d, a, b, x, s, t);
1428 };
1429
1430 var md5_ii = function (a, b, c, d, x, s, t) {
1431 return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
1432 };
1433
1434 /*
1435 * Calculate the MD5 of an array of little-endian words, and a bit length
1436 */
1437 var core_md5 = function (x, len) {
1438 /* append padding */
1439 x[len >> 5] |= 0x80 << ((len) % 32);
1440 x[(((len + 64) >>> 9) << 4) + 14] = len;
1441
1442 var a = 1732584193;
1443 var b = -271733879;
1444 var c = -1732584194;
1445 var d = 271733878;
1446
1447 var olda, oldb, oldc, oldd;
1448 for (var i = 0; i < x.length; i += 16)
1449 {
1450 olda = a;
1451 oldb = b;
1452 oldc = c;
1453 oldd = d;
1454
1455 a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
1456 d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
1457 c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
1458 b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
1459 a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
1460 d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
1461 c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
1462 b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
1463 a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
1464 d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
1465 c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
1466 b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
1467 a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
1468 d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
1469 c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
1470 b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
1471
1472 a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
1473 d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
1474 c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
1475 b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
1476 a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
1477 d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
1478 c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
1479 b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
1480 a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
1481 d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
1482 c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
1483 b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
1484 a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
1485 d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
1486 c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
1487 b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
1488
1489 a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
1490 d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
1491 c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
1492 b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
1493 a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
1494 d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
1495 c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
1496 b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
1497 a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
1498 d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
1499 c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
1500 b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
1501 a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
1502 d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
1503 c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
1504 b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
1505
1506 a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
1507 d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
1508 c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
1509 b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
1510 a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
1511 d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
1512 c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
1513 b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
1514 a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
1515 d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
1516 c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
1517 b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
1518 a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
1519 d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
1520 c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
1521 b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
1522
1523 a = safe_add(a, olda);
1524 b = safe_add(b, oldb);
1525 c = safe_add(c, oldc);
1526 d = safe_add(d, oldd);
1527 }
1528 return [a, b, c, d];
1529 };
1530
1531 var obj = {
1532 /*
1533 * These are the functions you'll usually want to call.
1534 * They take string arguments and return either hex or base-64 encoded
1535 * strings.
1536 */
1537 hexdigest: function (s) {
1538 return binl2hex(core_md5(str2binl(s), s.length * 8));
1539 },
1540
1541 hash: function (s) {
1542 return binl2str(core_md5(str2binl(s), s.length * 8));
1543 }
1544 };
1545 return obj;
1546}));
1547
1548(function (root, factory) {
1549 if (typeof define === 'function' && define.amd) {
1550 define('strophe-utils',[], function () {
1551 return factory();
1552 });
1553 } else if (typeof exports === 'object') {
1554 module.exports = factory();
1555 } else {
1556 // Browser globals
1557 root.stropheUtils = factory();
1558 }
1559}(this, function () {
1560
1561 var utils = {
1562
1563 utf16to8: function (str) {
1564 var i, c;
1565 var out = "";
1566 var len = str.length;
1567 for (i = 0; i < len; i++) {
1568 c = str.charCodeAt(i);
1569 if ((c >= 0x0000) && (c <= 0x007F)) {
1570 out += str.charAt(i);
1571 } else if (c > 0x07FF) {
1572 out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
1573 out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
1574 out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
1575 } else {
1576 out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
1577 out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
1578 }
1579 }
1580 return out;
1581 },
1582
1583 addCookies: function (cookies) {
1584 /* Parameters:
1585 * (Object) cookies - either a map of cookie names
1586 * to string values or to maps of cookie values.
1587 *
1588 * For example:
1589 * { "myCookie": "1234" }
1590 *
1591 * or:
1592 * { "myCookie": {
1593 * "value": "1234",
1594 * "domain": ".example.org",
1595 * "path": "/",
1596 * "expires": expirationDate
1597 * }
1598 * }
1599 *
1600 * These values get passed to Strophe.Connection via
1601 * options.cookies
1602 */
1603 var cookieName, cookieObj, isObj, cookieValue, expires, domain, path;
1604 for (cookieName in (cookies || {})) {
1605 expires = '';
1606 domain = '';
1607 path = '';
1608 cookieObj = cookies[cookieName];
1609 isObj = typeof cookieObj === "object";
1610 cookieValue = escape(unescape(isObj ? cookieObj.value : cookieObj));
1611 if (isObj) {
1612 expires = cookieObj.expires ? ";expires="+cookieObj.expires : '';
1613 domain = cookieObj.domain ? ";domain="+cookieObj.domain : '';
1614 path = cookieObj.path ? ";path="+cookieObj.path : '';
1615 }
1616 document.cookie =
1617 cookieName+'='+cookieValue + expires + domain + path;
1618 }
1619 }
1620 };
1621 return utils;
1622}));
1623
1624/*
1625 This program is distributed under the terms of the MIT license.
1626 Please see the LICENSE file for details.
1627
1628 Copyright 2006-2008, OGG, LLC
1629*/
1630
1631/* jshint undef: true, unused: true:, noarg: true, latedef: true */
1632/*global define, document, sessionStorage, setTimeout, clearTimeout, ActiveXObject, DOMParser, btoa, atob */
1633
1634(function (root, factory) {
1635 if (typeof define === 'function' && define.amd) {
1636 define('strophe-core',[
1637 'strophe-sha1',
1638 'strophe-md5',
1639 'strophe-utils'
1640 ], function () {
1641 return factory.apply(this, arguments);
1642 });
1643 } else if (typeof exports === 'object') {
1644 module.exports = factory(
1645 require('./sha1'),
1646 require('./md5'),
1647 require('./utils')
1648 );
1649 } else {
1650 // Browser globals
1651 var o = factory(root.SHA1, root.MD5, root.stropheUtils);
1652 root.Strophe = o.Strophe;
1653 root.$build = o.$build;
1654 root.$iq = o.$iq;
1655 root.$msg = o.$msg;
1656 root.$pres = o.$pres;
1657 root.SHA1 = o.SHA1;
1658 root.MD5 = o.MD5;
1659 root.b64_hmac_sha1 = o.SHA1.b64_hmac_sha1;
1660 root.b64_sha1 = o.SHA1.b64_sha1;
1661 root.str_hmac_sha1 = o.SHA1.str_hmac_sha1;
1662 root.str_sha1 = o.SHA1.str_sha1;
1663 }
1664}(this, function (SHA1, MD5, utils) {
1665
1666var Strophe;
1667
1668/** Function: $build
1669 * Create a Strophe.Builder.
1670 * This is an alias for 'new Strophe.Builder(name, attrs)'.
1671 *
1672 * Parameters:
1673 * (String) name - The root element name.
1674 * (Object) attrs - The attributes for the root element in object notation.
1675 *
1676 * Returns:
1677 * A new Strophe.Builder object.
1678 */
1679function $build(name, attrs) { return new Strophe.Builder(name, attrs); }
1680
1681/** Function: $msg
1682 * Create a Strophe.Builder with a <message/> element as the root.
1683 *
1684 * Parameters:
1685 * (Object) attrs - The <message/> element attributes in object notation.
1686 *
1687 * Returns:
1688 * A new Strophe.Builder object.
1689 */
1690function $msg(attrs) { return new Strophe.Builder("message", attrs); }
1691
1692/** Function: $iq
1693 * Create a Strophe.Builder with an <iq/> element as the root.
1694 *
1695 * Parameters:
1696 * (Object) attrs - The <iq/> element attributes in object notation.
1697 *
1698 * Returns:
1699 * A new Strophe.Builder object.
1700 */
1701function $iq(attrs) { return new Strophe.Builder("iq", attrs); }
1702
1703/** Function: $pres
1704 * Create a Strophe.Builder with a <presence/> element as the root.
1705 *
1706 * Parameters:
1707 * (Object) attrs - The <presence/> element attributes in object notation.
1708 *
1709 * Returns:
1710 * A new Strophe.Builder object.
1711 */
1712function $pres(attrs) { return new Strophe.Builder("presence", attrs); }
1713
1714/** Class: Strophe
1715 * An object container for all Strophe library functions.
1716 *
1717 * This class is just a container for all the objects and constants
1718 * used in the library. It is not meant to be instantiated, but to
1719 * provide a namespace for library objects, constants, and functions.
1720 */
1721Strophe = {
1722 /** Constant: VERSION */
1723 VERSION: "1.2.15",
1724
1725 /** Constants: XMPP Namespace Constants
1726 * Common namespace constants from the XMPP RFCs and XEPs.
1727 *
1728 * NS.HTTPBIND - HTTP BIND namespace from XEP 124.
1729 * NS.BOSH - BOSH namespace from XEP 206.
1730 * NS.CLIENT - Main XMPP client namespace.
1731 * NS.AUTH - Legacy authentication namespace.
1732 * NS.ROSTER - Roster operations namespace.
1733 * NS.PROFILE - Profile namespace.
1734 * NS.DISCO_INFO - Service discovery info namespace from XEP 30.
1735 * NS.DISCO_ITEMS - Service discovery items namespace from XEP 30.
1736 * NS.MUC - Multi-User Chat namespace from XEP 45.
1737 * NS.SASL - XMPP SASL namespace from RFC 3920.
1738 * NS.STREAM - XMPP Streams namespace from RFC 3920.
1739 * NS.BIND - XMPP Binding namespace from RFC 3920.
1740 * NS.SESSION - XMPP Session namespace from RFC 3920.
1741 * NS.XHTML_IM - XHTML-IM namespace from XEP 71.
1742 * NS.XHTML - XHTML body namespace from XEP 71.
1743 */
1744 NS: {
1745 HTTPBIND: "http://jabber.org/protocol/httpbind",
1746 BOSH: "urn:xmpp:xbosh",
1747 CLIENT: "jabber:client",
1748 AUTH: "jabber:iq:auth",
1749 ROSTER: "jabber:iq:roster",
1750 PROFILE: "jabber:iq:profile",
1751 DISCO_INFO: "http://jabber.org/protocol/disco#info",
1752 DISCO_ITEMS: "http://jabber.org/protocol/disco#items",
1753 MUC: "http://jabber.org/protocol/muc",
1754 SASL: "urn:ietf:params:xml:ns:xmpp-sasl",
1755 STREAM: "http://etherx.jabber.org/streams",
1756 FRAMING: "urn:ietf:params:xml:ns:xmpp-framing",
1757 BIND: "urn:ietf:params:xml:ns:xmpp-bind",
1758 SESSION: "urn:ietf:params:xml:ns:xmpp-session",
1759 VERSION: "jabber:iq:version",
1760 STANZAS: "urn:ietf:params:xml:ns:xmpp-stanzas",
1761 XHTML_IM: "http://jabber.org/protocol/xhtml-im",
1762 XHTML: "http://www.w3.org/1999/xhtml"
1763 },
1764
1765 /** Constants: XHTML_IM Namespace
1766 * contains allowed tags, tag attributes, and css properties.
1767 * Used in the createHtml function to filter incoming html into the allowed XHTML-IM subset.
1768 * See http://xmpp.org/extensions/xep-0071.html#profile-summary for the list of recommended
1769 * allowed tags and their attributes.
1770 */
1771 XHTML: {
1772 tags: ['a','blockquote','br','cite','em','img','li','ol','p','span','strong','ul','body'],
1773 attributes: {
1774 'a': ['href'],
1775 'blockquote': ['style'],
1776 'br': [],
1777 'cite': ['style'],
1778 'em': [],
1779 'img': ['src', 'alt', 'style', 'height', 'width'],
1780 'li': ['style'],
1781 'ol': ['style'],
1782 'p': ['style'],
1783 'span': ['style'],
1784 'strong': [],
1785 'ul': ['style'],
1786 'body': []
1787 },
1788 css: ['background-color','color','font-family','font-size','font-style','font-weight','margin-left','margin-right','text-align','text-decoration'],
1789 /** Function: XHTML.validTag
1790 *
1791 * Utility method to determine whether a tag is allowed
1792 * in the XHTML_IM namespace.
1793 *
1794 * XHTML tag names are case sensitive and must be lower case.
1795 */
1796 validTag: function(tag) {
1797 for (var i = 0; i < Strophe.XHTML.tags.length; i++) {
1798 if (tag === Strophe.XHTML.tags[i]) {
1799 return true;
1800 }
1801 }
1802 return false;
1803 },
1804 /** Function: XHTML.validAttribute
1805 *
1806 * Utility method to determine whether an attribute is allowed
1807 * as recommended per XEP-0071
1808 *
1809 * XHTML attribute names are case sensitive and must be lower case.
1810 */
1811 validAttribute: function(tag, attribute) {
1812 if (typeof Strophe.XHTML.attributes[tag] !== 'undefined' && Strophe.XHTML.attributes[tag].length > 0) {
1813 for (var i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {
1814 if (attribute === Strophe.XHTML.attributes[tag][i]) {
1815 return true;
1816 }
1817 }
1818 }
1819 return false;
1820 },
1821 validCSS: function(style) {
1822 for (var i = 0; i < Strophe.XHTML.css.length; i++) {
1823 if (style === Strophe.XHTML.css[i]) {
1824 return true;
1825 }
1826 }
1827 return false;
1828 }
1829 },
1830
1831 /** Constants: Connection Status Constants
1832 * Connection status constants for use by the connection handler
1833 * callback.
1834 *
1835 * Status.ERROR - An error has occurred
1836 * Status.CONNECTING - The connection is currently being made
1837 * Status.CONNFAIL - The connection attempt failed
1838 * Status.AUTHENTICATING - The connection is authenticating
1839 * Status.AUTHFAIL - The authentication attempt failed
1840 * Status.CONNECTED - The connection has succeeded
1841 * Status.DISCONNECTED - The connection has been terminated
1842 * Status.DISCONNECTING - The connection is currently being terminated
1843 * Status.ATTACHED - The connection has been attached
1844 * Status.REDIRECT - The connection has been redirected
1845 * Status.CONNTIMEOUT - The connection has timed out
1846 */
1847 Status: {
1848 ERROR: 0,
1849 CONNECTING: 1,
1850 CONNFAIL: 2,
1851 AUTHENTICATING: 3,
1852 AUTHFAIL: 4,
1853 CONNECTED: 5,
1854 DISCONNECTED: 6,
1855 DISCONNECTING: 7,
1856 ATTACHED: 8,
1857 REDIRECT: 9,
1858 CONNTIMEOUT: 10
1859 },
1860
1861 ErrorCondition: {
1862 BAD_FORMAT: "bad-format",
1863 CONFLICT: "conflict",
1864 MISSING_JID_NODE: "x-strophe-bad-non-anon-jid",
1865 NO_AUTH_MECH: "no-auth-mech",
1866 UNKNOWN_REASON: "unknown",
1867 },
1868
1869 /** Constants: Log Level Constants
1870 * Logging level indicators.
1871 *
1872 * LogLevel.DEBUG - Debug output
1873 * LogLevel.INFO - Informational output
1874 * LogLevel.WARN - Warnings
1875 * LogLevel.ERROR - Errors
1876 * LogLevel.FATAL - Fatal errors
1877 */
1878 LogLevel: {
1879 DEBUG: 0,
1880 INFO: 1,
1881 WARN: 2,
1882 ERROR: 3,
1883 FATAL: 4
1884 },
1885
1886 /** PrivateConstants: DOM Element Type Constants
1887 * DOM element types.
1888 *
1889 * ElementType.NORMAL - Normal element.
1890 * ElementType.TEXT - Text data element.
1891 * ElementType.FRAGMENT - XHTML fragment element.
1892 */
1893 ElementType: {
1894 NORMAL: 1,
1895 TEXT: 3,
1896 CDATA: 4,
1897 FRAGMENT: 11
1898 },
1899
1900 /** PrivateConstants: Timeout Values
1901 * Timeout values for error states. These values are in seconds.
1902 * These should not be changed unless you know exactly what you are
1903 * doing.
1904 *
1905 * TIMEOUT - Timeout multiplier. A waiting request will be considered
1906 * failed after Math.floor(TIMEOUT * wait) seconds have elapsed.
1907 * This defaults to 1.1, and with default wait, 66 seconds.
1908 * SECONDARY_TIMEOUT - Secondary timeout multiplier. In cases where
1909 * Strophe can detect early failure, it will consider the request
1910 * failed if it doesn't return after
1911 * Math.floor(SECONDARY_TIMEOUT * wait) seconds have elapsed.
1912 * This defaults to 0.1, and with default wait, 6 seconds.
1913 */
1914 TIMEOUT: 1.1,
1915 SECONDARY_TIMEOUT: 0.1,
1916
1917 /** Function: addNamespace
1918 * This function is used to extend the current namespaces in
1919 * Strophe.NS. It takes a key and a value with the key being the
1920 * name of the new namespace, with its actual value.
1921 * For example:
1922 * Strophe.addNamespace('PUBSUB', "http://jabber.org/protocol/pubsub");
1923 *
1924 * Parameters:
1925 * (String) name - The name under which the namespace will be
1926 * referenced under Strophe.NS
1927 * (String) value - The actual namespace.
1928 */
1929 addNamespace: function (name, value) {
1930 Strophe.NS[name] = value;
1931 },
1932
1933 /** Function: forEachChild
1934 * Map a function over some or all child elements of a given element.
1935 *
1936 * This is a small convenience function for mapping a function over
1937 * some or all of the children of an element. If elemName is null, all
1938 * children will be passed to the function, otherwise only children
1939 * whose tag names match elemName will be passed.
1940 *
1941 * Parameters:
1942 * (XMLElement) elem - The element to operate on.
1943 * (String) elemName - The child element tag name filter.
1944 * (Function) func - The function to apply to each child. This
1945 * function should take a single argument, a DOM element.
1946 */
1947 forEachChild: function (elem, elemName, func) {
1948 var i, childNode;
1949 for (i = 0; i < elem.childNodes.length; i++) {
1950 childNode = elem.childNodes[i];
1951 if (childNode.nodeType === Strophe.ElementType.NORMAL &&
1952 (!elemName || this.isTagEqual(childNode, elemName))) {
1953 func(childNode);
1954 }
1955 }
1956 },
1957
1958 /** Function: isTagEqual
1959 * Compare an element's tag name with a string.
1960 *
1961 * This function is case sensitive.
1962 *
1963 * Parameters:
1964 * (XMLElement) el - A DOM element.
1965 * (String) name - The element name.
1966 *
1967 * Returns:
1968 * true if the element's tag name matches _el_, and false
1969 * otherwise.
1970 */
1971 isTagEqual: function (el, name) {
1972 return el.tagName === name;
1973 },
1974
1975 /** PrivateVariable: _xmlGenerator
1976 * _Private_ variable that caches a DOM document to
1977 * generate elements.
1978 */
1979 _xmlGenerator: null,
1980
1981 /** PrivateFunction: _makeGenerator
1982 * _Private_ function that creates a dummy XML DOM document to serve as
1983 * an element and text node generator.
1984 */
1985 _makeGenerator: function () {
1986 var doc;
1987 // IE9 does implement createDocument(); however, using it will cause the browser to leak memory on page unload.
1988 // Here, we test for presence of createDocument() plus IE's proprietary documentMode attribute, which would be
1989 // less than 10 in the case of IE9 and below.
1990 if (document.implementation.createDocument === undefined ||
1991 document.implementation.createDocument && document.documentMode && document.documentMode < 10) {
1992 doc = this._getIEXmlDom();
1993 doc.appendChild(doc.createElement('strophe'));
1994 } else {
1995 doc = document.implementation
1996 .createDocument('jabber:client', 'strophe', null);
1997 }
1998 return doc;
1999 },
2000
2001 /** Function: xmlGenerator
2002 * Get the DOM document to generate elements.
2003 *
2004 * Returns:
2005 * The currently used DOM document.
2006 */
2007 xmlGenerator: function () {
2008 if (!Strophe._xmlGenerator) {
2009 Strophe._xmlGenerator = Strophe._makeGenerator();
2010 }
2011 return Strophe._xmlGenerator;
2012 },
2013
2014 /** PrivateFunction: _getIEXmlDom
2015 * Gets IE xml doc object
2016 *
2017 * Returns:
2018 * A Microsoft XML DOM Object
2019 * See Also:
2020 * http://msdn.microsoft.com/en-us/library/ms757837%28VS.85%29.aspx
2021 */
2022 _getIEXmlDom : function() {
2023 var doc = null;
2024 var docStrings = [
2025 "Msxml2.DOMDocument.6.0",
2026 "Msxml2.DOMDocument.5.0",
2027 "Msxml2.DOMDocument.4.0",
2028 "MSXML2.DOMDocument.3.0",
2029 "MSXML2.DOMDocument",
2030 "MSXML.DOMDocument",
2031 "Microsoft.XMLDOM"
2032 ];
2033
2034 for (var d = 0; d < docStrings.length; d++) {
2035 if (doc === null) {
2036 try {
2037 doc = new ActiveXObject(docStrings[d]);
2038 } catch (e) {
2039 doc = null;
2040 }
2041 } else {
2042 break;
2043 }
2044 }
2045 return doc;
2046 },
2047
2048 /** Function: xmlElement
2049 * Create an XML DOM element.
2050 *
2051 * This function creates an XML DOM element correctly across all
2052 * implementations. Note that these are not HTML DOM elements, which
2053 * aren't appropriate for XMPP stanzas.
2054 *
2055 * Parameters:
2056 * (String) name - The name for the element.
2057 * (Array|Object) attrs - An optional array or object containing
2058 * key/value pairs to use as element attributes. The object should
2059 * be in the format {'key': 'value'} or {key: 'value'}. The array
2060 * should have the format [['key1', 'value1'], ['key2', 'value2']].
2061 * (String) text - The text child data for the element.
2062 *
2063 * Returns:
2064 * A new XML DOM element.
2065 */
2066 xmlElement: function (name) {
2067 if (!name) { return null; }
2068
2069 var node = Strophe.xmlGenerator().createElement(name);
2070 // FIXME: this should throw errors if args are the wrong type or
2071 // there are more than two optional args
2072 var a, i, k;
2073 for (a = 1; a < arguments.length; a++) {
2074 var arg = arguments[a];
2075 if (!arg) { continue; }
2076 if (typeof(arg) === "string" ||
2077 typeof(arg) === "number") {
2078 node.appendChild(Strophe.xmlTextNode(arg));
2079 } else if (typeof(arg) === "object" &&
2080 typeof(arg.sort) === "function") {
2081 for (i = 0; i < arg.length; i++) {
2082 var attr = arg[i];
2083 if (typeof(attr) === "object" &&
2084 typeof(attr.sort) === "function" &&
2085 attr[1] !== undefined &&
2086 attr[1] !== null) {
2087 node.setAttribute(attr[0], attr[1]);
2088 }
2089 }
2090 } else if (typeof(arg) === "object") {
2091 for (k in arg) {
2092 if (arg.hasOwnProperty(k)) {
2093 if (arg[k] !== undefined &&
2094 arg[k] !== null) {
2095 node.setAttribute(k, arg[k]);
2096 }
2097 }
2098 }
2099 }
2100 }
2101
2102 return node;
2103 },
2104
2105 /* Function: xmlescape
2106 * Excapes invalid xml characters.
2107 *
2108 * Parameters:
2109 * (String) text - text to escape.
2110 *
2111 * Returns:
2112 * Escaped text.
2113 */
2114 xmlescape: function(text) {
2115 text = text.replace(/\&/g, "&");
2116 text = text.replace(/</g, "<");
2117 text = text.replace(/>/g, ">");
2118 text = text.replace(/'/g, "'");
2119 text = text.replace(/"/g, """);
2120 return text;
2121 },
2122
2123 /* Function: xmlunescape
2124 * Unexcapes invalid xml characters.
2125 *
2126 * Parameters:
2127 * (String) text - text to unescape.
2128 *
2129 * Returns:
2130 * Unescaped text.
2131 */
2132 xmlunescape: function(text) {
2133 text = text.replace(/\&/g, "&");
2134 text = text.replace(/</g, "<");
2135 text = text.replace(/>/g, ">");
2136 text = text.replace(/'/g, "'");
2137 text = text.replace(/"/g, "\"");
2138 return text;
2139 },
2140
2141 /** Function: xmlTextNode
2142 * Creates an XML DOM text node.
2143 *
2144 * Provides a cross implementation version of document.createTextNode.
2145 *
2146 * Parameters:
2147 * (String) text - The content of the text node.
2148 *
2149 * Returns:
2150 * A new XML DOM text node.
2151 */
2152 xmlTextNode: function (text) {
2153 return Strophe.xmlGenerator().createTextNode(text);
2154 },
2155
2156 /** Function: xmlHtmlNode
2157 * Creates an XML DOM html node.
2158 *
2159 * Parameters:
2160 * (String) html - The content of the html node.
2161 *
2162 * Returns:
2163 * A new XML DOM text node.
2164 */
2165 xmlHtmlNode: function (html) {
2166 var node;
2167 //ensure text is escaped
2168 if (DOMParser) {
2169 var parser = new DOMParser();
2170 node = parser.parseFromString(html, "text/xml");
2171 } else {
2172 node = new ActiveXObject("Microsoft.XMLDOM");
2173 node.async="false";
2174 node.loadXML(html);
2175 }
2176 return node;
2177 },
2178
2179 /** Function: getText
2180 * Get the concatenation of all text children of an element.
2181 *
2182 * Parameters:
2183 * (XMLElement) elem - A DOM element.
2184 *
2185 * Returns:
2186 * A String with the concatenated text of all text element children.
2187 */
2188 getText: function (elem) {
2189 if (!elem) { return null; }
2190
2191 var str = "";
2192 if (elem.childNodes.length === 0 && elem.nodeType === Strophe.ElementType.TEXT) {
2193 str += elem.nodeValue;
2194 }
2195
2196 for (var i = 0; i < elem.childNodes.length; i++) {
2197 if (elem.childNodes[i].nodeType === Strophe.ElementType.TEXT) {
2198 str += elem.childNodes[i].nodeValue;
2199 }
2200 }
2201
2202 return Strophe.xmlescape(str);
2203 },
2204
2205 /** Function: copyElement
2206 * Copy an XML DOM element.
2207 *
2208 * This function copies a DOM element and all its descendants and returns
2209 * the new copy.
2210 *
2211 * Parameters:
2212 * (XMLElement) elem - A DOM element.
2213 *
2214 * Returns:
2215 * A new, copied DOM element tree.
2216 */
2217 copyElement: function (elem) {
2218 var i, el;
2219 if (elem.nodeType === Strophe.ElementType.NORMAL) {
2220 el = Strophe.xmlElement(elem.tagName);
2221
2222 for (i = 0; i < elem.attributes.length; i++) {
2223 el.setAttribute(elem.attributes[i].nodeName,
2224 elem.attributes[i].value);
2225 }
2226
2227 for (i = 0; i < elem.childNodes.length; i++) {
2228 el.appendChild(Strophe.copyElement(elem.childNodes[i]));
2229 }
2230 } else if (elem.nodeType === Strophe.ElementType.TEXT) {
2231 el = Strophe.xmlGenerator().createTextNode(elem.nodeValue);
2232 }
2233 return el;
2234 },
2235
2236
2237 /** Function: createHtml
2238 * Copy an HTML DOM element into an XML DOM.
2239 *
2240 * This function copies a DOM element and all its descendants and returns
2241 * the new copy.
2242 *
2243 * Parameters:
2244 * (HTMLElement) elem - A DOM element.
2245 *
2246 * Returns:
2247 * A new, copied DOM element tree.
2248 */
2249 createHtml: function (elem) {
2250 var i, el, j, tag, attribute, value, css, cssAttrs, attr, cssName, cssValue;
2251 if (elem.nodeType === Strophe.ElementType.NORMAL) {
2252 tag = elem.nodeName.toLowerCase(); // XHTML tags must be lower case.
2253 if(Strophe.XHTML.validTag(tag)) {
2254 try {
2255 el = Strophe.xmlElement(tag);
2256 for(i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {
2257 attribute = Strophe.XHTML.attributes[tag][i];
2258 value = elem.getAttribute(attribute);
2259 if(typeof value === 'undefined' || value === null || value === '' || value === false || value === 0) {
2260 continue;
2261 }
2262 if(attribute === 'style' && typeof value === 'object') {
2263 if(typeof value.cssText !== 'undefined') {
2264 value = value.cssText; // we're dealing with IE, need to get CSS out
2265 }
2266 }
2267 // filter out invalid css styles
2268 if(attribute === 'style') {
2269 css = [];
2270 cssAttrs = value.split(';');
2271 for(j = 0; j < cssAttrs.length; j++) {
2272 attr = cssAttrs[j].split(':');
2273 cssName = attr[0].replace(/^\s*/, "").replace(/\s*$/, "").toLowerCase();
2274 if(Strophe.XHTML.validCSS(cssName)) {
2275 cssValue = attr[1].replace(/^\s*/, "").replace(/\s*$/, "");
2276 css.push(cssName + ': ' + cssValue);
2277 }
2278 }
2279 if(css.length > 0) {
2280 value = css.join('; ');
2281 el.setAttribute(attribute, value);
2282 }
2283 } else {
2284 el.setAttribute(attribute, value);
2285 }
2286 }
2287
2288 for (i = 0; i < elem.childNodes.length; i++) {
2289 el.appendChild(Strophe.createHtml(elem.childNodes[i]));
2290 }
2291 } catch(e) { // invalid elements
2292 el = Strophe.xmlTextNode('');
2293 }
2294 } else {
2295 el = Strophe.xmlGenerator().createDocumentFragment();
2296 for (i = 0; i < elem.childNodes.length; i++) {
2297 el.appendChild(Strophe.createHtml(elem.childNodes[i]));
2298 }
2299 }
2300 } else if (elem.nodeType === Strophe.ElementType.FRAGMENT) {
2301 el = Strophe.xmlGenerator().createDocumentFragment();
2302 for (i = 0; i < elem.childNodes.length; i++) {
2303 el.appendChild(Strophe.createHtml(elem.childNodes[i]));
2304 }
2305 } else if (elem.nodeType === Strophe.ElementType.TEXT) {
2306 el = Strophe.xmlTextNode(elem.nodeValue);
2307 }
2308 return el;
2309 },
2310
2311 /** Function: escapeNode
2312 * Escape the node part (also called local part) of a JID.
2313 *
2314 * Parameters:
2315 * (String) node - A node (or local part).
2316 *
2317 * Returns:
2318 * An escaped node (or local part).
2319 */
2320 escapeNode: function (node) {
2321 if (typeof node !== "string") { return node; }
2322 return node.replace(/^\s+|\s+$/g, '')
2323 .replace(/\\/g, "\\5c")
2324 .replace(/ /g, "\\20")
2325 .replace(/\"/g, "\\22")
2326 .replace(/\&/g, "\\26")
2327 .replace(/\'/g, "\\27")
2328 .replace(/\//g, "\\2f")
2329 .replace(/:/g, "\\3a")
2330 .replace(/</g, "\\3c")
2331 .replace(/>/g, "\\3e")
2332 .replace(/@/g, "\\40");
2333 },
2334
2335 /** Function: unescapeNode
2336 * Unescape a node part (also called local part) of a JID.
2337 *
2338 * Parameters:
2339 * (String) node - A node (or local part).
2340 *
2341 * Returns:
2342 * An unescaped node (or local part).
2343 */
2344 unescapeNode: function (node) {
2345 if (typeof node !== "string") { return node; }
2346 return node.replace(/\\20/g, " ")
2347 .replace(/\\22/g, '"')
2348 .replace(/\\26/g, "&")
2349 .replace(/\\27/g, "'")
2350 .replace(/\\2f/g, "/")
2351 .replace(/\\3a/g, ":")
2352 .replace(/\\3c/g, "<")
2353 .replace(/\\3e/g, ">")
2354 .replace(/\\40/g, "@")
2355 .replace(/\\5c/g, "\\");
2356 },
2357
2358 /** Function: getNodeFromJid
2359 * Get the node portion of a JID String.
2360 *
2361 * Parameters:
2362 * (String) jid - A JID.
2363 *
2364 * Returns:
2365 * A String containing the node.
2366 */
2367 getNodeFromJid: function (jid) {
2368 if (jid.indexOf("@") < 0) { return null; }
2369 return jid.split("@")[0];
2370 },
2371
2372 /** Function: getDomainFromJid
2373 * Get the domain portion of a JID String.
2374 *
2375 * Parameters:
2376 * (String) jid - A JID.
2377 *
2378 * Returns:
2379 * A String containing the domain.
2380 */
2381 getDomainFromJid: function (jid) {
2382 var bare = Strophe.getBareJidFromJid(jid);
2383 if (bare.indexOf("@") < 0) {
2384 return bare;
2385 } else {
2386 var parts = bare.split("@");
2387 parts.splice(0, 1);
2388 return parts.join('@');
2389 }
2390 },
2391
2392 /** Function: getResourceFromJid
2393 * Get the resource portion of a JID String.
2394 *
2395 * Parameters:
2396 * (String) jid - A JID.
2397 *
2398 * Returns:
2399 * A String containing the resource.
2400 */
2401 getResourceFromJid: function (jid) {
2402 var s = jid.split("/");
2403 if (s.length < 2) { return null; }
2404 s.splice(0, 1);
2405 return s.join('/');
2406 },
2407
2408 /** Function: getBareJidFromJid
2409 * Get the bare JID from a JID String.
2410 *
2411 * Parameters:
2412 * (String) jid - A JID.
2413 *
2414 * Returns:
2415 * A String containing the bare JID.
2416 */
2417 getBareJidFromJid: function (jid) {
2418 return jid ? jid.split("/")[0] : null;
2419 },
2420
2421 /** PrivateFunction: _handleError
2422 * _Private_ function that properly logs an error to the console
2423 */
2424 _handleError: function (e) {
2425 if (typeof e.stack !== "undefined") {
2426 Strophe.fatal(e.stack);
2427 }
2428 if (e.sourceURL) {
2429 Strophe.fatal("error: " + this.handler + " " + e.sourceURL + ":" +
2430 e.line + " - " + e.name + ": " + e.message);
2431 } else if (e.fileName) {
2432 Strophe.fatal("error: " + this.handler + " " +
2433 e.fileName + ":" + e.lineNumber + " - " +
2434 e.name + ": " + e.message);
2435 } else {
2436 Strophe.fatal("error: " + e.message);
2437 }
2438 },
2439
2440 /** Function: log
2441 * User overrideable logging function.
2442 *
2443 * This function is called whenever the Strophe library calls any
2444 * of the logging functions. The default implementation of this
2445 * function logs only fatal errors. If client code wishes to handle the logging
2446 * messages, it should override this with
2447 * > Strophe.log = function (level, msg) {
2448 * > (user code here)
2449 * > };
2450 *
2451 * Please note that data sent and received over the wire is logged
2452 * via Strophe.Connection.rawInput() and Strophe.Connection.rawOutput().
2453 *
2454 * The different levels and their meanings are
2455 *
2456 * DEBUG - Messages useful for debugging purposes.
2457 * INFO - Informational messages. This is mostly information like
2458 * 'disconnect was called' or 'SASL auth succeeded'.
2459 * WARN - Warnings about potential problems. This is mostly used
2460 * to report transient connection errors like request timeouts.
2461 * ERROR - Some error occurred.
2462 * FATAL - A non-recoverable fatal error occurred.
2463 *
2464 * Parameters:
2465 * (Integer) level - The log level of the log message. This will
2466 * be one of the values in Strophe.LogLevel.
2467 * (String) msg - The log message.
2468 */
2469 log: function (level, msg) {
2470 if (level === this.LogLevel.FATAL &&
2471 typeof window.console === 'object' &&
2472 typeof window.console.error === 'function') {
2473 window.console.error(msg);
2474 }
2475 },
2476
2477 /** Function: debug
2478 * Log a message at the Strophe.LogLevel.DEBUG level.
2479 *
2480 * Parameters:
2481 * (String) msg - The log message.
2482 */
2483 debug: function(msg) {
2484 this.log(this.LogLevel.DEBUG, msg);
2485 },
2486
2487 /** Function: info
2488 * Log a message at the Strophe.LogLevel.INFO level.
2489 *
2490 * Parameters:
2491 * (String) msg - The log message.
2492 */
2493 info: function (msg) {
2494 this.log(this.LogLevel.INFO, msg);
2495 },
2496
2497 /** Function: warn
2498 * Log a message at the Strophe.LogLevel.WARN level.
2499 *
2500 * Parameters:
2501 * (String) msg - The log message.
2502 */
2503 warn: function (msg) {
2504 this.log(this.LogLevel.WARN, msg);
2505 },
2506
2507 /** Function: error
2508 * Log a message at the Strophe.LogLevel.ERROR level.
2509 *
2510 * Parameters:
2511 * (String) msg - The log message.
2512 */
2513 error: function (msg) {
2514 this.log(this.LogLevel.ERROR, msg);
2515 },
2516
2517 /** Function: fatal
2518 * Log a message at the Strophe.LogLevel.FATAL level.
2519 *
2520 * Parameters:
2521 * (String) msg - The log message.
2522 */
2523 fatal: function (msg) {
2524 this.log(this.LogLevel.FATAL, msg);
2525 },
2526
2527 /** Function: serialize
2528 * Render a DOM element and all descendants to a String.
2529 *
2530 * Parameters:
2531 * (XMLElement) elem - A DOM element.
2532 *
2533 * Returns:
2534 * The serialized element tree as a String.
2535 */
2536 serialize: function (elem) {
2537 var result;
2538
2539 if (!elem) { return null; }
2540
2541 if (typeof(elem.tree) === "function") {
2542 elem = elem.tree();
2543 }
2544
2545 var nodeName = elem.nodeName;
2546 var i, child;
2547
2548 if (elem.getAttribute("_realname")) {
2549 nodeName = elem.getAttribute("_realname");
2550 }
2551
2552 result = "<" + nodeName;
2553 for (i = 0; i < elem.attributes.length; i++) {
2554 if(elem.attributes[i].nodeName !== "_realname") {
2555 result += " " + elem.attributes[i].nodeName +
2556 "='" + Strophe.xmlescape(elem.attributes[i].value) + "'";
2557 }
2558 }
2559
2560 if (elem.childNodes.length > 0) {
2561 result += ">";
2562 for (i = 0; i < elem.childNodes.length; i++) {
2563 child = elem.childNodes[i];
2564 switch( child.nodeType ){
2565 case Strophe.ElementType.NORMAL:
2566 // normal element, so recurse
2567 result += Strophe.serialize(child);
2568 break;
2569 case Strophe.ElementType.TEXT:
2570 // text element to escape values
2571 result += Strophe.xmlescape(child.nodeValue);
2572 break;
2573 case Strophe.ElementType.CDATA:
2574 // cdata section so don't escape values
2575 result += "<![CDATA["+child.nodeValue+"]]>";
2576 }
2577 }
2578 result += "</" + nodeName + ">";
2579 } else {
2580 result += "/>";
2581 }
2582
2583 return result;
2584 },
2585
2586 /** PrivateVariable: _requestId
2587 * _Private_ variable that keeps track of the request ids for
2588 * connections.
2589 */
2590 _requestId: 0,
2591
2592 /** PrivateVariable: Strophe.connectionPlugins
2593 * _Private_ variable Used to store plugin names that need
2594 * initialization on Strophe.Connection construction.
2595 */
2596 _connectionPlugins: {},
2597
2598 /** Function: addConnectionPlugin
2599 * Extends the Strophe.Connection object with the given plugin.
2600 *
2601 * Parameters:
2602 * (String) name - The name of the extension.
2603 * (Object) ptype - The plugin's prototype.
2604 */
2605 addConnectionPlugin: function (name, ptype) {
2606 Strophe._connectionPlugins[name] = ptype;
2607 }
2608};
2609
2610/** Class: Strophe.Builder
2611 * XML DOM builder.
2612 *
2613 * This object provides an interface similar to JQuery but for building
2614 * DOM elements easily and rapidly. All the functions except for toString()
2615 * and tree() return the object, so calls can be chained. Here's an
2616 * example using the $iq() builder helper.
2617 * > $iq({to: 'you', from: 'me', type: 'get', id: '1'})
2618 * > .c('query', {xmlns: 'strophe:example'})
2619 * > .c('example')
2620 * > .toString()
2621 *
2622 * The above generates this XML fragment
2623 * > <iq to='you' from='me' type='get' id='1'>
2624 * > <query xmlns='strophe:example'>
2625 * > <example/>
2626 * > </query>
2627 * > </iq>
2628 * The corresponding DOM manipulations to get a similar fragment would be
2629 * a lot more tedious and probably involve several helper variables.
2630 *
2631 * Since adding children makes new operations operate on the child, up()
2632 * is provided to traverse up the tree. To add two children, do
2633 * > builder.c('child1', ...).up().c('child2', ...)
2634 * The next operation on the Builder will be relative to the second child.
2635 */
2636
2637/** Constructor: Strophe.Builder
2638 * Create a Strophe.Builder object.
2639 *
2640 * The attributes should be passed in object notation. For example
2641 * > var b = new Builder('message', {to: 'you', from: 'me'});
2642 * or
2643 * > var b = new Builder('messsage', {'xml:lang': 'en'});
2644 *
2645 * Parameters:
2646 * (String) name - The name of the root element.
2647 * (Object) attrs - The attributes for the root element in object notation.
2648 *
2649 * Returns:
2650 * A new Strophe.Builder.
2651 */
2652Strophe.Builder = function (name, attrs) {
2653 // Set correct namespace for jabber:client elements
2654 if (name === "presence" || name === "message" || name === "iq") {
2655 if (attrs && !attrs.xmlns) {
2656 attrs.xmlns = Strophe.NS.CLIENT;
2657 } else if (!attrs) {
2658 attrs = {xmlns: Strophe.NS.CLIENT};
2659 }
2660 }
2661
2662 // Holds the tree being built.
2663 this.nodeTree = Strophe.xmlElement(name, attrs);
2664
2665 // Points to the current operation node.
2666 this.node = this.nodeTree;
2667};
2668
2669Strophe.Builder.prototype = {
2670 /** Function: tree
2671 * Return the DOM tree.
2672 *
2673 * This function returns the current DOM tree as an element object. This
2674 * is suitable for passing to functions like Strophe.Connection.send().
2675 *
2676 * Returns:
2677 * The DOM tree as a element object.
2678 */
2679 tree: function () {
2680 return this.nodeTree;
2681 },
2682
2683 /** Function: toString
2684 * Serialize the DOM tree to a String.
2685 *
2686 * This function returns a string serialization of the current DOM
2687 * tree. It is often used internally to pass data to a
2688 * Strophe.Request object.
2689 *
2690 * Returns:
2691 * The serialized DOM tree in a String.
2692 */
2693 toString: function () {
2694 return Strophe.serialize(this.nodeTree);
2695 },
2696
2697 /** Function: up
2698 * Make the current parent element the new current element.
2699 *
2700 * This function is often used after c() to traverse back up the tree.
2701 * For example, to add two children to the same element
2702 * > builder.c('child1', {}).up().c('child2', {});
2703 *
2704 * Returns:
2705 * The Stophe.Builder object.
2706 */
2707 up: function () {
2708 this.node = this.node.parentNode;
2709 return this;
2710 },
2711
2712 /** Function: root
2713 * Make the root element the new current element.
2714 *
2715 * When at a deeply nested element in the tree, this function can be used
2716 * to jump back to the root of the tree, instead of having to repeatedly
2717 * call up().
2718 *
2719 * Returns:
2720 * The Stophe.Builder object.
2721 */
2722 root: function () {
2723 this.node = this.nodeTree;
2724 return this;
2725 },
2726
2727 /** Function: attrs
2728 * Add or modify attributes of the current element.
2729 *
2730 * The attributes should be passed in object notation. This function
2731 * does not move the current element pointer.
2732 *
2733 * Parameters:
2734 * (Object) moreattrs - The attributes to add/modify in object notation.
2735 *
2736 * Returns:
2737 * The Strophe.Builder object.
2738 */
2739 attrs: function (moreattrs) {
2740 for (var k in moreattrs) {
2741 if (moreattrs.hasOwnProperty(k)) {
2742 if (moreattrs[k] === undefined) {
2743 this.node.removeAttribute(k);
2744 } else {
2745 this.node.setAttribute(k, moreattrs[k]);
2746 }
2747 }
2748 }
2749 return this;
2750 },
2751
2752 /** Function: c
2753 * Add a child to the current element and make it the new current
2754 * element.
2755 *
2756 * This function moves the current element pointer to the child,
2757 * unless text is provided. If you need to add another child, it
2758 * is necessary to use up() to go back to the parent in the tree.
2759 *
2760 * Parameters:
2761 * (String) name - The name of the child.
2762 * (Object) attrs - The attributes of the child in object notation.
2763 * (String) text - The text to add to the child.
2764 *
2765 * Returns:
2766 * The Strophe.Builder object.
2767 */
2768 c: function (name, attrs, text) {
2769 var child = Strophe.xmlElement(name, attrs, text);
2770 this.node.appendChild(child);
2771 if (typeof text !== "string" && typeof text !=="number") {
2772 this.node = child;
2773 }
2774 return this;
2775 },
2776
2777 /** Function: cnode
2778 * Add a child to the current element and make it the new current
2779 * element.
2780 *
2781 * This function is the same as c() except that instead of using a
2782 * name and an attributes object to create the child it uses an
2783 * existing DOM element object.
2784 *
2785 * Parameters:
2786 * (XMLElement) elem - A DOM element.
2787 *
2788 * Returns:
2789 * The Strophe.Builder object.
2790 */
2791 cnode: function (elem) {
2792 var impNode;
2793 var xmlGen = Strophe.xmlGenerator();
2794 try {
2795 impNode = (xmlGen.importNode !== undefined);
2796 } catch (e) {
2797 impNode = false;
2798 }
2799 var newElem = impNode ?
2800 xmlGen.importNode(elem, true) :
2801 Strophe.copyElement(elem);
2802 this.node.appendChild(newElem);
2803 this.node = newElem;
2804 return this;
2805 },
2806
2807 /** Function: t
2808 * Add a child text element.
2809 *
2810 * This *does not* make the child the new current element since there
2811 * are no children of text elements.
2812 *
2813 * Parameters:
2814 * (String) text - The text data to append to the current element.
2815 *
2816 * Returns:
2817 * The Strophe.Builder object.
2818 */
2819 t: function (text) {
2820 var child = Strophe.xmlTextNode(text);
2821 this.node.appendChild(child);
2822 return this;
2823 },
2824
2825 /** Function: h
2826 * Replace current element contents with the HTML passed in.
2827 *
2828 * This *does not* make the child the new current element
2829 *
2830 * Parameters:
2831 * (String) html - The html to insert as contents of current element.
2832 *
2833 * Returns:
2834 * The Strophe.Builder object.
2835 */
2836 h: function (html) {
2837 var fragment = document.createElement('body');
2838
2839 // force the browser to try and fix any invalid HTML tags
2840 fragment.innerHTML = html;
2841
2842 // copy cleaned html into an xml dom
2843 var xhtml = Strophe.createHtml(fragment);
2844
2845 while(xhtml.childNodes.length > 0) {
2846 this.node.appendChild(xhtml.childNodes[0]);
2847 }
2848 return this;
2849 }
2850};
2851
2852/** PrivateClass: Strophe.Handler
2853 * _Private_ helper class for managing stanza handlers.
2854 *
2855 * A Strophe.Handler encapsulates a user provided callback function to be
2856 * executed when matching stanzas are received by the connection.
2857 * Handlers can be either one-off or persistant depending on their
2858 * return value. Returning true will cause a Handler to remain active, and
2859 * returning false will remove the Handler.
2860 *
2861 * Users will not use Strophe.Handler objects directly, but instead they
2862 * will use Strophe.Connection.addHandler() and
2863 * Strophe.Connection.deleteHandler().
2864 */
2865
2866/** PrivateConstructor: Strophe.Handler
2867 * Create and initialize a new Strophe.Handler.
2868 *
2869 * Parameters:
2870 * (Function) handler - A function to be executed when the handler is run.
2871 * (String) ns - The namespace to match.
2872 * (String) name - The element name to match.
2873 * (String) type - The element type to match.
2874 * (String) id - The element id attribute to match.
2875 * (String) from - The element from attribute to match.
2876 * (Object) options - Handler options
2877 *
2878 * Returns:
2879 * A new Strophe.Handler object.
2880 */
2881Strophe.Handler = function (handler, ns, name, type, id, from, options) {
2882 this.handler = handler;
2883 this.ns = ns;
2884 this.name = name;
2885 this.type = type;
2886 this.id = id;
2887 this.options = options || {'matchBareFromJid': false, 'ignoreNamespaceFragment': false};
2888 // BBB: Maintain backward compatibility with old `matchBare` option
2889 if (this.options.matchBare) {
2890 Strophe.warn('The "matchBare" option is deprecated, use "matchBareFromJid" instead.');
2891 this.options.matchBareFromJid = this.options.matchBare;
2892 delete this.options.matchBare;
2893 }
2894
2895 if (this.options.matchBareFromJid) {
2896 this.from = from ? Strophe.getBareJidFromJid(from) : null;
2897 } else {
2898 this.from = from;
2899 }
2900 // whether the handler is a user handler or a system handler
2901 this.user = true;
2902};
2903
2904Strophe.Handler.prototype = {
2905 /** PrivateFunction: getNamespace
2906 * Returns the XML namespace attribute on an element.
2907 * If `ignoreNamespaceFragment` was passed in for this handler, then the
2908 * URL fragment will be stripped.
2909 *
2910 * Parameters:
2911 * (XMLElement) elem - The XML element with the namespace.
2912 *
2913 * Returns:
2914 * The namespace, with optionally the fragment stripped.
2915 */
2916 getNamespace: function (elem) {
2917 var elNamespace = elem.getAttribute("xmlns");
2918 if (elNamespace && this.options.ignoreNamespaceFragment) {
2919 elNamespace = elNamespace.split('#')[0];
2920 }
2921 return elNamespace;
2922 },
2923
2924 /** PrivateFunction: namespaceMatch
2925 * Tests if a stanza matches the namespace set for this Strophe.Handler.
2926 *
2927 * Parameters:
2928 * (XMLElement) elem - The XML element to test.
2929 *
2930 * Returns:
2931 * true if the stanza matches and false otherwise.
2932 */
2933 namespaceMatch: function (elem) {
2934 var nsMatch = false;
2935 if (!this.ns) {
2936 return true;
2937 } else {
2938 var that = this;
2939 Strophe.forEachChild(elem, null, function (elem) {
2940 if (that.getNamespace(elem) === that.ns) {
2941 nsMatch = true;
2942 }
2943 });
2944 nsMatch = nsMatch || this.getNamespace(elem) === this.ns;
2945 }
2946 return nsMatch;
2947 },
2948
2949 /** PrivateFunction: isMatch
2950 * Tests if a stanza matches the Strophe.Handler.
2951 *
2952 * Parameters:
2953 * (XMLElement) elem - The XML element to test.
2954 *
2955 * Returns:
2956 * true if the stanza matches and false otherwise.
2957 */
2958 isMatch: function (elem) {
2959 var from = elem.getAttribute('from');
2960 if (this.options.matchBareFromJid) {
2961 from = Strophe.getBareJidFromJid(from);
2962 }
2963 var elem_type = elem.getAttribute("type");
2964 if (this.namespaceMatch(elem) &&
2965 (!this.name || Strophe.isTagEqual(elem, this.name)) &&
2966 (!this.type || (Array.isArray(this.type) ? this.type.indexOf(elem_type) !== -1 : elem_type === this.type)) &&
2967 (!this.id || elem.getAttribute("id") === this.id) &&
2968 (!this.from || from === this.from)) {
2969 return true;
2970 }
2971 return false;
2972 },
2973
2974 /** PrivateFunction: run
2975 * Run the callback on a matching stanza.
2976 *
2977 * Parameters:
2978 * (XMLElement) elem - The DOM element that triggered the
2979 * Strophe.Handler.
2980 *
2981 * Returns:
2982 * A boolean indicating if the handler should remain active.
2983 */
2984 run: function (elem) {
2985 var result = null;
2986 try {
2987 result = this.handler(elem);
2988 } catch (e) {
2989 Strophe._handleError(e);
2990 throw e;
2991 }
2992 return result;
2993 },
2994
2995 /** PrivateFunction: toString
2996 * Get a String representation of the Strophe.Handler object.
2997 *
2998 * Returns:
2999 * A String.
3000 */
3001 toString: function () {
3002 return "{Handler: " + this.handler + "(" + this.name + "," +
3003 this.id + "," + this.ns + ")}";
3004 }
3005};
3006
3007/** PrivateClass: Strophe.TimedHandler
3008 * _Private_ helper class for managing timed handlers.
3009 *
3010 * A Strophe.TimedHandler encapsulates a user provided callback that
3011 * should be called after a certain period of time or at regular
3012 * intervals. The return value of the callback determines whether the
3013 * Strophe.TimedHandler will continue to fire.
3014 *
3015 * Users will not use Strophe.TimedHandler objects directly, but instead
3016 * they will use Strophe.Connection.addTimedHandler() and
3017 * Strophe.Connection.deleteTimedHandler().
3018 */
3019
3020/** PrivateConstructor: Strophe.TimedHandler
3021 * Create and initialize a new Strophe.TimedHandler object.
3022 *
3023 * Parameters:
3024 * (Integer) period - The number of milliseconds to wait before the
3025 * handler is called.
3026 * (Function) handler - The callback to run when the handler fires. This
3027 * function should take no arguments.
3028 *
3029 * Returns:
3030 * A new Strophe.TimedHandler object.
3031 */
3032Strophe.TimedHandler = function (period, handler) {
3033 this.period = period;
3034 this.handler = handler;
3035 this.lastCalled = new Date().getTime();
3036 this.user = true;
3037};
3038
3039Strophe.TimedHandler.prototype = {
3040 /** PrivateFunction: run
3041 * Run the callback for the Strophe.TimedHandler.
3042 *
3043 * Returns:
3044 * true if the Strophe.TimedHandler should be called again, and false
3045 * otherwise.
3046 */
3047 run: function () {
3048 this.lastCalled = new Date().getTime();
3049 return this.handler();
3050 },
3051
3052 /** PrivateFunction: reset
3053 * Reset the last called time for the Strophe.TimedHandler.
3054 */
3055 reset: function () {
3056 this.lastCalled = new Date().getTime();
3057 },
3058
3059 /** PrivateFunction: toString
3060 * Get a string representation of the Strophe.TimedHandler object.
3061 *
3062 * Returns:
3063 * The string representation.
3064 */
3065 toString: function () {
3066 return "{TimedHandler: " + this.handler + "(" + this.period +")}";
3067 }
3068};
3069
3070/** Class: Strophe.Connection
3071 * XMPP Connection manager.
3072 *
3073 * This class is the main part of Strophe. It manages a BOSH or websocket
3074 * connection to an XMPP server and dispatches events to the user callbacks
3075 * as data arrives. It supports SASL PLAIN, SASL DIGEST-MD5, SASL SCRAM-SHA1
3076 * and legacy authentication.
3077 *
3078 * After creating a Strophe.Connection object, the user will typically
3079 * call connect() with a user supplied callback to handle connection level
3080 * events like authentication failure, disconnection, or connection
3081 * complete.
3082 *
3083 * The user will also have several event handlers defined by using
3084 * addHandler() and addTimedHandler(). These will allow the user code to
3085 * respond to interesting stanzas or do something periodically with the
3086 * connection. These handlers will be active once authentication is
3087 * finished.
3088 *
3089 * To send data to the connection, use send().
3090 */
3091
3092/** Constructor: Strophe.Connection
3093 * Create and initialize a Strophe.Connection object.
3094 *
3095 * The transport-protocol for this connection will be chosen automatically
3096 * based on the given service parameter. URLs starting with "ws://" or
3097 * "wss://" will use WebSockets, URLs starting with "http://", "https://"
3098 * or without a protocol will use BOSH.
3099 *
3100 * To make Strophe connect to the current host you can leave out the protocol
3101 * and host part and just pass the path, e.g.
3102 *
3103 * > var conn = new Strophe.Connection("/http-bind/");
3104 *
3105 * Options common to both Websocket and BOSH:
3106 * ------------------------------------------
3107 *
3108 * cookies:
3109 *
3110 * The *cookies* option allows you to pass in cookies to be added to the
3111 * document. These cookies will then be included in the BOSH XMLHttpRequest
3112 * or in the websocket connection.
3113 *
3114 * The passed in value must be a map of cookie names and string values.
3115 *
3116 * > { "myCookie": {
3117 * > "value": "1234",
3118 * > "domain": ".example.org",
3119 * > "path": "/",
3120 * > "expires": expirationDate
3121 * > }
3122 * > }
3123 *
3124 * Note that cookies can't be set in this way for other domains (i.e. cross-domain).
3125 * Those cookies need to be set under those domains, for example they can be
3126 * set server-side by making a XHR call to that domain to ask it to set any
3127 * necessary cookies.
3128 *
3129 * mechanisms:
3130 *
3131 * The *mechanisms* option allows you to specify the SASL mechanisms that this
3132 * instance of Strophe.Connection (and therefore your XMPP client) will
3133 * support.
3134 *
3135 * The value must be an array of objects with Strophe.SASLMechanism
3136 * prototypes.
3137 *
3138 * If nothing is specified, then the following mechanisms (and their
3139 * priorities) are registered:
3140 *
3141 * SCRAM-SHA1 - 70
3142 * DIGEST-MD5 - 60
3143 * PLAIN - 50
3144 * OAUTH-BEARER - 40
3145 * OAUTH-2 - 30
3146 * ANONYMOUS - 20
3147 * EXTERNAL - 10
3148 *
3149 * WebSocket options:
3150 * ------------------
3151 *
3152 * If you want to connect to the current host with a WebSocket connection you
3153 * can tell Strophe to use WebSockets through a "protocol" attribute in the
3154 * optional options parameter. Valid values are "ws" for WebSocket and "wss"
3155 * for Secure WebSocket.
3156 * So to connect to "wss://CURRENT_HOSTNAME/xmpp-websocket" you would call
3157 *
3158 * > var conn = new Strophe.Connection("/xmpp-websocket/", {protocol: "wss"});
3159 *
3160 * Note that relative URLs _NOT_ starting with a "/" will also include the path
3161 * of the current site.
3162 *
3163 * Also because downgrading security is not permitted by browsers, when using
3164 * relative URLs both BOSH and WebSocket connections will use their secure
3165 * variants if the current connection to the site is also secure (https).
3166 *
3167 * BOSH options:
3168 * -------------
3169 *
3170 * By adding "sync" to the options, you can control if requests will
3171 * be made synchronously or not. The default behaviour is asynchronous.
3172 * If you want to make requests synchronous, make "sync" evaluate to true.
3173 * > var conn = new Strophe.Connection("/http-bind/", {sync: true});
3174 *
3175 * You can also toggle this on an already established connection.
3176 * > conn.options.sync = true;
3177 *
3178 * The *customHeaders* option can be used to provide custom HTTP headers to be
3179 * included in the XMLHttpRequests made.
3180 *
3181 * The *keepalive* option can be used to instruct Strophe to maintain the
3182 * current BOSH session across interruptions such as webpage reloads.
3183 *
3184 * It will do this by caching the sessions tokens in sessionStorage, and when
3185 * "restore" is called it will check whether there are cached tokens with
3186 * which it can resume an existing session.
3187 *
3188 * The *withCredentials* option should receive a Boolean value and is used to
3189 * indicate wether cookies should be included in ajax requests (by default
3190 * they're not).
3191 * Set this value to true if you are connecting to a BOSH service
3192 * and for some reason need to send cookies to it.
3193 * In order for this to work cross-domain, the server must also enable
3194 * credentials by setting the Access-Control-Allow-Credentials response header
3195 * to "true". For most usecases however this setting should be false (which
3196 * is the default).
3197 * Additionally, when using Access-Control-Allow-Credentials, the
3198 * Access-Control-Allow-Origin header can't be set to the wildcard "*", but
3199 * instead must be restricted to actual domains.
3200 *
3201 * The *contentType* option can be set to change the default Content-Type
3202 * of "text/xml; charset=utf-8", which can be useful to reduce the amount of
3203 * CORS preflight requests that are sent to the server.
3204 *
3205 * Parameters:
3206 * (String) service - The BOSH or WebSocket service URL.
3207 * (Object) options - A hash of configuration options
3208 *
3209 * Returns:
3210 * A new Strophe.Connection object.
3211 */
3212Strophe.Connection = function (service, options) {
3213 // The service URL
3214 this.service = service;
3215 // Configuration options
3216 this.options = options || {};
3217 var proto = this.options.protocol || "";
3218
3219 // Select protocal based on service or options
3220 if (service.indexOf("ws:") === 0 || service.indexOf("wss:") === 0 ||
3221 proto.indexOf("ws") === 0) {
3222 this._proto = new Strophe.Websocket(this);
3223 } else {
3224 this._proto = new Strophe.Bosh(this);
3225 }
3226
3227 /* The connected JID. */
3228 this.jid = "";
3229 /* the JIDs domain */
3230 this.domain = null;
3231 /* stream:features */
3232 this.features = null;
3233
3234 // SASL
3235 this._sasl_data = {};
3236 this.do_session = false;
3237 this.do_bind = false;
3238
3239 // handler lists
3240 this.timedHandlers = [];
3241 this.handlers = [];
3242 this.removeTimeds = [];
3243 this.removeHandlers = [];
3244 this.addTimeds = [];
3245 this.addHandlers = [];
3246 this.protocolErrorHandlers = {
3247 'HTTP': {},
3248 'websocket': {}
3249 };
3250
3251 this._idleTimeout = null;
3252 this._disconnectTimeout = null;
3253
3254 this.authenticated = false;
3255 this.connected = false;
3256 this.disconnecting = false;
3257 this.do_authentication = true;
3258 this.paused = false;
3259 this.restored = false;
3260
3261 this._data = [];
3262 this._uniqueId = 0;
3263
3264 this._sasl_success_handler = null;
3265 this._sasl_failure_handler = null;
3266 this._sasl_challenge_handler = null;
3267
3268 // Max retries before disconnecting
3269 this.maxRetries = 5;
3270
3271 // Call onIdle callback every 1/10th of a second
3272 // XXX: setTimeout should be called only with function expressions (23974bc1)
3273 this._idleTimeout = setTimeout(function() {
3274 this._onIdle();
3275 }.bind(this), 100);
3276
3277 utils.addCookies(this.options.cookies);
3278 this.registerSASLMechanisms(this.options.mechanisms);
3279
3280 // initialize plugins
3281 for (var k in Strophe._connectionPlugins) {
3282 if (Strophe._connectionPlugins.hasOwnProperty(k)) {
3283 var ptype = Strophe._connectionPlugins[k];
3284 // jslint complaints about the below line, but this is fine
3285 var F = function () {}; // jshint ignore:line
3286 F.prototype = ptype;
3287 this[k] = new F();
3288 this[k].init(this);
3289 }
3290 }
3291};
3292
3293Strophe.Connection.prototype = {
3294 /** Function: reset
3295 * Reset the connection.
3296 *
3297 * This function should be called after a connection is disconnected
3298 * before that connection is reused.
3299 */
3300 reset: function () {
3301 this._proto._reset();
3302
3303 // SASL
3304 this.do_session = false;
3305 this.do_bind = false;
3306
3307 // handler lists
3308 this.timedHandlers = [];
3309 this.handlers = [];
3310 this.removeTimeds = [];
3311 this.removeHandlers = [];
3312 this.addTimeds = [];
3313 this.addHandlers = [];
3314
3315 this.authenticated = false;
3316 this.connected = false;
3317 this.disconnecting = false;
3318 this.restored = false;
3319
3320 this._data = [];
3321 this._requests = [];
3322 this._uniqueId = 0;
3323 },
3324
3325 /** Function: pause
3326 * Pause the request manager.
3327 *
3328 * This will prevent Strophe from sending any more requests to the
3329 * server. This is very useful for temporarily pausing
3330 * BOSH-Connections while a lot of send() calls are happening quickly.
3331 * This causes Strophe to send the data in a single request, saving
3332 * many request trips.
3333 */
3334 pause: function () {
3335 this.paused = true;
3336 },
3337
3338 /** Function: resume
3339 * Resume the request manager.
3340 *
3341 * This resumes after pause() has been called.
3342 */
3343 resume: function () {
3344 this.paused = false;
3345 },
3346
3347 /** Function: getUniqueId
3348 * Generate a unique ID for use in <iq/> elements.
3349 *
3350 * All <iq/> stanzas are required to have unique id attributes. This
3351 * function makes creating these easy. Each connection instance has
3352 * a counter which starts from zero, and the value of this counter
3353 * plus a colon followed by the suffix becomes the unique id. If no
3354 * suffix is supplied, the counter is used as the unique id.
3355 *
3356 * Suffixes are used to make debugging easier when reading the stream
3357 * data, and their use is recommended. The counter resets to 0 for
3358 * every new connection for the same reason. For connections to the
3359 * same server that authenticate the same way, all the ids should be
3360 * the same, which makes it easy to see changes. This is useful for
3361 * automated testing as well.
3362 *
3363 * Parameters:
3364 * (String) suffix - A optional suffix to append to the id.
3365 *
3366 * Returns:
3367 * A unique string to be used for the id attribute.
3368 */
3369 getUniqueId: function(suffix) {
3370 var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
3371 var r = Math.random() * 16 | 0,
3372 v = c === 'x' ? r : r & 0x3 | 0x8;
3373 return v.toString(16);
3374 });
3375 if (typeof(suffix) === "string" || typeof(suffix) === "number") {
3376 return uuid + ":" + suffix;
3377 } else {
3378 return uuid + "";
3379 }
3380 },
3381
3382 /** Function: addProtocolErrorHandler
3383 * Register a handler function for when a protocol (websocker or HTTP)
3384 * error occurs.
3385 *
3386 * NOTE: Currently only HTTP errors for BOSH requests are handled.
3387 * Patches that handle websocket errors would be very welcome.
3388 *
3389 * Parameters:
3390 * (String) protocol - 'HTTP' or 'websocket'
3391 * (Integer) status_code - Error status code (e.g 500, 400 or 404)
3392 * (Function) callback - Function that will fire on Http error
3393 *
3394 * Example:
3395 * function onError(err_code){
3396 * //do stuff
3397 * }
3398 *
3399 * var conn = Strophe.connect('http://example.com/http-bind');
3400 * conn.addProtocolErrorHandler('HTTP', 500, onError);
3401 * // Triggers HTTP 500 error and onError handler will be called
3402 * conn.connect('user_jid@incorrect_jabber_host', 'secret', onConnect);
3403 */
3404 addProtocolErrorHandler: function(protocol, status_code, callback){
3405 this.protocolErrorHandlers[protocol][status_code] = callback;
3406 },
3407
3408
3409 /** Function: connect
3410 * Starts the connection process.
3411 *
3412 * As the connection process proceeds, the user supplied callback will
3413 * be triggered multiple times with status updates. The callback
3414 * should take two arguments - the status code and the error condition.
3415 *
3416 * The status code will be one of the values in the Strophe.Status
3417 * constants. The error condition will be one of the conditions
3418 * defined in RFC 3920 or the condition 'strophe-parsererror'.
3419 *
3420 * The Parameters _wait_, _hold_ and _route_ are optional and only relevant
3421 * for BOSH connections. Please see XEP 124 for a more detailed explanation
3422 * of the optional parameters.
3423 *
3424 * Parameters:
3425 * (String) jid - The user's JID. This may be a bare JID,
3426 * or a full JID. If a node is not supplied, SASL OAUTHBEARER or
3427 * SASL ANONYMOUS authentication will be attempted (OAUTHBEARER will
3428 * process the provided password value as an access token).
3429 * (String) pass - The user's password.
3430 * (Function) callback - The connect callback function.
3431 * (Integer) wait - The optional HTTPBIND wait value. This is the
3432 * time the server will wait before returning an empty result for
3433 * a request. The default setting of 60 seconds is recommended.
3434 * (Integer) hold - The optional HTTPBIND hold value. This is the
3435 * number of connections the server will hold at one time. This
3436 * should almost always be set to 1 (the default).
3437 * (String) route - The optional route value.
3438 * (String) authcid - The optional alternative authentication identity
3439 * (username) if intending to impersonate another user.
3440 * When using the SASL-EXTERNAL authentication mechanism, for example
3441 * with client certificates, then the authcid value is used to
3442 * determine whether an authorization JID (authzid) should be sent to
3443 * the server. The authzid should not be sent to the server if the
3444 * authzid and authcid are the same. So to prevent it from being sent
3445 * (for example when the JID is already contained in the client
3446 * certificate), set authcid to that same JID. See XEP-178 for more
3447 * details.
3448 */
3449 connect: function (jid, pass, callback, wait, hold, route, authcid) {
3450 this.jid = jid;
3451 /** Variable: authzid
3452 * Authorization identity.
3453 */
3454 this.authzid = Strophe.getBareJidFromJid(this.jid);
3455
3456 /** Variable: authcid
3457 * Authentication identity (User name).
3458 */
3459 this.authcid = authcid || Strophe.getNodeFromJid(this.jid);
3460
3461 /** Variable: pass
3462 * Authentication identity (User password).
3463 */
3464 this.pass = pass;
3465
3466 /** Variable: servtype
3467 * Digest MD5 compatibility.
3468 */
3469 this.servtype = "xmpp";
3470
3471 this.connect_callback = callback;
3472 this.disconnecting = false;
3473 this.connected = false;
3474 this.authenticated = false;
3475 this.restored = false;
3476
3477 // parse jid for domain
3478 this.domain = Strophe.getDomainFromJid(this.jid);
3479
3480 this._changeConnectStatus(Strophe.Status.CONNECTING, null);
3481
3482 this._proto._connect(wait, hold, route);
3483 },
3484
3485 /** Function: attach
3486 * Attach to an already created and authenticated BOSH session.
3487 *
3488 * This function is provided to allow Strophe to attach to BOSH
3489 * sessions which have been created externally, perhaps by a Web
3490 * application. This is often used to support auto-login type features
3491 * without putting user credentials into the page.
3492 *
3493 * Parameters:
3494 * (String) jid - The full JID that is bound by the session.
3495 * (String) sid - The SID of the BOSH session.
3496 * (String) rid - The current RID of the BOSH session. This RID
3497 * will be used by the next request.
3498 * (Function) callback The connect callback function.
3499 * (Integer) wait - The optional HTTPBIND wait value. This is the
3500 * time the server will wait before returning an empty result for
3501 * a request. The default setting of 60 seconds is recommended.
3502 * Other settings will require tweaks to the Strophe.TIMEOUT value.
3503 * (Integer) hold - The optional HTTPBIND hold value. This is the
3504 * number of connections the server will hold at one time. This
3505 * should almost always be set to 1 (the default).
3506 * (Integer) wind - The optional HTTBIND window value. This is the
3507 * allowed range of request ids that are valid. The default is 5.
3508 */
3509 attach: function (jid, sid, rid, callback, wait, hold, wind) {
3510 if (this._proto instanceof Strophe.Bosh) {
3511 this._proto._attach(jid, sid, rid, callback, wait, hold, wind);
3512 } else {
3513 throw {
3514 name: 'StropheSessionError',
3515 message: 'The "attach" method can only be used with a BOSH connection.'
3516 };
3517 }
3518 },
3519
3520 /** Function: restore
3521 * Attempt to restore a cached BOSH session.
3522 *
3523 * This function is only useful in conjunction with providing the
3524 * "keepalive":true option when instantiating a new Strophe.Connection.
3525 *
3526 * When "keepalive" is set to true, Strophe will cache the BOSH tokens
3527 * RID (Request ID) and SID (Session ID) and then when this function is
3528 * called, it will attempt to restore the session from those cached
3529 * tokens.
3530 *
3531 * This function must therefore be called instead of connect or attach.
3532 *
3533 * For an example on how to use it, please see examples/restore.js
3534 *
3535 * Parameters:
3536 * (String) jid - The user's JID. This may be a bare JID or a full JID.
3537 * (Function) callback - The connect callback function.
3538 * (Integer) wait - The optional HTTPBIND wait value. This is the
3539 * time the server will wait before returning an empty result for
3540 * a request. The default setting of 60 seconds is recommended.
3541 * (Integer) hold - The optional HTTPBIND hold value. This is the
3542 * number of connections the server will hold at one time. This
3543 * should almost always be set to 1 (the default).
3544 * (Integer) wind - The optional HTTBIND window value. This is the
3545 * allowed range of request ids that are valid. The default is 5.
3546 */
3547 restore: function (jid, callback, wait, hold, wind) {
3548 if (this._sessionCachingSupported()) {
3549 this._proto._restore(jid, callback, wait, hold, wind);
3550 } else {
3551 throw {
3552 name: 'StropheSessionError',
3553 message: 'The "restore" method can only be used with a BOSH connection.'
3554 };
3555 }
3556 },
3557
3558 /** PrivateFunction: _sessionCachingSupported
3559 * Checks whether sessionStorage and JSON are supported and whether we're
3560 * using BOSH.
3561 */
3562 _sessionCachingSupported: function () {
3563 if (this._proto instanceof Strophe.Bosh) {
3564 if (!JSON) { return false; }
3565 try {
3566 sessionStorage.setItem('_strophe_', '_strophe_');
3567 sessionStorage.removeItem('_strophe_');
3568 } catch (e) {
3569 return false;
3570 }
3571 return true;
3572 }
3573 return false;
3574 },
3575
3576 /** Function: xmlInput
3577 * User overrideable function that receives XML data coming into the
3578 * connection.
3579 *
3580 * The default function does nothing. User code can override this with
3581 * > Strophe.Connection.xmlInput = function (elem) {
3582 * > (user code)
3583 * > };
3584 *
3585 * Due to limitations of current Browsers' XML-Parsers the opening and closing
3586 * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.
3587 *
3588 * BOSH-Connections will have all stanzas wrapped in a <body> tag. See
3589 * <Strophe.Bosh.strip> if you want to strip this tag.
3590 *
3591 * Parameters:
3592 * (XMLElement) elem - The XML data received by the connection.
3593 */
3594 /* jshint unused:false */
3595 xmlInput: function (elem) {
3596 return;
3597 },
3598 /* jshint unused:true */
3599
3600 /** Function: xmlOutput
3601 * User overrideable function that receives XML data sent to the
3602 * connection.
3603 *
3604 * The default function does nothing. User code can override this with
3605 * > Strophe.Connection.xmlOutput = function (elem) {
3606 * > (user code)
3607 * > };
3608 *
3609 * Due to limitations of current Browsers' XML-Parsers the opening and closing
3610 * <stream> tag for WebSocket-Connoctions will be passed as selfclosing here.
3611 *
3612 * BOSH-Connections will have all stanzas wrapped in a <body> tag. See
3613 * <Strophe.Bosh.strip> if you want to strip this tag.
3614 *
3615 * Parameters:
3616 * (XMLElement) elem - The XMLdata sent by the connection.
3617 */
3618 /* jshint unused:false */
3619 xmlOutput: function (elem) {
3620 return;
3621 },
3622 /* jshint unused:true */
3623
3624 /** Function: rawInput
3625 * User overrideable function that receives raw data coming into the
3626 * connection.
3627 *
3628 * The default function does nothing. User code can override this with
3629 * > Strophe.Connection.rawInput = function (data) {
3630 * > (user code)
3631 * > };
3632 *
3633 * Parameters:
3634 * (String) data - The data received by the connection.
3635 */
3636 /* jshint unused:false */
3637 rawInput: function (data) {
3638 return;
3639 },
3640 /* jshint unused:true */
3641
3642 /** Function: rawOutput
3643 * User overrideable function that receives raw data sent to the
3644 * connection.
3645 *
3646 * The default function does nothing. User code can override this with
3647 * > Strophe.Connection.rawOutput = function (data) {
3648 * > (user code)
3649 * > };
3650 *
3651 * Parameters:
3652 * (String) data - The data sent by the connection.
3653 */
3654 /* jshint unused:false */
3655 rawOutput: function (data) {
3656 return;
3657 },
3658 /* jshint unused:true */
3659
3660 /** Function: nextValidRid
3661 * User overrideable function that receives the new valid rid.
3662 *
3663 * The default function does nothing. User code can override this with
3664 * > Strophe.Connection.nextValidRid = function (rid) {
3665 * > (user code)
3666 * > };
3667 *
3668 * Parameters:
3669 * (Number) rid - The next valid rid
3670 */
3671 /* jshint unused:false */
3672 nextValidRid: function (rid) {
3673 return;
3674 },
3675 /* jshint unused:true */
3676
3677 /** Function: send
3678 * Send a stanza.
3679 *
3680 * This function is called to push data onto the send queue to
3681 * go out over the wire. Whenever a request is sent to the BOSH
3682 * server, all pending data is sent and the queue is flushed.
3683 *
3684 * Parameters:
3685 * (XMLElement |
3686 * [XMLElement] |
3687 * Strophe.Builder) elem - The stanza to send.
3688 */
3689 send: function (elem) {
3690 if (elem === null) { return ; }
3691 if (typeof(elem.sort) === "function") {
3692 for (var i = 0; i < elem.length; i++) {
3693 this._queueData(elem[i]);
3694 }
3695 } else if (typeof(elem.tree) === "function") {
3696 this._queueData(elem.tree());
3697 } else {
3698 this._queueData(elem);
3699 }
3700
3701 this._proto._send();
3702 },
3703
3704 /** Function: flush
3705 * Immediately send any pending outgoing data.
3706 *
3707 * Normally send() queues outgoing data until the next idle period
3708 * (100ms), which optimizes network use in the common cases when
3709 * several send()s are called in succession. flush() can be used to
3710 * immediately send all pending data.
3711 */
3712 flush: function () {
3713 // cancel the pending idle period and run the idle function
3714 // immediately
3715 clearTimeout(this._idleTimeout);
3716 this._onIdle();
3717 },
3718
3719 /** Function: sendPresence
3720 * Helper function to send presence stanzas. The main benefit is for
3721 * sending presence stanzas for which you expect a responding presence
3722 * stanza with the same id (for example when leaving a chat room).
3723 *
3724 * Parameters:
3725 * (XMLElement) elem - The stanza to send.
3726 * (Function) callback - The callback function for a successful request.
3727 * (Function) errback - The callback function for a failed or timed
3728 * out request. On timeout, the stanza will be null.
3729 * (Integer) timeout - The time specified in milliseconds for a
3730 * timeout to occur.
3731 *
3732 * Returns:
3733 * The id used to send the presence.
3734 */
3735 sendPresence: function(elem, callback, errback, timeout) {
3736 var timeoutHandler = null;
3737 var that = this;
3738 if (typeof(elem.tree) === "function") {
3739 elem = elem.tree();
3740 }
3741 var id = elem.getAttribute('id');
3742 if (!id) { // inject id if not found
3743 id = this.getUniqueId("sendPresence");
3744 elem.setAttribute("id", id);
3745 }
3746
3747 if (typeof callback === "function" || typeof errback === "function") {
3748 var handler = this.addHandler(function (stanza) {
3749 // remove timeout handler if there is one
3750 if (timeoutHandler) {
3751 that.deleteTimedHandler(timeoutHandler);
3752 }
3753 var type = stanza.getAttribute('type');
3754 if (type === 'error') {
3755 if (errback) {
3756 errback(stanza);
3757 }
3758 } else if (callback) {
3759 callback(stanza);
3760 }
3761 }, null, 'presence', null, id);
3762
3763 // if timeout specified, set up a timeout handler.
3764 if (timeout) {
3765 timeoutHandler = this.addTimedHandler(timeout, function () {
3766 // get rid of normal handler
3767 that.deleteHandler(handler);
3768 // call errback on timeout with null stanza
3769 if (errback) {
3770 errback(null);
3771 }
3772 return false;
3773 });
3774 }
3775 }
3776 this.send(elem);
3777 return id;
3778 },
3779
3780 /** Function: sendIQ
3781 * Helper function to send IQ stanzas.
3782 *
3783 * Parameters:
3784 * (XMLElement) elem - The stanza to send.
3785 * (Function) callback - The callback function for a successful request.
3786 * (Function) errback - The callback function for a failed or timed
3787 * out request. On timeout, the stanza will be null.
3788 * (Integer) timeout - The time specified in milliseconds for a
3789 * timeout to occur.
3790 *
3791 * Returns:
3792 * The id used to send the IQ.
3793 */
3794 sendIQ: function(elem, callback, errback, timeout) {
3795 var timeoutHandler = null;
3796 var that = this;
3797 if (typeof(elem.tree) === "function") {
3798 elem = elem.tree();
3799 }
3800 var id = elem.getAttribute('id');
3801 if (!id) { // inject id if not found
3802 id = this.getUniqueId("sendIQ");
3803 elem.setAttribute("id", id);
3804 }
3805
3806 if (typeof callback === "function" || typeof errback === "function") {
3807 var handler = this.addHandler(function (stanza) {
3808 // remove timeout handler if there is one
3809 if (timeoutHandler) {
3810 that.deleteTimedHandler(timeoutHandler);
3811 }
3812 var iqtype = stanza.getAttribute('type');
3813 if (iqtype === 'result') {
3814 if (callback) {
3815 callback(stanza);
3816 }
3817 } else if (iqtype === 'error') {
3818 if (errback) {
3819 errback(stanza);
3820 }
3821 } else {
3822 throw {
3823 name: "StropheError",
3824 message: "Got bad IQ type of " + iqtype
3825 };
3826 }
3827 }, null, 'iq', ['error', 'result'], id);
3828
3829 // if timeout specified, set up a timeout handler.
3830 if (timeout) {
3831 timeoutHandler = this.addTimedHandler(timeout, function () {
3832 // get rid of normal handler
3833 that.deleteHandler(handler);
3834 // call errback on timeout with null stanza
3835 if (errback) {
3836 errback(null);
3837 }
3838 return false;
3839 });
3840 }
3841 }
3842 this.send(elem);
3843 return id;
3844 },
3845
3846 /** PrivateFunction: _queueData
3847 * Queue outgoing data for later sending. Also ensures that the data
3848 * is a DOMElement.
3849 */
3850 _queueData: function (element) {
3851 if (element === null ||
3852 !element.tagName ||
3853 !element.childNodes) {
3854 throw {
3855 name: "StropheError",
3856 message: "Cannot queue non-DOMElement."
3857 };
3858 }
3859 this._data.push(element);
3860 },
3861
3862 /** PrivateFunction: _sendRestart
3863 * Send an xmpp:restart stanza.
3864 */
3865 _sendRestart: function () {
3866 this._data.push("restart");
3867 this._proto._sendRestart();
3868 // XXX: setTimeout should be called only with function expressions (23974bc1)
3869 this._idleTimeout = setTimeout(function() {
3870 this._onIdle();
3871 }.bind(this), 100);
3872 },
3873
3874 /** Function: addTimedHandler
3875 * Add a timed handler to the connection.
3876 *
3877 * This function adds a timed handler. The provided handler will
3878 * be called every period milliseconds until it returns false,
3879 * the connection is terminated, or the handler is removed. Handlers
3880 * that wish to continue being invoked should return true.
3881 *
3882 * Because of method binding it is necessary to save the result of
3883 * this function if you wish to remove a handler with
3884 * deleteTimedHandler().
3885 *
3886 * Note that user handlers are not active until authentication is
3887 * successful.
3888 *
3889 * Parameters:
3890 * (Integer) period - The period of the handler.
3891 * (Function) handler - The callback function.
3892 *
3893 * Returns:
3894 * A reference to the handler that can be used to remove it.
3895 */
3896 addTimedHandler: function (period, handler) {
3897 var thand = new Strophe.TimedHandler(period, handler);
3898 this.addTimeds.push(thand);
3899 return thand;
3900 },
3901
3902 /** Function: deleteTimedHandler
3903 * Delete a timed handler for a connection.
3904 *
3905 * This function removes a timed handler from the connection. The
3906 * handRef parameter is *not* the function passed to addTimedHandler(),
3907 * but is the reference returned from addTimedHandler().
3908 *
3909 * Parameters:
3910 * (Strophe.TimedHandler) handRef - The handler reference.
3911 */
3912 deleteTimedHandler: function (handRef) {
3913 // this must be done in the Idle loop so that we don't change
3914 // the handlers during iteration
3915 this.removeTimeds.push(handRef);
3916 },
3917
3918 /** Function: addHandler
3919 * Add a stanza handler for the connection.
3920 *
3921 * This function adds a stanza handler to the connection. The
3922 * handler callback will be called for any stanza that matches
3923 * the parameters. Note that if multiple parameters are supplied,
3924 * they must all match for the handler to be invoked.
3925 *
3926 * The handler will receive the stanza that triggered it as its argument.
3927 * *The handler should return true if it is to be invoked again;
3928 * returning false will remove the handler after it returns.*
3929 *
3930 * As a convenience, the ns parameters applies to the top level element
3931 * and also any of its immediate children. This is primarily to make
3932 * matching /iq/query elements easy.
3933 *
3934 * Options
3935 * ~~~~~~~
3936 * With the options argument, you can specify boolean flags that affect how
3937 * matches are being done.
3938 *
3939 * Currently two flags exist:
3940 *
3941 * - matchBareFromJid:
3942 * When set to true, the from parameter and the
3943 * from attribute on the stanza will be matched as bare JIDs instead
3944 * of full JIDs. To use this, pass {matchBareFromJid: true} as the
3945 * value of options. The default value for matchBareFromJid is false.
3946 *
3947 * - ignoreNamespaceFragment:
3948 * When set to true, a fragment specified on the stanza's namespace
3949 * URL will be ignored when it's matched with the one configured for
3950 * the handler.
3951 *
3952 * This means that if you register like this:
3953 * > connection.addHandler(
3954 * > handler,
3955 * > 'http://jabber.org/protocol/muc',
3956 * > null, null, null, null,
3957 * > {'ignoreNamespaceFragment': true}
3958 * > );
3959 *
3960 * Then a stanza with XML namespace of
3961 * 'http://jabber.org/protocol/muc#user' will also be matched. If
3962 * 'ignoreNamespaceFragment' is false, then only stanzas with
3963 * 'http://jabber.org/protocol/muc' will be matched.
3964 *
3965 * Deleting the handler
3966 * ~~~~~~~~~~~~~~~~~~~~
3967 * The return value should be saved if you wish to remove the handler
3968 * with deleteHandler().
3969 *
3970 * Parameters:
3971 * (Function) handler - The user callback.
3972 * (String) ns - The namespace to match.
3973 * (String) name - The stanza name to match.
3974 * (String|Array) type - The stanza type (or types if an array) to match.
3975 * (String) id - The stanza id attribute to match.
3976 * (String) from - The stanza from attribute to match.
3977 * (String) options - The handler options
3978 *
3979 * Returns:
3980 * A reference to the handler that can be used to remove it.
3981 */
3982 addHandler: function (handler, ns, name, type, id, from, options) {
3983 var hand = new Strophe.Handler(handler, ns, name, type, id, from, options);
3984 this.addHandlers.push(hand);
3985 return hand;
3986 },
3987
3988 /** Function: deleteHandler
3989 * Delete a stanza handler for a connection.
3990 *
3991 * This function removes a stanza handler from the connection. The
3992 * handRef parameter is *not* the function passed to addHandler(),
3993 * but is the reference returned from addHandler().
3994 *
3995 * Parameters:
3996 * (Strophe.Handler) handRef - The handler reference.
3997 */
3998 deleteHandler: function (handRef) {
3999 // this must be done in the Idle loop so that we don't change
4000 // the handlers during iteration
4001 this.removeHandlers.push(handRef);
4002 // If a handler is being deleted while it is being added,
4003 // prevent it from getting added
4004 var i = this.addHandlers.indexOf(handRef);
4005 if (i >= 0) {
4006 this.addHandlers.splice(i, 1);
4007 }
4008 },
4009
4010 /** Function: registerSASLMechanisms
4011 *
4012 * Register the SASL mechanisms which will be supported by this instance of
4013 * Strophe.Connection (i.e. which this XMPP client will support).
4014 *
4015 * Parameters:
4016 * (Array) mechanisms - Array of objects with Strophe.SASLMechanism prototypes
4017 *
4018 */
4019 registerSASLMechanisms: function (mechanisms) {
4020 this.mechanisms = {};
4021 mechanisms = mechanisms || [
4022 Strophe.SASLAnonymous,
4023 Strophe.SASLExternal,
4024 Strophe.SASLMD5,
4025 Strophe.SASLOAuthBearer,
4026 Strophe.SASLXOAuth2,
4027 Strophe.SASLPlain,
4028 Strophe.SASLSHA1
4029 ];
4030 mechanisms.forEach(this.registerSASLMechanism.bind(this));
4031 },
4032
4033 /** Function: registerSASLMechanism
4034 *
4035 * Register a single SASL mechanism, to be supported by this client.
4036 *
4037 * Parameters:
4038 * (Object) mechanism - Object with a Strophe.SASLMechanism prototype
4039 *
4040 */
4041 registerSASLMechanism: function (mechanism) {
4042 this.mechanisms[mechanism.prototype.name] = mechanism;
4043 },
4044
4045 /** Function: disconnect
4046 * Start the graceful disconnection process.
4047 *
4048 * This function starts the disconnection process. This process starts
4049 * by sending unavailable presence and sending BOSH body of type
4050 * terminate. A timeout handler makes sure that disconnection happens
4051 * even if the BOSH server does not respond.
4052 * If the Connection object isn't connected, at least tries to abort all pending requests
4053 * so the connection object won't generate successful requests (which were already opened).
4054 *
4055 * The user supplied connection callback will be notified of the
4056 * progress as this process happens.
4057 *
4058 * Parameters:
4059 * (String) reason - The reason the disconnect is occuring.
4060 */
4061 disconnect: function (reason) {
4062 this._changeConnectStatus(Strophe.Status.DISCONNECTING, reason);
4063
4064 Strophe.info("Disconnect was called because: " + reason);
4065 if (this.connected) {
4066 var pres = false;
4067 this.disconnecting = true;
4068 if (this.authenticated) {
4069 pres = $pres({
4070 xmlns: Strophe.NS.CLIENT,
4071 type: 'unavailable'
4072 });
4073 }
4074 // setup timeout handler
4075 this._disconnectTimeout = this._addSysTimedHandler(
4076 3000, this._onDisconnectTimeout.bind(this));
4077 this._proto._disconnect(pres);
4078 } else {
4079 Strophe.info("Disconnect was called before Strophe connected to the server");
4080 this._proto._abortAllRequests();
4081 this._doDisconnect();
4082 }
4083 },
4084
4085 /** PrivateFunction: _changeConnectStatus
4086 * _Private_ helper function that makes sure plugins and the user's
4087 * callback are notified of connection status changes.
4088 *
4089 * Parameters:
4090 * (Integer) status - the new connection status, one of the values
4091 * in Strophe.Status
4092 * (String) condition - the error condition or null
4093 * (XMLElement) elem - The triggering stanza.
4094 */
4095 _changeConnectStatus: function (status, condition, elem) {
4096 // notify all plugins listening for status changes
4097 for (var k in Strophe._connectionPlugins) {
4098 if (Strophe._connectionPlugins.hasOwnProperty(k)) {
4099 var plugin = this[k];
4100 if (plugin.statusChanged) {
4101 try {
4102 plugin.statusChanged(status, condition);
4103 } catch (err) {
4104 Strophe.error("" + k + " plugin caused an exception " +
4105 "changing status: " + err);
4106 }
4107 }
4108 }
4109 }
4110
4111 // notify the user's callback
4112 if (this.connect_callback) {
4113 try {
4114 this.connect_callback(status, condition, elem);
4115 } catch (e) {
4116 Strophe._handleError(e);
4117 Strophe.error(
4118 "User connection callback caused an "+"exception: "+e);
4119 }
4120 }
4121 },
4122
4123 /** PrivateFunction: _doDisconnect
4124 * _Private_ function to disconnect.
4125 *
4126 * This is the last piece of the disconnection logic. This resets the
4127 * connection and alerts the user's connection callback.
4128 */
4129 _doDisconnect: function (condition) {
4130 if (typeof this._idleTimeout === "number") {
4131 clearTimeout(this._idleTimeout);
4132 }
4133
4134 // Cancel Disconnect Timeout
4135 if (this._disconnectTimeout !== null) {
4136 this.deleteTimedHandler(this._disconnectTimeout);
4137 this._disconnectTimeout = null;
4138 }
4139
4140 Strophe.info("_doDisconnect was called");
4141 this._proto._doDisconnect();
4142
4143 this.authenticated = false;
4144 this.disconnecting = false;
4145 this.restored = false;
4146
4147 // delete handlers
4148 this.handlers = [];
4149 this.timedHandlers = [];
4150 this.removeTimeds = [];
4151 this.removeHandlers = [];
4152 this.addTimeds = [];
4153 this.addHandlers = [];
4154
4155 // tell the parent we disconnected
4156 this._changeConnectStatus(Strophe.Status.DISCONNECTED, condition);
4157 this.connected = false;
4158 },
4159
4160 /** PrivateFunction: _dataRecv
4161 * _Private_ handler to processes incoming data from the the connection.
4162 *
4163 * Except for _connect_cb handling the initial connection request,
4164 * this function handles the incoming data for all requests. This
4165 * function also fires stanza handlers that match each incoming
4166 * stanza.
4167 *
4168 * Parameters:
4169 * (Strophe.Request) req - The request that has data ready.
4170 * (string) req - The stanza a raw string (optiona).
4171 */
4172 _dataRecv: function (req, raw) {
4173 Strophe.info("_dataRecv called");
4174 var elem = this._proto._reqToData(req);
4175 if (elem === null) { return; }
4176
4177 if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {
4178 if (elem.nodeName === this._proto.strip && elem.childNodes.length) {
4179 this.xmlInput(elem.childNodes[0]);
4180 } else {
4181 this.xmlInput(elem);
4182 }
4183 }
4184 if (this.rawInput !== Strophe.Connection.prototype.rawInput) {
4185 if (raw) {
4186 this.rawInput(raw);
4187 } else {
4188 this.rawInput(Strophe.serialize(elem));
4189 }
4190 }
4191
4192 // remove handlers scheduled for deletion
4193 var i, hand;
4194 while (this.removeHandlers.length > 0) {
4195 hand = this.removeHandlers.pop();
4196 i = this.handlers.indexOf(hand);
4197 if (i >= 0) {
4198 this.handlers.splice(i, 1);
4199 }
4200 }
4201
4202 // add handlers scheduled for addition
4203 while (this.addHandlers.length > 0) {
4204 this.handlers.push(this.addHandlers.pop());
4205 }
4206
4207 // handle graceful disconnect
4208 if (this.disconnecting && this._proto._emptyQueue()) {
4209 this._doDisconnect();
4210 return;
4211 }
4212
4213 // handle error scenarios
4214 var errors;
4215 if (elem.getElementsByTagNameNS) {
4216 errors = elem.getElementsByTagNameNS(Strophe.NS.STREAM, "error");
4217 } else {
4218 errors = elem.getElementsByTagName("stream:error");
4219 }
4220 if (errors.length > 0) {
4221 // Don't process stanzas that come in after disconnect
4222 if (this.disconnecting) {
4223 return;
4224 }
4225 var error = errors[0];
4226
4227 var condition = "";
4228 var text = "";
4229
4230 var ns = "urn:ietf:params:xml:ns:xmpp-streams";
4231 for (var i = 0; i < error.childNodes.length; i++) {
4232 var e = error.childNodes[i];
4233 if (e.getAttribute("xmlns") !== ns) {
4234 break;
4235 } if (e.nodeName === "text") {
4236 text = e.textContent;
4237 } else {
4238 condition = e.nodeName;
4239 }
4240 }
4241
4242 var errorString = "BOSH stream error: ";
4243
4244 if (condition) {
4245 errorString += condition;
4246 } else {
4247 errorString += "unknown";
4248 }
4249
4250 if (text) {
4251 errorString += " - " + text;
4252 }
4253
4254 Strophe.error(errorString);
4255 this._changeConnectStatus(Strophe.Status.ERROR, condition);
4256 this._doDisconnect(condition);
4257 return;
4258 }
4259
4260 // send each incoming stanza through the handler chain
4261 var that = this;
4262 Strophe.forEachChild(elem, null, function (child) {
4263 var i, newList;
4264 // process handlers
4265 newList = that.handlers;
4266 that.handlers = [];
4267 for (i = 0; i < newList.length; i++) {
4268 var hand = newList[i];
4269 // encapsulate 'handler.run' not to lose the whole handler list if
4270 // one of the handlers throws an exception
4271 try {
4272 if (hand.isMatch(child) &&
4273 (that.authenticated || !hand.user)) {
4274 if (hand.run(child)) {
4275 that.handlers.push(hand);
4276 }
4277 } else {
4278 that.handlers.push(hand);
4279 }
4280 } catch(e) {
4281 // if the handler throws an exception, we consider it as false
4282 Strophe.warn('Removing Strophe handlers due to uncaught exception: '+e.message);
4283 }
4284 }
4285 });
4286 },
4287
4288
4289 /** Attribute: mechanisms
4290 * SASL Mechanisms available for Connection.
4291 */
4292 mechanisms: {},
4293
4294 /** PrivateFunction: _no_auth_received
4295 *
4296 * Called on stream start/restart when no stream:features
4297 * has been received or when no viable authentication mechanism is offered.
4298 *
4299 * Sends a blank poll request.
4300 */
4301 _no_auth_received: function (_callback) {
4302 var error_msg = "Server did not offer a supported authentication mechanism";
4303 Strophe.error(error_msg);
4304 this._changeConnectStatus(
4305 Strophe.Status.CONNFAIL,
4306 Strophe.ErrorCondition.NO_AUTH_MECH
4307 );
4308 if (_callback) {
4309 _callback.call(this);
4310 }
4311 this._doDisconnect();
4312 },
4313
4314 /** PrivateFunction: _connect_cb
4315 * _Private_ handler for initial connection request.
4316 *
4317 * This handler is used to process the initial connection request
4318 * response from the BOSH server. It is used to set up authentication
4319 * handlers and start the authentication process.
4320 *
4321 * SASL authentication will be attempted if available, otherwise
4322 * the code will fall back to legacy authentication.
4323 *
4324 * Parameters:
4325 * (Strophe.Request) req - The current request.
4326 * (Function) _callback - low level (xmpp) connect callback function.
4327 * Useful for plugins with their own xmpp connect callback (when they
4328 * want to do something special).
4329 */
4330 _connect_cb: function (req, _callback, raw) {
4331 Strophe.info("_connect_cb was called");
4332 this.connected = true;
4333
4334 var bodyWrap;
4335 try {
4336 bodyWrap = this._proto._reqToData(req);
4337 } catch (e) {
4338 if (e !== "badformat") { throw e; }
4339 this._changeConnectStatus(
4340 Strophe.Status.CONNFAIL,
4341 Strophe.ErrorCondition.BAD_FORMAT
4342 );
4343 this._doDisconnect(Strophe.ErrorCondition.BAD_FORMAT);
4344 }
4345 if (!bodyWrap) { return; }
4346
4347 if (this.xmlInput !== Strophe.Connection.prototype.xmlInput) {
4348 if (bodyWrap.nodeName === this._proto.strip && bodyWrap.childNodes.length) {
4349 this.xmlInput(bodyWrap.childNodes[0]);
4350 } else {
4351 this.xmlInput(bodyWrap);
4352 }
4353 }
4354 if (this.rawInput !== Strophe.Connection.prototype.rawInput) {
4355 if (raw) {
4356 this.rawInput(raw);
4357 } else {
4358 this.rawInput(Strophe.serialize(bodyWrap));
4359 }
4360 }
4361
4362 var conncheck = this._proto._connect_cb(bodyWrap);
4363 if (conncheck === Strophe.Status.CONNFAIL) {
4364 return;
4365 }
4366
4367 // Check for the stream:features tag
4368 var hasFeatures;
4369 if (bodyWrap.getElementsByTagNameNS) {
4370 hasFeatures = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "features").length > 0;
4371 } else {
4372 hasFeatures = bodyWrap.getElementsByTagName("stream:features").length > 0 ||
4373 bodyWrap.getElementsByTagName("features").length > 0;
4374 }
4375 if (!hasFeatures) {
4376 this._no_auth_received(_callback);
4377 return;
4378 }
4379
4380 var matched = [], i, mech;
4381 var mechanisms = bodyWrap.getElementsByTagName("mechanism");
4382 if (mechanisms.length > 0) {
4383 for (i = 0; i < mechanisms.length; i++) {
4384 mech = Strophe.getText(mechanisms[i]);
4385 if (this.mechanisms[mech]) matched.push(this.mechanisms[mech]);
4386 }
4387 }
4388 if (matched.length === 0) {
4389 if (bodyWrap.getElementsByTagName("auth").length === 0) {
4390 // There are no matching SASL mechanisms and also no legacy
4391 // auth available.
4392 this._no_auth_received(_callback);
4393 return;
4394 }
4395 }
4396 if (this.do_authentication !== false) {
4397 this.authenticate(matched);
4398 }
4399 },
4400
4401 /** Function: sortMechanismsByPriority
4402 *
4403 * Sorts an array of objects with prototype SASLMechanism according to
4404 * their priorities.
4405 *
4406 * Parameters:
4407 * (Array) mechanisms - Array of SASL mechanisms.
4408 *
4409 */
4410 sortMechanismsByPriority: function (mechanisms) {
4411 // Sorting mechanisms according to priority.
4412 var i, j, higher, swap;
4413 for (i = 0; i < mechanisms.length - 1; ++i) {
4414 higher = i;
4415 for (j = i + 1; j < mechanisms.length; ++j) {
4416 if (mechanisms[j].prototype.priority > mechanisms[higher].prototype.priority) {
4417 higher = j;
4418 }
4419 }
4420 if (higher !== i) {
4421 swap = mechanisms[i];
4422 mechanisms[i] = mechanisms[higher];
4423 mechanisms[higher] = swap;
4424 }
4425 }
4426 return mechanisms;
4427 },
4428
4429 /** PrivateFunction: _attemptSASLAuth
4430 *
4431 * Iterate through an array of SASL mechanisms and attempt authentication
4432 * with the highest priority (enabled) mechanism.
4433 *
4434 * Parameters:
4435 * (Array) mechanisms - Array of SASL mechanisms.
4436 *
4437 * Returns:
4438 * (Boolean) mechanism_found - true or false, depending on whether a
4439 * valid SASL mechanism was found with which authentication could be
4440 * started.
4441 */
4442 _attemptSASLAuth: function (mechanisms) {
4443 mechanisms = this.sortMechanismsByPriority(mechanisms || []);
4444 var i = 0, mechanism_found = false;
4445 for (i = 0; i < mechanisms.length; ++i) {
4446 if (!mechanisms[i].prototype.test(this)) {
4447 continue;
4448 }
4449 this._sasl_success_handler = this._addSysHandler(
4450 this._sasl_success_cb.bind(this), null,
4451 "success", null, null);
4452 this._sasl_failure_handler = this._addSysHandler(
4453 this._sasl_failure_cb.bind(this), null,
4454 "failure", null, null);
4455 this._sasl_challenge_handler = this._addSysHandler(
4456 this._sasl_challenge_cb.bind(this), null,
4457 "challenge", null, null);
4458
4459 this._sasl_mechanism = new mechanisms[i]();
4460 this._sasl_mechanism.onStart(this);
4461
4462 var request_auth_exchange = $build("auth", {
4463 xmlns: Strophe.NS.SASL,
4464 mechanism: this._sasl_mechanism.name
4465 });
4466 if (this._sasl_mechanism.isClientFirst) {
4467 var response = this._sasl_mechanism.onChallenge(this, null);
4468 request_auth_exchange.t(btoa(response));
4469 }
4470 this.send(request_auth_exchange.tree());
4471 mechanism_found = true;
4472 break;
4473 }
4474 return mechanism_found;
4475 },
4476
4477 /** PrivateFunction: _attemptLegacyAuth
4478 *
4479 * Attempt legacy (i.e. non-SASL) authentication.
4480 *
4481 */
4482 _attemptLegacyAuth: function () {
4483 if (Strophe.getNodeFromJid(this.jid) === null) {
4484 // we don't have a node, which is required for non-anonymous
4485 // client connections
4486 this._changeConnectStatus(
4487 Strophe.Status.CONNFAIL,
4488 Strophe.ErrorCondition.MISSING_JID_NODE
4489 );
4490 this.disconnect(Strophe.ErrorCondition.MISSING_JID_NODE);
4491 } else {
4492 // Fall back to legacy authentication
4493 this._changeConnectStatus(Strophe.Status.AUTHENTICATING, null);
4494 this._addSysHandler(
4495 this._auth1_cb.bind(this),
4496 null, null, null, "_auth_1"
4497 );
4498 this.send($iq({
4499 'type': "get",
4500 'to': this.domain,
4501 'id': "_auth_1"
4502 }).c("query", {xmlns: Strophe.NS.AUTH})
4503 .c("username", {}).t(Strophe.getNodeFromJid(this.jid))
4504 .tree());
4505 }
4506 },
4507
4508 /** Function: authenticate
4509 * Set up authentication
4510 *
4511 * Continues the initial connection request by setting up authentication
4512 * handlers and starting the authentication process.
4513 *
4514 * SASL authentication will be attempted if available, otherwise
4515 * the code will fall back to legacy authentication.
4516 *
4517 * Parameters:
4518 * (Array) matched - Array of SASL mechanisms supported.
4519 *
4520 */
4521 authenticate: function (matched) {
4522 if (!this._attemptSASLAuth(matched)) {
4523 this._attemptLegacyAuth();
4524 }
4525 },
4526
4527 /** PrivateFunction: _sasl_challenge_cb
4528 * _Private_ handler for the SASL challenge
4529 *
4530 */
4531 _sasl_challenge_cb: function(elem) {
4532 var challenge = atob(Strophe.getText(elem));
4533 var response = this._sasl_mechanism.onChallenge(this, challenge);
4534 var stanza = $build('response', {
4535 'xmlns': Strophe.NS.SASL
4536 });
4537 if (response !== "") {
4538 stanza.t(btoa(response));
4539 }
4540 this.send(stanza.tree());
4541 return true;
4542 },
4543
4544 /** PrivateFunction: _auth1_cb
4545 * _Private_ handler for legacy authentication.
4546 *
4547 * This handler is called in response to the initial <iq type='get'/>
4548 * for legacy authentication. It builds an authentication <iq/> and
4549 * sends it, creating a handler (calling back to _auth2_cb()) to
4550 * handle the result
4551 *
4552 * Parameters:
4553 * (XMLElement) elem - The stanza that triggered the callback.
4554 *
4555 * Returns:
4556 * false to remove the handler.
4557 */
4558 /* jshint unused:false */
4559 _auth1_cb: function (elem) {
4560 // build plaintext auth iq
4561 var iq = $iq({type: "set", id: "_auth_2"})
4562 .c('query', {xmlns: Strophe.NS.AUTH})
4563 .c('username', {}).t(Strophe.getNodeFromJid(this.jid))
4564 .up()
4565 .c('password').t(this.pass);
4566
4567 if (!Strophe.getResourceFromJid(this.jid)) {
4568 // since the user has not supplied a resource, we pick
4569 // a default one here. unlike other auth methods, the server
4570 // cannot do this for us.
4571 this.jid = Strophe.getBareJidFromJid(this.jid) + '/strophe';
4572 }
4573 iq.up().c('resource', {}).t(Strophe.getResourceFromJid(this.jid));
4574
4575 this._addSysHandler(this._auth2_cb.bind(this), null,
4576 null, null, "_auth_2");
4577 this.send(iq.tree());
4578 return false;
4579 },
4580 /* jshint unused:true */
4581
4582 /** PrivateFunction: _sasl_success_cb
4583 * _Private_ handler for succesful SASL authentication.
4584 *
4585 * Parameters:
4586 * (XMLElement) elem - The matching stanza.
4587 *
4588 * Returns:
4589 * false to remove the handler.
4590 */
4591 _sasl_success_cb: function (elem) {
4592 if (this._sasl_data["server-signature"]) {
4593 var serverSignature;
4594 var success = atob(Strophe.getText(elem));
4595 var attribMatch = /([a-z]+)=([^,]+)(,|$)/;
4596 var matches = success.match(attribMatch);
4597 if (matches[1] === "v") {
4598 serverSignature = matches[2];
4599 }
4600
4601 if (serverSignature !== this._sasl_data["server-signature"]) {
4602 // remove old handlers
4603 this.deleteHandler(this._sasl_failure_handler);
4604 this._sasl_failure_handler = null;
4605 if (this._sasl_challenge_handler) {
4606 this.deleteHandler(this._sasl_challenge_handler);
4607 this._sasl_challenge_handler = null;
4608 }
4609
4610 this._sasl_data = {};
4611 return this._sasl_failure_cb(null);
4612 }
4613 }
4614 Strophe.info("SASL authentication succeeded.");
4615
4616 if (this._sasl_mechanism) {
4617 this._sasl_mechanism.onSuccess();
4618 }
4619
4620 // remove old handlers
4621 this.deleteHandler(this._sasl_failure_handler);
4622 this._sasl_failure_handler = null;
4623 if (this._sasl_challenge_handler) {
4624 this.deleteHandler(this._sasl_challenge_handler);
4625 this._sasl_challenge_handler = null;
4626 }
4627
4628 var streamfeature_handlers = [];
4629 var wrapper = function(handlers, elem) {
4630 while (handlers.length) {
4631 this.deleteHandler(handlers.pop());
4632 }
4633 this._sasl_auth1_cb.bind(this)(elem);
4634 return false;
4635 };
4636 streamfeature_handlers.push(this._addSysHandler(function(elem) {
4637 wrapper.bind(this)(streamfeature_handlers, elem);
4638 }.bind(this), null, "stream:features", null, null));
4639 streamfeature_handlers.push(this._addSysHandler(function(elem) {
4640 wrapper.bind(this)(streamfeature_handlers, elem);
4641 }.bind(this), Strophe.NS.STREAM, "features", null, null));
4642
4643 // we must send an xmpp:restart now
4644 this._sendRestart();
4645
4646 return false;
4647 },
4648
4649 /** PrivateFunction: _sasl_auth1_cb
4650 * _Private_ handler to start stream binding.
4651 *
4652 * Parameters:
4653 * (XMLElement) elem - The matching stanza.
4654 *
4655 * Returns:
4656 * false to remove the handler.
4657 */
4658 _sasl_auth1_cb: function (elem) {
4659 // save stream:features for future usage
4660 this.features = elem;
4661 var i, child;
4662 for (i = 0; i < elem.childNodes.length; i++) {
4663 child = elem.childNodes[i];
4664 if (child.nodeName === 'bind') {
4665 this.do_bind = true;
4666 }
4667
4668 if (child.nodeName === 'session') {
4669 this.do_session = true;
4670 }
4671 }
4672
4673 if (!this.do_bind) {
4674 this._changeConnectStatus(Strophe.Status.AUTHFAIL, null);
4675 return false;
4676 } else {
4677 this._addSysHandler(this._sasl_bind_cb.bind(this), null, null,
4678 null, "_bind_auth_2");
4679
4680 var resource = Strophe.getResourceFromJid(this.jid);
4681 if (resource) {
4682 this.send($iq({type: "set", id: "_bind_auth_2"})
4683 .c('bind', {xmlns: Strophe.NS.BIND})
4684 .c('resource', {}).t(resource).tree());
4685 } else {
4686 this.send($iq({type: "set", id: "_bind_auth_2"})
4687 .c('bind', {xmlns: Strophe.NS.BIND})
4688 .tree());
4689 }
4690 }
4691 return false;
4692 },
4693
4694 /** PrivateFunction: _sasl_bind_cb
4695 * _Private_ handler for binding result and session start.
4696 *
4697 * Parameters:
4698 * (XMLElement) elem - The matching stanza.
4699 *
4700 * Returns:
4701 * false to remove the handler.
4702 */
4703 _sasl_bind_cb: function (elem) {
4704 if (elem.getAttribute("type") === "error") {
4705 Strophe.info("SASL binding failed.");
4706 var conflict = elem.getElementsByTagName("conflict"), condition;
4707 if (conflict.length > 0) {
4708 condition = Strophe.ErrorCondition.CONFLICT;
4709 }
4710 this._changeConnectStatus(Strophe.Status.AUTHFAIL, condition, elem);
4711 return false;
4712 }
4713
4714 // TODO - need to grab errors
4715 var bind = elem.getElementsByTagName("bind");
4716 var jidNode;
4717 if (bind.length > 0) {
4718 // Grab jid
4719 jidNode = bind[0].getElementsByTagName("jid");
4720 if (jidNode.length > 0) {
4721 this.jid = Strophe.getText(jidNode[0]);
4722
4723 if (this.do_session) {
4724 this._addSysHandler(this._sasl_session_cb.bind(this),
4725 null, null, null, "_session_auth_2");
4726
4727 this.send($iq({type: "set", id: "_session_auth_2"})
4728 .c('session', {xmlns: Strophe.NS.SESSION})
4729 .tree());
4730 } else {
4731 this.authenticated = true;
4732 this._changeConnectStatus(Strophe.Status.CONNECTED, null);
4733 }
4734 }
4735 } else {
4736 Strophe.info("SASL binding failed.");
4737 this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
4738 return false;
4739 }
4740 },
4741
4742 /** PrivateFunction: _sasl_session_cb
4743 * _Private_ handler to finish successful SASL connection.
4744 *
4745 * This sets Connection.authenticated to true on success, which
4746 * starts the processing of user handlers.
4747 *
4748 * Parameters:
4749 * (XMLElement) elem - The matching stanza.
4750 *
4751 * Returns:
4752 * false to remove the handler.
4753 */
4754 _sasl_session_cb: function (elem) {
4755 if (elem.getAttribute("type") === "result") {
4756 this.authenticated = true;
4757 this._changeConnectStatus(Strophe.Status.CONNECTED, null);
4758 } else if (elem.getAttribute("type") === "error") {
4759 Strophe.info("Session creation failed.");
4760 this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
4761 return false;
4762 }
4763 return false;
4764 },
4765
4766 /** PrivateFunction: _sasl_failure_cb
4767 * _Private_ handler for SASL authentication failure.
4768 *
4769 * Parameters:
4770 * (XMLElement) elem - The matching stanza.
4771 *
4772 * Returns:
4773 * false to remove the handler.
4774 */
4775 /* jshint unused:false */
4776 _sasl_failure_cb: function (elem) {
4777 // delete unneeded handlers
4778 if (this._sasl_success_handler) {
4779 this.deleteHandler(this._sasl_success_handler);
4780 this._sasl_success_handler = null;
4781 }
4782 if (this._sasl_challenge_handler) {
4783 this.deleteHandler(this._sasl_challenge_handler);
4784 this._sasl_challenge_handler = null;
4785 }
4786
4787 if(this._sasl_mechanism)
4788 this._sasl_mechanism.onFailure();
4789 this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
4790 return false;
4791 },
4792 /* jshint unused:true */
4793
4794 /** PrivateFunction: _auth2_cb
4795 * _Private_ handler to finish legacy authentication.
4796 *
4797 * This handler is called when the result from the jabber:iq:auth
4798 * <iq/> stanza is returned.
4799 *
4800 * Parameters:
4801 * (XMLElement) elem - The stanza that triggered the callback.
4802 *
4803 * Returns:
4804 * false to remove the handler.
4805 */
4806 _auth2_cb: function (elem) {
4807 if (elem.getAttribute("type") === "result") {
4808 this.authenticated = true;
4809 this._changeConnectStatus(Strophe.Status.CONNECTED, null);
4810 } else if (elem.getAttribute("type") === "error") {
4811 this._changeConnectStatus(Strophe.Status.AUTHFAIL, null, elem);
4812 this.disconnect('authentication failed');
4813 }
4814 return false;
4815 },
4816
4817 /** PrivateFunction: _addSysTimedHandler
4818 * _Private_ function to add a system level timed handler.
4819 *
4820 * This function is used to add a Strophe.TimedHandler for the
4821 * library code. System timed handlers are allowed to run before
4822 * authentication is complete.
4823 *
4824 * Parameters:
4825 * (Integer) period - The period of the handler.
4826 * (Function) handler - The callback function.
4827 */
4828 _addSysTimedHandler: function (period, handler) {
4829 var thand = new Strophe.TimedHandler(period, handler);
4830 thand.user = false;
4831 this.addTimeds.push(thand);
4832 return thand;
4833 },
4834
4835 /** PrivateFunction: _addSysHandler
4836 * _Private_ function to add a system level stanza handler.
4837 *
4838 * This function is used to add a Strophe.Handler for the
4839 * library code. System stanza handlers are allowed to run before
4840 * authentication is complete.
4841 *
4842 * Parameters:
4843 * (Function) handler - The callback function.
4844 * (String) ns - The namespace to match.
4845 * (String) name - The stanza name to match.
4846 * (String) type - The stanza type attribute to match.
4847 * (String) id - The stanza id attribute to match.
4848 */
4849 _addSysHandler: function (handler, ns, name, type, id) {
4850 var hand = new Strophe.Handler(handler, ns, name, type, id);
4851 hand.user = false;
4852 this.addHandlers.push(hand);
4853 return hand;
4854 },
4855
4856 /** PrivateFunction: _onDisconnectTimeout
4857 * _Private_ timeout handler for handling non-graceful disconnection.
4858 *
4859 * If the graceful disconnect process does not complete within the
4860 * time allotted, this handler finishes the disconnect anyway.
4861 *
4862 * Returns:
4863 * false to remove the handler.
4864 */
4865 _onDisconnectTimeout: function () {
4866 Strophe.info("_onDisconnectTimeout was called");
4867 this._changeConnectStatus(Strophe.Status.CONNTIMEOUT, null);
4868 this._proto._onDisconnectTimeout();
4869 // actually disconnect
4870 this._doDisconnect();
4871 return false;
4872 },
4873
4874 /** PrivateFunction: _onIdle
4875 * _Private_ handler to process events during idle cycle.
4876 *
4877 * This handler is called every 100ms to fire timed handlers that
4878 * are ready and keep poll requests going.
4879 */
4880 _onIdle: function () {
4881 var i, thand, since, newList;
4882
4883 // add timed handlers scheduled for addition
4884 // NOTE: we add before remove in the case a timed handler is
4885 // added and then deleted before the next _onIdle() call.
4886 while (this.addTimeds.length > 0) {
4887 this.timedHandlers.push(this.addTimeds.pop());
4888 }
4889
4890 // remove timed handlers that have been scheduled for deletion
4891 while (this.removeTimeds.length > 0) {
4892 thand = this.removeTimeds.pop();
4893 i = this.timedHandlers.indexOf(thand);
4894 if (i >= 0) {
4895 this.timedHandlers.splice(i, 1);
4896 }
4897 }
4898
4899 // call ready timed handlers
4900 var now = new Date().getTime();
4901 newList = [];
4902 for (i = 0; i < this.timedHandlers.length; i++) {
4903 thand = this.timedHandlers[i];
4904 if (this.authenticated || !thand.user) {
4905 since = thand.lastCalled + thand.period;
4906 if (since - now <= 0) {
4907 if (thand.run()) {
4908 newList.push(thand);
4909 }
4910 } else {
4911 newList.push(thand);
4912 }
4913 }
4914 }
4915 this.timedHandlers = newList;
4916
4917 clearTimeout(this._idleTimeout);
4918
4919 this._proto._onIdle();
4920
4921 // reactivate the timer only if connected
4922 if (this.connected) {
4923 // XXX: setTimeout should be called only with function expressions (23974bc1)
4924 this._idleTimeout = setTimeout(function() {
4925 this._onIdle();
4926 }.bind(this), 100);
4927 }
4928 }
4929};
4930
4931/** Class: Strophe.SASLMechanism
4932 *
4933 * encapsulates SASL authentication mechanisms.
4934 *
4935 * User code may override the priority for each mechanism or disable it completely.
4936 * See <priority> for information about changing priority and <test> for informatian on
4937 * how to disable a mechanism.
4938 *
4939 * By default, all mechanisms are enabled and the priorities are
4940 *
4941 * OAUTHBEARER - 60
4942 * SCRAM-SHA1 - 50
4943 * DIGEST-MD5 - 40
4944 * PLAIN - 30
4945 * ANONYMOUS - 20
4946 * EXTERNAL - 10
4947 *
4948 * See: Strophe.Connection.addSupportedSASLMechanisms
4949 */
4950
4951/**
4952 * PrivateConstructor: Strophe.SASLMechanism
4953 * SASL auth mechanism abstraction.
4954 *
4955 * Parameters:
4956 * (String) name - SASL Mechanism name.
4957 * (Boolean) isClientFirst - If client should send response first without challenge.
4958 * (Number) priority - Priority.
4959 *
4960 * Returns:
4961 * A new Strophe.SASLMechanism object.
4962 */
4963Strophe.SASLMechanism = function(name, isClientFirst, priority) {
4964 /** PrivateVariable: name
4965 * Mechanism name.
4966 */
4967 this.name = name;
4968 /** PrivateVariable: isClientFirst
4969 * If client sends response without initial server challenge.
4970 */
4971 this.isClientFirst = isClientFirst;
4972 /** Variable: priority
4973 * Determines which <SASLMechanism> is chosen for authentication (Higher is better).
4974 * Users may override this to prioritize mechanisms differently.
4975 *
4976 * In the default configuration the priorities are
4977 *
4978 * SCRAM-SHA1 - 40
4979 * DIGEST-MD5 - 30
4980 * Plain - 20
4981 *
4982 * Example: (This will cause Strophe to choose the mechanism that the server sent first)
4983 *
4984 * > Strophe.SASLMD5.priority = Strophe.SASLSHA1.priority;
4985 *
4986 * See <SASL mechanisms> for a list of available mechanisms.
4987 *
4988 */
4989 this.priority = priority;
4990};
4991
4992Strophe.SASLMechanism.prototype = {
4993 /**
4994 * Function: test
4995 * Checks if mechanism able to run.
4996 * To disable a mechanism, make this return false;
4997 *
4998 * To disable plain authentication run
4999 * > Strophe.SASLPlain.test = function() {
5000 * > return false;
5001 * > }
5002 *
5003 * See <SASL mechanisms> for a list of available mechanisms.
5004 *
5005 * Parameters:
5006 * (Strophe.Connection) connection - Target Connection.
5007 *
5008 * Returns:
5009 * (Boolean) If mechanism was able to run.
5010 */
5011 /* jshint unused:false */
5012 test: function(connection) {
5013 return true;
5014 },
5015 /* jshint unused:true */
5016
5017 /** PrivateFunction: onStart
5018 * Called before starting mechanism on some connection.
5019 *
5020 * Parameters:
5021 * (Strophe.Connection) connection - Target Connection.
5022 */
5023 onStart: function(connection) {
5024 this._connection = connection;
5025 },
5026
5027 /** PrivateFunction: onChallenge
5028 * Called by protocol implementation on incoming challenge. If client is
5029 * first (isClientFirst === true) challenge will be null on the first call.
5030 *
5031 * Parameters:
5032 * (Strophe.Connection) connection - Target Connection.
5033 * (String) challenge - current challenge to handle.
5034 *
5035 * Returns:
5036 * (String) Mechanism response.
5037 */
5038 /* jshint unused:false */
5039 onChallenge: function(connection, challenge) {
5040 throw new Error("You should implement challenge handling!");
5041 },
5042 /* jshint unused:true */
5043
5044 /** PrivateFunction: onFailure
5045 * Protocol informs mechanism implementation about SASL failure.
5046 */
5047 onFailure: function() {
5048 this._connection = null;
5049 },
5050
5051 /** PrivateFunction: onSuccess
5052 * Protocol informs mechanism implementation about SASL success.
5053 */
5054 onSuccess: function() {
5055 this._connection = null;
5056 }
5057};
5058
5059 /** Constants: SASL mechanisms
5060 * Available authentication mechanisms
5061 *
5062 * Strophe.SASLAnonymous - SASL ANONYMOUS authentication.
5063 * Strophe.SASLPlain - SASL PLAIN authentication.
5064 * Strophe.SASLMD5 - SASL DIGEST-MD5 authentication
5065 * Strophe.SASLSHA1 - SASL SCRAM-SHA1 authentication
5066 * Strophe.SASLOAuthBearer - SASL OAuth Bearer authentication
5067 * Strophe.SASLExternal - SASL EXTERNAL authentication
5068 * Strophe.SASLXOAuth2 - SASL X-OAuth2 authentication
5069 */
5070
5071// Building SASL callbacks
5072
5073/** PrivateConstructor: SASLAnonymous
5074 * SASL ANONYMOUS authentication.
5075 */
5076Strophe.SASLAnonymous = function() {};
5077Strophe.SASLAnonymous.prototype = new Strophe.SASLMechanism("ANONYMOUS", false, 20);
5078
5079Strophe.SASLAnonymous.prototype.test = function(connection) {
5080 return connection.authcid === null;
5081};
5082
5083
5084/** PrivateConstructor: SASLPlain
5085 * SASL PLAIN authentication.
5086 */
5087Strophe.SASLPlain = function() {};
5088Strophe.SASLPlain.prototype = new Strophe.SASLMechanism("PLAIN", true, 50);
5089
5090Strophe.SASLPlain.prototype.test = function(connection) {
5091 return connection.authcid !== null;
5092};
5093
5094Strophe.SASLPlain.prototype.onChallenge = function(connection) {
5095 var auth_str = connection.authzid;
5096 auth_str = auth_str + "\u0000";
5097 auth_str = auth_str + connection.authcid;
5098 auth_str = auth_str + "\u0000";
5099 auth_str = auth_str + connection.pass;
5100 return utils.utf16to8(auth_str);
5101};
5102
5103
5104/** PrivateConstructor: SASLSHA1
5105 * SASL SCRAM SHA 1 authentication.
5106 */
5107Strophe.SASLSHA1 = function() {};
5108Strophe.SASLSHA1.prototype = new Strophe.SASLMechanism("SCRAM-SHA-1", true, 70);
5109
5110Strophe.SASLSHA1.prototype.test = function(connection) {
5111 return connection.authcid !== null;
5112};
5113
5114Strophe.SASLSHA1.prototype.onChallenge = function(connection, challenge, test_cnonce) {
5115 var cnonce = test_cnonce || MD5.hexdigest(Math.random() * 1234567890);
5116 var auth_str = "n=" + utils.utf16to8(connection.authcid);
5117 auth_str += ",r=";
5118 auth_str += cnonce;
5119 connection._sasl_data.cnonce = cnonce;
5120 connection._sasl_data["client-first-message-bare"] = auth_str;
5121
5122 auth_str = "n,," + auth_str;
5123
5124 this.onChallenge = function (connection, challenge) {
5125 var nonce, salt, iter, Hi, U, U_old, i, k, pass;
5126 var clientKey, serverKey, clientSignature;
5127 var responseText = "c=biws,";
5128 var authMessage = connection._sasl_data["client-first-message-bare"] + "," +
5129 challenge + ",";
5130 var cnonce = connection._sasl_data.cnonce;
5131 var attribMatch = /([a-z]+)=([^,]+)(,|$)/;
5132
5133 while (challenge.match(attribMatch)) {
5134 var matches = challenge.match(attribMatch);
5135 challenge = challenge.replace(matches[0], "");
5136 switch (matches[1]) {
5137 case "r":
5138 nonce = matches[2];
5139 break;
5140 case "s":
5141 salt = matches[2];
5142 break;
5143 case "i":
5144 iter = matches[2];
5145 break;
5146 }
5147 }
5148
5149 if (nonce.substr(0, cnonce.length) !== cnonce) {
5150 connection._sasl_data = {};
5151 return connection._sasl_failure_cb();
5152 }
5153
5154 responseText += "r=" + nonce;
5155 authMessage += responseText;
5156
5157 salt = atob(salt);
5158 salt += "\x00\x00\x00\x01";
5159
5160 pass = utils.utf16to8(connection.pass);
5161 Hi = U_old = SHA1.core_hmac_sha1(pass, salt);
5162 for (i = 1; i < iter; i++) {
5163 U = SHA1.core_hmac_sha1(pass, SHA1.binb2str(U_old));
5164 for (k = 0; k < 5; k++) {
5165 Hi[k] ^= U[k];
5166 }
5167 U_old = U;
5168 }
5169 Hi = SHA1.binb2str(Hi);
5170
5171 clientKey = SHA1.core_hmac_sha1(Hi, "Client Key");
5172 serverKey = SHA1.str_hmac_sha1(Hi, "Server Key");
5173 clientSignature = SHA1.core_hmac_sha1(SHA1.str_sha1(SHA1.binb2str(clientKey)), authMessage);
5174 connection._sasl_data["server-signature"] = SHA1.b64_hmac_sha1(serverKey, authMessage);
5175
5176 for (k = 0; k < 5; k++) {
5177 clientKey[k] ^= clientSignature[k];
5178 }
5179
5180 responseText += ",p=" + btoa(SHA1.binb2str(clientKey));
5181 return responseText;
5182 }.bind(this);
5183
5184 return auth_str;
5185};
5186
5187
5188/** PrivateConstructor: SASLMD5
5189 * SASL DIGEST MD5 authentication.
5190 */
5191Strophe.SASLMD5 = function() {};
5192Strophe.SASLMD5.prototype = new Strophe.SASLMechanism("DIGEST-MD5", false, 60);
5193
5194Strophe.SASLMD5.prototype.test = function(connection) {
5195 return connection.authcid !== null;
5196};
5197
5198/** PrivateFunction: _quote
5199 * _Private_ utility function to backslash escape and quote strings.
5200 *
5201 * Parameters:
5202 * (String) str - The string to be quoted.
5203 *
5204 * Returns:
5205 * quoted string
5206 */
5207Strophe.SASLMD5.prototype._quote = function (str) {
5208 return '"' + str.replace(/\\/g, "\\\\").replace(/"/g, '\\"') + '"';
5209 //" end string workaround for emacs
5210};
5211
5212Strophe.SASLMD5.prototype.onChallenge = function(connection, challenge, test_cnonce) {
5213 var attribMatch = /([a-z]+)=("[^"]+"|[^,"]+)(?:,|$)/;
5214 var cnonce = test_cnonce || MD5.hexdigest("" + (Math.random() * 1234567890));
5215 var realm = "";
5216 var host = null;
5217 var nonce = "";
5218 var qop = "";
5219 var matches;
5220
5221 while (challenge.match(attribMatch)) {
5222 matches = challenge.match(attribMatch);
5223 challenge = challenge.replace(matches[0], "");
5224 matches[2] = matches[2].replace(/^"(.+)"$/, "$1");
5225 switch (matches[1]) {
5226 case "realm":
5227 realm = matches[2];
5228 break;
5229 case "nonce":
5230 nonce = matches[2];
5231 break;
5232 case "qop":
5233 qop = matches[2];
5234 break;
5235 case "host":
5236 host = matches[2];
5237 break;
5238 }
5239 }
5240
5241 var digest_uri = connection.servtype + "/" + connection.domain;
5242 if (host !== null) {
5243 digest_uri = digest_uri + "/" + host;
5244 }
5245
5246 var cred = utils.utf16to8(connection.authcid + ":" + realm + ":" + this._connection.pass);
5247 var A1 = MD5.hash(cred) + ":" + nonce + ":" + cnonce;
5248 var A2 = 'AUTHENTICATE:' + digest_uri;
5249
5250 var responseText = "";
5251 responseText += 'charset=utf-8,';
5252 responseText += 'username=' + this._quote(utils.utf16to8(connection.authcid)) + ',';
5253 responseText += 'realm=' + this._quote(realm) + ',';
5254 responseText += 'nonce=' + this._quote(nonce) + ',';
5255 responseText += 'nc=00000001,';
5256 responseText += 'cnonce=' + this._quote(cnonce) + ',';
5257 responseText += 'digest-uri=' + this._quote(digest_uri) + ',';
5258 responseText += 'response=' + MD5.hexdigest(MD5.hexdigest(A1) + ":" +
5259 nonce + ":00000001:" +
5260 cnonce + ":auth:" +
5261 MD5.hexdigest(A2)) + ",";
5262 responseText += 'qop=auth';
5263
5264 this.onChallenge = function () {
5265 return "";
5266 };
5267 return responseText;
5268};
5269
5270
5271/** PrivateConstructor: SASLOAuthBearer
5272 * SASL OAuth Bearer authentication.
5273 */
5274Strophe.SASLOAuthBearer = function() {};
5275Strophe.SASLOAuthBearer.prototype = new Strophe.SASLMechanism("OAUTHBEARER", true, 40);
5276
5277Strophe.SASLOAuthBearer.prototype.test = function(connection) {
5278 return connection.pass !== null;
5279};
5280
5281Strophe.SASLOAuthBearer.prototype.onChallenge = function(connection) {
5282 var auth_str = 'n,';
5283 if (connection.authcid !== null) {
5284 auth_str = auth_str + 'a=' + connection.authzid;
5285 }
5286 auth_str = auth_str + ',';
5287 auth_str = auth_str + "\u0001";
5288 auth_str = auth_str + 'auth=Bearer ';
5289 auth_str = auth_str + connection.pass;
5290 auth_str = auth_str + "\u0001";
5291 auth_str = auth_str + "\u0001";
5292
5293 return utils.utf16to8(auth_str);
5294};
5295
5296
5297/** PrivateConstructor: SASLExternal
5298 * SASL EXTERNAL authentication.
5299 *
5300 * The EXTERNAL mechanism allows a client to request the server to use
5301 * credentials established by means external to the mechanism to
5302 * authenticate the client. The external means may be, for instance,
5303 * TLS services.
5304 */
5305Strophe.SASLExternal = function() {};
5306Strophe.SASLExternal.prototype = new Strophe.SASLMechanism("EXTERNAL", true, 10);
5307
5308Strophe.SASLExternal.prototype.onChallenge = function(connection) {
5309 /** According to XEP-178, an authzid SHOULD NOT be presented when the
5310 * authcid contained or implied in the client certificate is the JID (i.e.
5311 * authzid) with which the user wants to log in as.
5312 *
5313 * To NOT send the authzid, the user should therefore set the authcid equal
5314 * to the JID when instantiating a new Strophe.Connection object.
5315 */
5316 return connection.authcid === connection.authzid ? '' : connection.authzid;
5317};
5318
5319
5320/** PrivateConstructor: SASLXOAuth2
5321 * SASL X-OAuth2 authentication.
5322 */
5323Strophe.SASLXOAuth2 = function () { };
5324Strophe.SASLXOAuth2.prototype = new Strophe.SASLMechanism("X-OAUTH2", true, 30);
5325
5326Strophe.SASLXOAuth2.prototype.test = function (connection) {
5327 return connection.pass !== null;
5328};
5329
5330Strophe.SASLXOAuth2.prototype.onChallenge = function (connection) {
5331 var auth_str = '\u0000';
5332 if (connection.authcid !== null) {
5333 auth_str = auth_str + connection.authzid;
5334 }
5335 auth_str = auth_str + "\u0000";
5336 auth_str = auth_str + connection.pass;
5337
5338 return utils.utf16to8(auth_str);
5339};
5340
5341
5342return {
5343 'Strophe': Strophe,
5344 '$build': $build,
5345 '$iq': $iq,
5346 '$msg': $msg,
5347 '$pres': $pres,
5348 'SHA1': SHA1,
5349 'MD5': MD5,
5350 'b64_hmac_sha1': SHA1.b64_hmac_sha1,
5351 'b64_sha1': SHA1.b64_sha1,
5352 'str_hmac_sha1': SHA1.str_hmac_sha1,
5353 'str_sha1': SHA1.str_sha1
5354};
5355}));
5356
5357/*
5358 This program is distributed under the terms of the MIT license.
5359 Please see the LICENSE file for details.
5360
5361 Copyright 2006-2008, OGG, LLC
5362*/
5363
5364/* jshint undef: true, unused: true:, noarg: true, latedef: true */
5365/* global define, window, setTimeout, clearTimeout, XMLHttpRequest, ActiveXObject, Strophe, $build */
5366
5367(function (root, factory) {
5368 if (typeof define === 'function' && define.amd) {
5369 define('strophe-bosh',['strophe-core'], function (core) {
5370 return factory(
5371 core.Strophe,
5372 core.$build
5373 );
5374 });
5375 } else if (typeof exports === 'object') {
5376 var core = require('./core');
5377
5378 module.exports = factory(core.Strophe, core.$build);
5379 } else {
5380 // Browser globals
5381 return factory(Strophe, $build);
5382 }
5383}(this, function (Strophe, $build) {
5384
5385/** PrivateClass: Strophe.Request
5386 * _Private_ helper class that provides a cross implementation abstraction
5387 * for a BOSH related XMLHttpRequest.
5388 *
5389 * The Strophe.Request class is used internally to encapsulate BOSH request
5390 * information. It is not meant to be used from user's code.
5391 */
5392
5393/** PrivateConstructor: Strophe.Request
5394 * Create and initialize a new Strophe.Request object.
5395 *
5396 * Parameters:
5397 * (XMLElement) elem - The XML data to be sent in the request.
5398 * (Function) func - The function that will be called when the
5399 * XMLHttpRequest readyState changes.
5400 * (Integer) rid - The BOSH rid attribute associated with this request.
5401 * (Integer) sends - The number of times this same request has been sent.
5402 */
5403Strophe.Request = function (elem, func, rid, sends) {
5404 this.id = ++Strophe._requestId;
5405 this.xmlData = elem;
5406 this.data = Strophe.serialize(elem);
5407 // save original function in case we need to make a new request
5408 // from this one.
5409 this.origFunc = func;
5410 this.func = func;
5411 this.rid = rid;
5412 this.date = NaN;
5413 this.sends = sends || 0;
5414 this.abort = false;
5415 this.dead = null;
5416
5417 this.age = function () {
5418 if (!this.date) { return 0; }
5419 var now = new Date();
5420 return (now - this.date) / 1000;
5421 };
5422 this.timeDead = function () {
5423 if (!this.dead) { return 0; }
5424 var now = new Date();
5425 return (now - this.dead) / 1000;
5426 };
5427 this.xhr = this._newXHR();
5428};
5429
5430Strophe.Request.prototype = {
5431 /** PrivateFunction: getResponse
5432 * Get a response from the underlying XMLHttpRequest.
5433 *
5434 * This function attempts to get a response from the request and checks
5435 * for errors.
5436 *
5437 * Throws:
5438 * "parsererror" - A parser error occured.
5439 * "badformat" - The entity has sent XML that cannot be processed.
5440 *
5441 * Returns:
5442 * The DOM element tree of the response.
5443 */
5444 getResponse: function () {
5445 var node = null;
5446 if (this.xhr.responseXML && this.xhr.responseXML.documentElement) {
5447 node = this.xhr.responseXML.documentElement;
5448 if (node.tagName === "parsererror") {
5449 Strophe.error("invalid response received");
5450 Strophe.error("responseText: " + this.xhr.responseText);
5451 Strophe.error("responseXML: " +
5452 Strophe.serialize(this.xhr.responseXML));
5453 throw "parsererror";
5454 }
5455 } else if (this.xhr.responseText) {
5456 // In React Native, we may get responseText but no responseXML. We can try to parse it manually.
5457 Strophe.debug("Got responseText but no responseXML; attempting to parse it with DOMParser...");
5458 node = new DOMParser().parseFromString(this.xhr.responseText, 'application/xml').documentElement;
5459 if (!node) {
5460 throw new Error('Parsing produced null node');
5461 } else if (node.querySelector('parsererror')) {
5462 Strophe.error("invalid response received: " + node.querySelector('parsererror').textContent);
5463 Strophe.error("responseText: " + this.xhr.responseText);
5464 throw "badformat";
5465 }
5466 }
5467 return node;
5468 },
5469
5470 /** PrivateFunction: _newXHR
5471 * _Private_ helper function to create XMLHttpRequests.
5472 *
5473 * This function creates XMLHttpRequests across all implementations.
5474 *
5475 * Returns:
5476 * A new XMLHttpRequest.
5477 */
5478 _newXHR: function () {
5479 var xhr = null;
5480 if (window.XMLHttpRequest) {
5481 xhr = new XMLHttpRequest();
5482 if (xhr.overrideMimeType) {
5483 xhr.overrideMimeType("text/xml; charset=utf-8");
5484 }
5485 } else if (window.ActiveXObject) {
5486 xhr = new ActiveXObject("Microsoft.XMLHTTP");
5487 }
5488 // use Function.bind() to prepend ourselves as an argument
5489 xhr.onreadystatechange = this.func.bind(null, this);
5490 return xhr;
5491 }
5492};
5493
5494/** Class: Strophe.Bosh
5495 * _Private_ helper class that handles BOSH Connections
5496 *
5497 * The Strophe.Bosh class is used internally by Strophe.Connection
5498 * to encapsulate BOSH sessions. It is not meant to be used from user's code.
5499 */
5500
5501/** File: bosh.js
5502 * A JavaScript library to enable BOSH in Strophejs.
5503 *
5504 * this library uses Bidirectional-streams Over Synchronous HTTP (BOSH)
5505 * to emulate a persistent, stateful, two-way connection to an XMPP server.
5506 * More information on BOSH can be found in XEP 124.
5507 */
5508
5509/** PrivateConstructor: Strophe.Bosh
5510 * Create and initialize a Strophe.Bosh object.
5511 *
5512 * Parameters:
5513 * (Strophe.Connection) connection - The Strophe.Connection that will use BOSH.
5514 *
5515 * Returns:
5516 * A new Strophe.Bosh object.
5517 */
5518Strophe.Bosh = function(connection) {
5519 this._conn = connection;
5520 /* request id for body tags */
5521 this.rid = Math.floor(Math.random() * 4294967295);
5522 /* The current session ID. */
5523 this.sid = null;
5524
5525 // default BOSH values
5526 this.hold = 1;
5527 // Changing default long poll time to 30seconds same as old finesse.
5528 // This will also help us findout the network disconnect in 2.5 mins instead of 5 min.
5529 this.wait = 30;
5530 this.window = 5;
5531 this.errors = 0;
5532 this.inactivity = null;
5533
5534 this.lastResponseHeaders = null;
5535
5536 this._requests = [];
5537};
5538
5539Strophe.Bosh.prototype = {
5540 /** Variable: strip
5541 *
5542 * BOSH-Connections will have all stanzas wrapped in a <body> tag when
5543 * passed to <Strophe.Connection.xmlInput> or <Strophe.Connection.xmlOutput>.
5544 * To strip this tag, User code can set <Strophe.Bosh.strip> to "body":
5545 *
5546 * > Strophe.Bosh.prototype.strip = "body";
5547 *
5548 * This will enable stripping of the body tag in both
5549 * <Strophe.Connection.xmlInput> and <Strophe.Connection.xmlOutput>.
5550 */
5551 strip: null,
5552
5553 /** PrivateFunction: _buildBody
5554 * _Private_ helper function to generate the <body/> wrapper for BOSH.
5555 *
5556 * Returns:
5557 * A Strophe.Builder with a <body/> element.
5558 */
5559 _buildBody: function () {
5560 var bodyWrap = $build('body', {
5561 rid: this.rid++,
5562 xmlns: Strophe.NS.HTTPBIND
5563 });
5564 if (this.sid !== null) {
5565 bodyWrap.attrs({sid: this.sid});
5566 }
5567 if (this._conn.options.keepalive && this._conn._sessionCachingSupported()) {
5568 this._cacheSession();
5569 }
5570 return bodyWrap;
5571 },
5572
5573 /** PrivateFunction: _reset
5574 * Reset the connection.
5575 *
5576 * This function is called by the reset function of the Strophe Connection
5577 */
5578 _reset: function () {
5579 this.rid = Math.floor(Math.random() * 4294967295);
5580 this.sid = null;
5581 this.errors = 0;
5582 if (this._conn._sessionCachingSupported()) {
5583 window.sessionStorage.removeItem('strophe-bosh-session');
5584 }
5585
5586 this._conn.nextValidRid(this.rid);
5587 },
5588
5589 /** PrivateFunction: _connect
5590 * _Private_ function that initializes the BOSH connection.
5591 *
5592 * Creates and sends the Request that initializes the BOSH connection.
5593 */
5594 _connect: function (wait, hold, route) {
5595 this.wait = wait || this.wait;
5596 this.hold = hold || this.hold;
5597 this.errors = 0;
5598
5599 // build the body tag
5600 var body = this._buildBody().attrs({
5601 to: this._conn.domain,
5602 "xml:lang": "en",
5603 wait: this.wait,
5604 hold: this.hold,
5605 content: "text/xml; charset=utf-8",
5606 ver: "1.6",
5607 "xmpp:version": "1.0",
5608 "xmlns:xmpp": Strophe.NS.BOSH
5609 });
5610
5611 if(route){
5612 body.attrs({
5613 route: route
5614 });
5615 }
5616
5617 var _connect_cb = this._conn._connect_cb;
5618
5619 this._requests.push(
5620 new Strophe.Request(body.tree(),
5621 this._onRequestStateChange.bind(
5622 this, _connect_cb.bind(this._conn)),
5623 body.tree().getAttribute("rid")));
5624 this._throttledRequestHandler();
5625 },
5626
5627 /** PrivateFunction: _attach
5628 * Attach to an already created and authenticated BOSH session.
5629 *
5630 * This function is provided to allow Strophe to attach to BOSH
5631 * sessions which have been created externally, perhaps by a Web
5632 * application. This is often used to support auto-login type features
5633 * without putting user credentials into the page.
5634 *
5635 * Parameters:
5636 * (String) jid - The full JID that is bound by the session.
5637 * (String) sid - The SID of the BOSH session.
5638 * (String) rid - The current RID of the BOSH session. This RID
5639 * will be used by the next request.
5640 * (Function) callback The connect callback function.
5641 * (Integer) wait - The optional HTTPBIND wait value. This is the
5642 * time the server will wait before returning an empty result for
5643 * a request. The default setting of 60 seconds is recommended.
5644 * Other settings will require tweaks to the Strophe.TIMEOUT value.
5645 * (Integer) hold - The optional HTTPBIND hold value. This is the
5646 * number of connections the server will hold at one time. This
5647 * should almost always be set to 1 (the default).
5648 * (Integer) wind - The optional HTTBIND window value. This is the
5649 * allowed range of request ids that are valid. The default is 5.
5650 */
5651 _attach: function (jid, sid, rid, callback, wait, hold, wind) {
5652 this._conn.jid = jid;
5653 this.sid = sid;
5654 this.rid = rid;
5655
5656 this._conn.connect_callback = callback;
5657
5658 this._conn.domain = Strophe.getDomainFromJid(this._conn.jid);
5659
5660 this._conn.authenticated = true;
5661 this._conn.connected = true;
5662
5663 this.wait = wait || this.wait;
5664 this.hold = hold || this.hold;
5665 this.window = wind || this.window;
5666
5667 this._conn._changeConnectStatus(Strophe.Status.ATTACHED, null);
5668 },
5669
5670 /** PrivateFunction: _restore
5671 * Attempt to restore a cached BOSH session
5672 *
5673 * Parameters:
5674 * (String) jid - The full JID that is bound by the session.
5675 * This parameter is optional but recommended, specifically in cases
5676 * where prebinded BOSH sessions are used where it's important to know
5677 * that the right session is being restored.
5678 * (Function) callback The connect callback function.
5679 * (Integer) wait - The optional HTTPBIND wait value. This is the
5680 * time the server will wait before returning an empty result for
5681 * a request. The default setting of 60 seconds is recommended.
5682 * Other settings will require tweaks to the Strophe.TIMEOUT value.
5683 * (Integer) hold - The optional HTTPBIND hold value. This is the
5684 * number of connections the server will hold at one time. This
5685 * should almost always be set to 1 (the default).
5686 * (Integer) wind - The optional HTTBIND window value. This is the
5687 * allowed range of request ids that are valid. The default is 5.
5688 */
5689 _restore: function (jid, callback, wait, hold, wind) {
5690 var session = JSON.parse(window.sessionStorage.getItem('strophe-bosh-session'));
5691 if (typeof session !== "undefined" &&
5692 session !== null &&
5693 session.rid &&
5694 session.sid &&
5695 session.jid &&
5696 ( typeof jid === "undefined" ||
5697 jid === null ||
5698 Strophe.getBareJidFromJid(session.jid) === Strophe.getBareJidFromJid(jid) ||
5699 // If authcid is null, then it's an anonymous login, so
5700 // we compare only the domains:
5701 ((Strophe.getNodeFromJid(jid) === null) && (Strophe.getDomainFromJid(session.jid) === jid))
5702 )
5703 ) {
5704 this._conn.restored = true;
5705 this._attach(session.jid, session.sid, session.rid, callback, wait, hold, wind);
5706 } else {
5707 throw { name: "StropheSessionError", message: "_restore: no restoreable session." };
5708 }
5709 },
5710
5711 /** PrivateFunction: _cacheSession
5712 * _Private_ handler for the beforeunload event.
5713 *
5714 * This handler is used to process the Bosh-part of the initial request.
5715 * Parameters:
5716 * (Strophe.Request) bodyWrap - The received stanza.
5717 */
5718 _cacheSession: function () {
5719 if (this._conn.authenticated) {
5720 if (this._conn.jid && this.rid && this.sid) {
5721 window.sessionStorage.setItem('strophe-bosh-session', JSON.stringify({
5722 'jid': this._conn.jid,
5723 'rid': this.rid,
5724 'sid': this.sid
5725 }));
5726 }
5727 } else {
5728 window.sessionStorage.removeItem('strophe-bosh-session');
5729 }
5730 },
5731
5732 /** PrivateFunction: _connect_cb
5733 * _Private_ handler for initial connection request.
5734 *
5735 * This handler is used to process the Bosh-part of the initial request.
5736 * Parameters:
5737 * (Strophe.Request) bodyWrap - The received stanza.
5738 */
5739 _connect_cb: function (bodyWrap) {
5740 var typ = bodyWrap.getAttribute("type");
5741 var cond, conflict;
5742 if (typ !== null && typ === "terminate") {
5743 // an error occurred
5744 cond = bodyWrap.getAttribute("condition");
5745 Strophe.error("BOSH-Connection failed: " + cond);
5746 conflict = bodyWrap.getElementsByTagName("conflict");
5747 if (cond !== null) {
5748 if (cond === "remote-stream-error" && conflict.length > 0) {
5749 cond = "conflict";
5750 }
5751 this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, cond);
5752 } else {
5753 this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "unknown");
5754 }
5755 this._conn._doDisconnect(cond);
5756 return Strophe.Status.CONNFAIL;
5757 }
5758
5759 // check to make sure we don't overwrite these if _connect_cb is
5760 // called multiple times in the case of missing stream:features
5761 if (!this.sid) {
5762 this.sid = bodyWrap.getAttribute("sid");
5763 }
5764 var wind = bodyWrap.getAttribute('requests');
5765 if (wind) { this.window = parseInt(wind, 10); }
5766 var hold = bodyWrap.getAttribute('hold');
5767 if (hold) { this.hold = parseInt(hold, 10); }
5768 var wait = bodyWrap.getAttribute('wait');
5769 if (wait) { this.wait = parseInt(wait, 10); }
5770 var inactivity = bodyWrap.getAttribute('inactivity');
5771 if (inactivity) { this.inactivity = parseInt(inactivity, 10); }
5772 },
5773
5774 /** PrivateFunction: _disconnect
5775 * _Private_ part of Connection.disconnect for Bosh
5776 *
5777 * Parameters:
5778 * (Request) pres - This stanza will be sent before disconnecting.
5779 */
5780 _disconnect: function (pres) {
5781 this._sendTerminate(pres);
5782 },
5783
5784 /** PrivateFunction: _doDisconnect
5785 * _Private_ function to disconnect.
5786 *
5787 * Resets the SID and RID.
5788 */
5789 _doDisconnect: function () {
5790 this.sid = null;
5791 this.rid = Math.floor(Math.random() * 4294967295);
5792 if (this._conn._sessionCachingSupported()) {
5793 window.sessionStorage.removeItem('strophe-bosh-session');
5794 }
5795
5796 this._conn.nextValidRid(this.rid);
5797 },
5798
5799 /** PrivateFunction: _emptyQueue
5800 * _Private_ function to check if the Request queue is empty.
5801 *
5802 * Returns:
5803 * True, if there are no Requests queued, False otherwise.
5804 */
5805 _emptyQueue: function () {
5806 return this._requests.length === 0;
5807 },
5808
5809 /** PrivateFunction: _callProtocolErrorHandlers
5810 * _Private_ function to call error handlers registered for HTTP errors.
5811 *
5812 * Parameters:
5813 * (Strophe.Request) req - The request that is changing readyState.
5814 */
5815 _callProtocolErrorHandlers: function (req) {
5816 var reqStatus = this._getRequestStatus(req),
5817 err_callback;
5818 err_callback = this._conn.protocolErrorHandlers.HTTP[reqStatus];
5819 if (err_callback) {
5820 err_callback.call(this, reqStatus);
5821 }
5822 },
5823
5824 /** PrivateFunction: _hitError
5825 * _Private_ function to handle the error count.
5826 *
5827 * Requests are resent automatically until their error count reaches
5828 * 5. Each time an error is encountered, this function is called to
5829 * increment the count and disconnect if the count is too high.
5830 *
5831 * Parameters:
5832 * (Integer) reqStatus - The request status.
5833 */
5834 _hitError: function (reqStatus) {
5835 this.errors++;
5836 Strophe.warn("request errored, status: " + reqStatus +
5837 ", number of errors: " + this.errors);
5838 if (this.errors > 4) {
5839 this._conn._onDisconnectTimeout();
5840 }
5841 },
5842
5843 /** PrivateFunction: _onDisconnectTimeout
5844 * _Private_ timeout handler for handling non-graceful disconnection.
5845 *
5846 * Cancels all remaining Requests and clears the queue.
5847 */
5848 _onDisconnectTimeout: function () {
5849 this._abortAllRequests();
5850 },
5851
5852 /** PrivateFunction: _abortAllRequests
5853 * _Private_ helper function that makes sure all pending requests are aborted.
5854 */
5855 _abortAllRequests: function _abortAllRequests() {
5856 var req;
5857 while (this._requests.length > 0) {
5858 req = this._requests.pop();
5859 req.abort = true;
5860 req.xhr.abort();
5861 // jslint complains, but this is fine. setting to empty func
5862 // is necessary for IE6
5863 req.xhr.onreadystatechange = function () {}; // jshint ignore:line
5864 }
5865 },
5866
5867 /** PrivateFunction: _onIdle
5868 * _Private_ handler called by Strophe.Connection._onIdle
5869 *
5870 * Sends all queued Requests or polls with empty Request if there are none.
5871 */
5872 _onIdle: function () {
5873 var data = this._conn._data;
5874 // if no requests are in progress, poll
5875 if (this._conn.authenticated && this._requests.length === 0 &&
5876 data.length === 0 && !this._conn.disconnecting) {
5877 Strophe.info("no requests during idle cycle, sending " +
5878 "blank request");
5879 data.push(null);
5880 }
5881
5882 if (this._conn.paused) {
5883 return;
5884 }
5885
5886 if (this._requests.length < 2 && data.length > 0) {
5887 var body = this._buildBody();
5888 for (var i = 0; i < data.length; i++) {
5889 if (data[i] !== null) {
5890 if (data[i] === "restart") {
5891 body.attrs({
5892 to: this._conn.domain,
5893 "xml:lang": "en",
5894 "xmpp:restart": "true",
5895 "xmlns:xmpp": Strophe.NS.BOSH
5896 });
5897 } else {
5898 body.cnode(data[i]).up();
5899 }
5900 }
5901 }
5902 delete this._conn._data;
5903 this._conn._data = [];
5904 this._requests.push(
5905 new Strophe.Request(body.tree(),
5906 this._onRequestStateChange.bind(
5907 this, this._conn._dataRecv.bind(this._conn)),
5908 body.tree().getAttribute("rid")));
5909 this._throttledRequestHandler();
5910 }
5911
5912 if (this._requests.length > 0) {
5913 var time_elapsed = this._requests[0].age();
5914 if (this._requests[0].dead !== null) {
5915 if (this._requests[0].timeDead() >
5916 Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait)) {
5917 this._throttledRequestHandler();
5918 }
5919 }
5920
5921 if (time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait)) {
5922 Strophe.warn("Request " +
5923 this._requests[0].id +
5924 " timed out, over " + Math.floor(Strophe.TIMEOUT * this.wait) +
5925 " seconds since last activity");
5926 this._throttledRequestHandler();
5927 }
5928 }
5929 },
5930
5931 /** PrivateFunction: _getRequestStatus
5932 *
5933 * Returns the HTTP status code from a Strophe.Request
5934 *
5935 * Parameters:
5936 * (Strophe.Request) req - The Strophe.Request instance.
5937 * (Integer) def - The default value that should be returned if no
5938 * status value was found.
5939 */
5940 _getRequestStatus: function (req, def) {
5941 var reqStatus;
5942 if (req.xhr.readyState === 4) {
5943 try {
5944 reqStatus = req.xhr.status;
5945 } catch (e) {
5946 // ignore errors from undefined status attribute. Works
5947 // around a browser bug
5948 Strophe.error(
5949 "Caught an error while retrieving a request's status, " +
5950 "reqStatus: " + reqStatus);
5951 }
5952 }
5953 if (typeof(reqStatus) === "undefined") {
5954 reqStatus = typeof def === 'number' ? def : 0;
5955 }
5956 return reqStatus;
5957 },
5958
5959 /** PrivateFunction: _onRequestStateChange
5960 * _Private_ handler for Strophe.Request state changes.
5961 *
5962 * This function is called when the XMLHttpRequest readyState changes.
5963 * It contains a lot of error handling logic for the many ways that
5964 * requests can fail, and calls the request callback when requests
5965 * succeed.
5966 *
5967 * Parameters:
5968 * (Function) func - The handler for the request.
5969 * (Strophe.Request) req - The request that is changing readyState.
5970 */
5971 _onRequestStateChange: function (func, req) {
5972 Strophe.debug("request id "+req.id+"."+req.sends+
5973 " state changed to "+req.xhr.readyState);
5974 if (req.abort) {
5975 req.abort = false;
5976 return;
5977 }
5978 if (req.xhr.readyState !== 4) {
5979 // The request is not yet complete
5980 return;
5981 }
5982 var reqStatus = this._getRequestStatus(req);
5983 this.lastResponseHeaders = req.xhr.getAllResponseHeaders();
5984 if (this.disconnecting && reqStatus >= 400) {
5985 this._hitError(reqStatus);
5986 this._callProtocolErrorHandlers(req);
5987 return;
5988 }
5989
5990 var valid_request = reqStatus > 0 && reqStatus < 500;
5991 var too_many_retries = req.sends > this._conn.maxRetries;
5992 if (valid_request || too_many_retries) {
5993 // remove from internal queue
5994 this._removeRequest(req);
5995 Strophe.debug("request id "+req.id+" should now be removed");
5996 }
5997
5998 if (reqStatus === 200) {
5999 // request succeeded
6000 var reqIs0 = (this._requests[0] === req);
6001 var reqIs1 = (this._requests[1] === req);
6002 // if request 1 finished, or request 0 finished and request
6003 // 1 is over Strophe.SECONDARY_TIMEOUT seconds old, we need to
6004 // restart the other - both will be in the first spot, as the
6005 // completed request has been removed from the queue already
6006 if (reqIs1 ||
6007 (reqIs0 && this._requests.length > 0 &&
6008 this._requests[0].age() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait))) {
6009 this._restartRequest(0);
6010 }
6011 this._conn.nextValidRid(Number(req.rid) + 1);
6012 Strophe.debug("request id "+req.id+"."+req.sends+" got 200");
6013 func(req); // call handler
6014 this.errors = 0;
6015 } else if (reqStatus === 0 ||
6016 (reqStatus >= 400 && reqStatus < 600) ||
6017 reqStatus >= 12000) {
6018 // request failed
6019 Strophe.error("request id "+req.id+"."+req.sends+" error "+reqStatus+" happened");
6020 this._hitError(reqStatus);
6021 this._callProtocolErrorHandlers(req);
6022 if (reqStatus >= 400 && reqStatus < 500) {
6023 this._conn._changeConnectStatus(Strophe.Status.DISCONNECTING, null);
6024 this._conn._doDisconnect();
6025 }
6026 } else {
6027 Strophe.error("request id "+req.id+"."+req.sends+" error "+reqStatus+" happened");
6028 }
6029
6030 if (!valid_request && !too_many_retries) {
6031 this._throttledRequestHandler();
6032 } else if (too_many_retries && !this._conn.connected) {
6033 this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, "giving-up");
6034 }
6035 },
6036
6037 /** PrivateFunction: _processRequest
6038 * _Private_ function to process a request in the queue.
6039 *
6040 * This function takes requests off the queue and sends them and
6041 * restarts dead requests.
6042 *
6043 * Parameters:
6044 * (Integer) i - The index of the request in the queue.
6045 */
6046 _processRequest: function (i) {
6047 var self = this;
6048 var req = this._requests[i];
6049 var reqStatus = this._getRequestStatus(req, -1);
6050
6051 // make sure we limit the number of retries
6052 if (req.sends > this._conn.maxRetries) {
6053 this._conn._onDisconnectTimeout();
6054 return;
6055 }
6056
6057 var time_elapsed = req.age();
6058 var primaryTimeout = (!isNaN(time_elapsed) &&
6059 time_elapsed > Math.floor(Strophe.TIMEOUT * this.wait));
6060 var secondaryTimeout = (req.dead !== null &&
6061 req.timeDead() > Math.floor(Strophe.SECONDARY_TIMEOUT * this.wait));
6062 var requestCompletedWithServerError = (req.xhr.readyState === 4 &&
6063 (reqStatus < 1 || reqStatus >= 500));
6064 if (primaryTimeout || secondaryTimeout ||
6065 requestCompletedWithServerError) {
6066 if (secondaryTimeout) {
6067 Strophe.error("Request " + this._requests[i].id +
6068 " timed out (secondary), restarting");
6069 }
6070 req.abort = true;
6071 req.xhr.abort();
6072 // setting to null fails on IE6, so set to empty function
6073 req.xhr.onreadystatechange = function () {};
6074 this._requests[i] = new Strophe.Request(req.xmlData,
6075 req.origFunc,
6076 req.rid,
6077 req.sends);
6078 req = this._requests[i];
6079 }
6080
6081 if (req.xhr.readyState === 0) {
6082 Strophe.debug("request id "+req.id+"."+req.sends+" posting");
6083
6084 try {
6085 var contentType = this._conn.options.contentType || "text/xml; charset=utf-8";
6086 req.xhr.open("POST", this._conn.service, this._conn.options.sync ? false : true);
6087 if (typeof req.xhr.setRequestHeader !== 'undefined') {
6088 // IE9 doesn't have setRequestHeader
6089 req.xhr.setRequestHeader("Content-Type", contentType);
6090 }
6091 if (this._conn.options.withCredentials) {
6092 req.xhr.withCredentials = true;
6093 }
6094 } catch (e2) {
6095 Strophe.error("XHR open failed: " + e2.toString());
6096 if (!this._conn.connected) {
6097 this._conn._changeConnectStatus(
6098 Strophe.Status.CONNFAIL, "bad-service");
6099 }
6100 this._conn.disconnect();
6101 return;
6102 }
6103
6104 // Fires the XHR request -- may be invoked immediately
6105 // or on a gradually expanding retry window for reconnects
6106 var sendFunc = function () {
6107 req.date = new Date();
6108 if (self._conn.options.customHeaders){
6109 var headers = self._conn.options.customHeaders;
6110 for (var header in headers) {
6111 if (headers.hasOwnProperty(header)) {
6112 req.xhr.setRequestHeader(header, headers[header]);
6113 }
6114 }
6115 }
6116 req.xhr.send(req.data);
6117 };
6118
6119 // Implement progressive backoff for reconnects --
6120 // First retry (send === 1) should also be instantaneous
6121 if (req.sends > 1) {
6122 // Using a cube of the retry number creates a nicely
6123 // expanding retry window
6124 var backoff = Math.min(Math.floor(Strophe.TIMEOUT * this.wait),
6125 Math.pow(req.sends, 3)) * 1000;
6126 setTimeout(function() {
6127 // XXX: setTimeout should be called only with function expressions (23974bc1)
6128 sendFunc();
6129 }, backoff);
6130 } else {
6131 sendFunc();
6132 }
6133
6134 req.sends++;
6135
6136 if (this._conn.xmlOutput !== Strophe.Connection.prototype.xmlOutput) {
6137 if (req.xmlData.nodeName === this.strip && req.xmlData.childNodes.length) {
6138 this._conn.xmlOutput(req.xmlData.childNodes[0]);
6139 } else {
6140 this._conn.xmlOutput(req.xmlData);
6141 }
6142 }
6143 if (this._conn.rawOutput !== Strophe.Connection.prototype.rawOutput) {
6144 this._conn.rawOutput(req.data);
6145 }
6146 } else {
6147 Strophe.debug("_processRequest: " +
6148 (i === 0 ? "first" : "second") +
6149 " request has readyState of " +
6150 req.xhr.readyState);
6151 }
6152 },
6153
6154 /** PrivateFunction: _removeRequest
6155 * _Private_ function to remove a request from the queue.
6156 *
6157 * Parameters:
6158 * (Strophe.Request) req - The request to remove.
6159 */
6160 _removeRequest: function (req) {
6161 Strophe.debug("removing request");
6162 var i;
6163 for (i = this._requests.length - 1; i >= 0; i--) {
6164 if (req === this._requests[i]) {
6165 this._requests.splice(i, 1);
6166 }
6167 }
6168 // IE6 fails on setting to null, so set to empty function
6169 req.xhr.onreadystatechange = function () {};
6170 this._throttledRequestHandler();
6171 },
6172
6173 /** PrivateFunction: _restartRequest
6174 * _Private_ function to restart a request that is presumed dead.
6175 *
6176 * Parameters:
6177 * (Integer) i - The index of the request in the queue.
6178 */
6179 _restartRequest: function (i) {
6180 var req = this._requests[i];
6181 if (req.dead === null) {
6182 req.dead = new Date();
6183 }
6184
6185 this._processRequest(i);
6186 },
6187
6188 /** PrivateFunction: _reqToData
6189 * _Private_ function to get a stanza out of a request.
6190 *
6191 * Tries to extract a stanza out of a Request Object.
6192 * When this fails the current connection will be disconnected.
6193 *
6194 * Parameters:
6195 * (Object) req - The Request.
6196 *
6197 * Returns:
6198 * The stanza that was passed.
6199 */
6200 _reqToData: function (req) {
6201 try {
6202 return req.getResponse();
6203 } catch (e) {
6204 if (e !== "parsererror") { throw e; }
6205 this._conn.disconnect("strophe-parsererror");
6206 }
6207 },
6208
6209 /** PrivateFunction: _sendTerminate
6210 * _Private_ function to send initial disconnect sequence.
6211 *
6212 * This is the first step in a graceful disconnect. It sends
6213 * the BOSH server a terminate body and includes an unavailable
6214 * presence if authentication has completed.
6215 */
6216 _sendTerminate: function (pres) {
6217 Strophe.info("_sendTerminate was called");
6218 var body = this._buildBody().attrs({type: "terminate"});
6219 if (pres) {
6220 body.cnode(pres.tree());
6221 }
6222 var req = new Strophe.Request(
6223 body.tree(),
6224 this._onRequestStateChange.bind(
6225 this, this._conn._dataRecv.bind(this._conn)),
6226 body.tree().getAttribute("rid")
6227 );
6228 this._requests.push(req);
6229 this._throttledRequestHandler();
6230 },
6231
6232 /** PrivateFunction: _send
6233 * _Private_ part of the Connection.send function for BOSH
6234 *
6235 * Just triggers the RequestHandler to send the messages that are in the queue
6236 */
6237 _send: function () {
6238 clearTimeout(this._conn._idleTimeout);
6239 this._throttledRequestHandler();
6240
6241 // XXX: setTimeout should be called only with function expressions (23974bc1)
6242 this._conn._idleTimeout = setTimeout(function() {
6243 this._onIdle();
6244 }.bind(this._conn), 100);
6245 },
6246
6247 /** PrivateFunction: _sendRestart
6248 *
6249 * Send an xmpp:restart stanza.
6250 */
6251 _sendRestart: function () {
6252 this._throttledRequestHandler();
6253 clearTimeout(this._conn._idleTimeout);
6254 },
6255
6256 /** PrivateFunction: _throttledRequestHandler
6257 * _Private_ function to throttle requests to the connection window.
6258 *
6259 * This function makes sure we don't send requests so fast that the
6260 * request ids overflow the connection window in the case that one
6261 * request died.
6262 */
6263 _throttledRequestHandler: function () {
6264 if (!this._requests) {
6265 Strophe.debug("_throttledRequestHandler called with " +
6266 "undefined requests");
6267 } else {
6268 Strophe.debug("_throttledRequestHandler called with " +
6269 this._requests.length + " requests");
6270 }
6271
6272 if (!this._requests || this._requests.length === 0) {
6273 return;
6274 }
6275
6276 if (this._requests.length > 0) {
6277 this._processRequest(0);
6278 }
6279
6280 if (this._requests.length > 1 &&
6281 Math.abs(this._requests[0].rid -
6282 this._requests[1].rid) < this.window) {
6283 this._processRequest(1);
6284 }
6285 }
6286};
6287return Strophe;
6288}));
6289
6290/*
6291 This program is distributed under the terms of the MIT license.
6292 Please see the LICENSE file for details.
6293
6294 Copyright 2006-2008, OGG, LLC
6295*/
6296
6297/* jshint undef: true, unused: true:, noarg: true, latedef: true */
6298/* global define, window, clearTimeout, WebSocket, DOMParser, Strophe, $build */
6299
6300(function (root, factory) {
6301 if (typeof define === 'function' && define.amd) {
6302 define('strophe-websocket',['strophe-core'], function (core) {
6303 return factory(
6304 core.Strophe,
6305 core.$build
6306 );
6307 });
6308 } else if (typeof exports === 'object') {
6309 var core = require('./core');
6310
6311 module.exports = factory(core.Strophe, core.$build);
6312 } else {
6313 // Browser globals
6314 return factory(Strophe, $build);
6315 }
6316}(this, function (Strophe, $build) {
6317
6318/** Class: Strophe.WebSocket
6319 * _Private_ helper class that handles WebSocket Connections
6320 *
6321 * The Strophe.WebSocket class is used internally by Strophe.Connection
6322 * to encapsulate WebSocket sessions. It is not meant to be used from user's code.
6323 */
6324
6325/** File: websocket.js
6326 * A JavaScript library to enable XMPP over Websocket in Strophejs.
6327 *
6328 * This file implements XMPP over WebSockets for Strophejs.
6329 * If a Connection is established with a Websocket url (ws://...)
6330 * Strophe will use WebSockets.
6331 * For more information on XMPP-over-WebSocket see RFC 7395:
6332 * http://tools.ietf.org/html/rfc7395
6333 *
6334 * WebSocket support implemented by Andreas Guth (andreas.guth@rwth-aachen.de)
6335 */
6336
6337/** PrivateConstructor: Strophe.Websocket
6338 * Create and initialize a Strophe.WebSocket object.
6339 * Currently only sets the connection Object.
6340 *
6341 * Parameters:
6342 * (Strophe.Connection) connection - The Strophe.Connection that will use WebSockets.
6343 *
6344 * Returns:
6345 * A new Strophe.WebSocket object.
6346 */
6347Strophe.Websocket = function(connection) {
6348 this._conn = connection;
6349 this.strip = "wrapper";
6350
6351 var service = connection.service;
6352 if (service.indexOf("ws:") !== 0 && service.indexOf("wss:") !== 0) {
6353 // If the service is not an absolute URL, assume it is a path and put the absolute
6354 // URL together from options, current URL and the path.
6355 var new_service = "";
6356
6357 if (connection.options.protocol === "ws" && window.location.protocol !== "https:") {
6358 new_service += "ws";
6359 } else {
6360 new_service += "wss";
6361 }
6362
6363 new_service += "://" + window.location.host;
6364
6365 if (service.indexOf("/") !== 0) {
6366 new_service += window.location.pathname + service;
6367 } else {
6368 new_service += service;
6369 }
6370
6371 connection.service = new_service;
6372 }
6373};
6374
6375Strophe.Websocket.prototype = {
6376 /** PrivateFunction: _buildStream
6377 * _Private_ helper function to generate the <stream> start tag for WebSockets
6378 *
6379 * Returns:
6380 * A Strophe.Builder with a <stream> element.
6381 */
6382 _buildStream: function () {
6383 return $build("open", {
6384 "xmlns": Strophe.NS.FRAMING,
6385 "to": this._conn.domain,
6386 "version": '1.0'
6387 });
6388 },
6389
6390 /** PrivateFunction: _check_streamerror
6391 * _Private_ checks a message for stream:error
6392 *
6393 * Parameters:
6394 * (Strophe.Request) bodyWrap - The received stanza.
6395 * connectstatus - The ConnectStatus that will be set on error.
6396 * Returns:
6397 * true if there was a streamerror, false otherwise.
6398 */
6399 _check_streamerror: function (bodyWrap, connectstatus) {
6400 var errors;
6401 if (bodyWrap.getElementsByTagNameNS) {
6402 errors = bodyWrap.getElementsByTagNameNS(Strophe.NS.STREAM, "error");
6403 } else {
6404 errors = bodyWrap.getElementsByTagName("stream:error");
6405 }
6406 if (errors.length === 0) {
6407 return false;
6408 }
6409 var error = errors[0];
6410
6411 var condition = "";
6412 var text = "";
6413
6414 var ns = "urn:ietf:params:xml:ns:xmpp-streams";
6415 for (var i = 0; i < error.childNodes.length; i++) {
6416 var e = error.childNodes[i];
6417 if (e.getAttribute("xmlns") !== ns) {
6418 break;
6419 } if (e.nodeName === "text") {
6420 text = e.textContent;
6421 } else {
6422 condition = e.nodeName;
6423 }
6424 }
6425
6426 var errorString = "WebSocket stream error: ";
6427
6428 if (condition) {
6429 errorString += condition;
6430 } else {
6431 errorString += "unknown";
6432 }
6433
6434 if (text) {
6435 errorString += " - " + text;
6436 }
6437
6438 Strophe.error(errorString);
6439
6440 // close the connection on stream_error
6441 this._conn._changeConnectStatus(connectstatus, condition);
6442 this._conn._doDisconnect();
6443 return true;
6444 },
6445
6446 /** PrivateFunction: _reset
6447 * Reset the connection.
6448 *
6449 * This function is called by the reset function of the Strophe Connection.
6450 * Is not needed by WebSockets.
6451 */
6452 _reset: function () {
6453 return;
6454 },
6455
6456 /** PrivateFunction: _connect
6457 * _Private_ function called by Strophe.Connection.connect
6458 *
6459 * Creates a WebSocket for a connection and assigns Callbacks to it.
6460 * Does nothing if there already is a WebSocket.
6461 */
6462 _connect: function () {
6463 // Ensure that there is no open WebSocket from a previous Connection.
6464 this._closeSocket();
6465
6466 // Create the new WobSocket
6467 this.socket = new WebSocket(this._conn.service, "xmpp");
6468 this.socket.onopen = this._onOpen.bind(this);
6469 this.socket.onerror = this._onError.bind(this);
6470 this.socket.onclose = this._onClose.bind(this);
6471 this.socket.onmessage = this._connect_cb_wrapper.bind(this);
6472 },
6473
6474 /** PrivateFunction: _connect_cb
6475 * _Private_ function called by Strophe.Connection._connect_cb
6476 *
6477 * checks for stream:error
6478 *
6479 * Parameters:
6480 * (Strophe.Request) bodyWrap - The received stanza.
6481 */
6482 _connect_cb: function(bodyWrap) {
6483 var error = this._check_streamerror(bodyWrap, Strophe.Status.CONNFAIL);
6484 if (error) {
6485 return Strophe.Status.CONNFAIL;
6486 }
6487 },
6488
6489 /** PrivateFunction: _handleStreamStart
6490 * _Private_ function that checks the opening <open /> tag for errors.
6491 *
6492 * Disconnects if there is an error and returns false, true otherwise.
6493 *
6494 * Parameters:
6495 * (Node) message - Stanza containing the <open /> tag.
6496 */
6497 _handleStreamStart: function(message) {
6498 var error = false;
6499
6500 // Check for errors in the <open /> tag
6501 var ns = message.getAttribute("xmlns");
6502 if (typeof ns !== "string") {
6503 error = "Missing xmlns in <open />";
6504 } else if (ns !== Strophe.NS.FRAMING) {
6505 error = "Wrong xmlns in <open />: " + ns;
6506 }
6507
6508 var ver = message.getAttribute("version");
6509 if (typeof ver !== "string") {
6510 error = "Missing version in <open />";
6511 } else if (ver !== "1.0") {
6512 error = "Wrong version in <open />: " + ver;
6513 }
6514
6515 if (error) {
6516 this._conn._changeConnectStatus(Strophe.Status.CONNFAIL, error);
6517 this._conn._doDisconnect();
6518 return false;
6519 }
6520
6521 return true;
6522 },
6523
6524 /** PrivateFunction: _connect_cb_wrapper
6525 * _Private_ function that handles the first connection messages.
6526 *
6527 * On receiving an opening stream tag this callback replaces itself with the real
6528 * message handler. On receiving a stream error the connection is terminated.
6529 */
6530 _connect_cb_wrapper: function(message) {
6531 if (message.data.indexOf("<open ") === 0 || message.data.indexOf("<?xml") === 0) {
6532 // Strip the XML Declaration, if there is one
6533 var data = message.data.replace(/^(<\?.*?\?>\s*)*/, "");
6534 if (data === '') return;
6535
6536 var streamStart = new DOMParser().parseFromString(data, "text/xml").documentElement;
6537 this._conn.xmlInput(streamStart);
6538 this._conn.rawInput(message.data);
6539
6540 //_handleStreamSteart will check for XML errors and disconnect on error
6541 if (this._handleStreamStart(streamStart)) {
6542 //_connect_cb will check for stream:error and disconnect on error
6543 this._connect_cb(streamStart);
6544 }
6545 } else if (message.data.indexOf("<close ") === 0) { // <close xmlns="urn:ietf:params:xml:ns:xmpp-framing />
6546 this._conn.rawInput(message.data);
6547 this._conn.xmlInput(message);
6548 var see_uri = message.getAttribute("see-other-uri");
6549 if (see_uri) {
6550 this._conn._changeConnectStatus(
6551 Strophe.Status.REDIRECT,
6552 "Received see-other-uri, resetting connection"
6553 );
6554 this._conn.reset();
6555 this._conn.service = see_uri;
6556 this._connect();
6557 } else {
6558 this._conn._changeConnectStatus(
6559 Strophe.Status.CONNFAIL,
6560 "Received closing stream"
6561 );
6562 this._conn._doDisconnect();
6563 }
6564 } else {
6565 var string = this._streamWrap(message.data);
6566 var elem = new DOMParser().parseFromString(string, "text/xml").documentElement;
6567 this.socket.onmessage = this._onMessage.bind(this);
6568 this._conn._connect_cb(elem, null, message.data);
6569 }
6570 },
6571
6572 /** PrivateFunction: _disconnect
6573 * _Private_ function called by Strophe.Connection.disconnect
6574 *
6575 * Disconnects and sends a last stanza if one is given
6576 *
6577 * Parameters:
6578 * (Request) pres - This stanza will be sent before disconnecting.
6579 */
6580 _disconnect: function (pres) {
6581 if (this.socket && this.socket.readyState !== WebSocket.CLOSED) {
6582 if (pres) {
6583 this._conn.send(pres);
6584 }
6585 var close = $build("close", { "xmlns": Strophe.NS.FRAMING });
6586 this._conn.xmlOutput(close.tree());
6587 var closeString = Strophe.serialize(close);
6588 this._conn.rawOutput(closeString);
6589 try {
6590 this.socket.send(closeString);
6591 } catch (e) {
6592 Strophe.info("Couldn't send <close /> tag.");
6593 }
6594 }
6595 this._conn._doDisconnect();
6596 },
6597
6598 /** PrivateFunction: _doDisconnect
6599 * _Private_ function to disconnect.
6600 *
6601 * Just closes the Socket for WebSockets
6602 */
6603 _doDisconnect: function () {
6604 Strophe.info("WebSockets _doDisconnect was called");
6605 this._closeSocket();
6606 },
6607
6608 /** PrivateFunction _streamWrap
6609 * _Private_ helper function to wrap a stanza in a <stream> tag.
6610 * This is used so Strophe can process stanzas from WebSockets like BOSH
6611 */
6612 _streamWrap: function (stanza) {
6613 return "<wrapper>" + stanza + '</wrapper>';
6614 },
6615
6616
6617 /** PrivateFunction: _closeSocket
6618 * _Private_ function to close the WebSocket.
6619 *
6620 * Closes the socket if it is still open and deletes it
6621 */
6622 _closeSocket: function () {
6623 if (this.socket) { try {
6624 this.socket.onerror = null;
6625 this.socket.close();
6626 } catch (e) {} }
6627 this.socket = null;
6628 },
6629
6630 /** PrivateFunction: _emptyQueue
6631 * _Private_ function to check if the message queue is empty.
6632 *
6633 * Returns:
6634 * True, because WebSocket messages are send immediately after queueing.
6635 */
6636 _emptyQueue: function () {
6637 return true;
6638 },
6639
6640 /** PrivateFunction: _onClose
6641 * _Private_ function to handle websockets closing.
6642 *
6643 * Nothing to do here for WebSockets
6644 */
6645 _onClose: function(e) {
6646 if(this._conn.connected && !this._conn.disconnecting) {
6647 Strophe.error("Websocket closed unexpectedly");
6648 this._conn._doDisconnect();
6649 } else if (e && e.code === 1006 && !this._conn.connected && this.socket) {
6650 // in case the onError callback was not called (Safari 10 does not
6651 // call onerror when the initial connection fails) we need to
6652 // dispatch a CONNFAIL status update to be consistent with the
6653 // behavior on other browsers.
6654 Strophe.error("Websocket closed unexcectedly");
6655 this._conn._changeConnectStatus(
6656 Strophe.Status.CONNFAIL,
6657 "The WebSocket connection could not be established or was disconnected."
6658 );
6659 this._conn._doDisconnect();
6660 } else {
6661 Strophe.info("Websocket closed");
6662 }
6663 },
6664
6665 /** PrivateFunction: _onDisconnectTimeout
6666 * _Private_ timeout handler for handling non-graceful disconnection.
6667 *
6668 * This does nothing for WebSockets
6669 */
6670 _onDisconnectTimeout: function () {},
6671
6672 /** PrivateFunction: _abortAllRequests
6673 * _Private_ helper function that makes sure all pending requests are aborted.
6674 */
6675 _abortAllRequests: function () {},
6676
6677 /** PrivateFunction: _onError
6678 * _Private_ function to handle websockets errors.
6679 *
6680 * Parameters:
6681 * (Object) error - The websocket error.
6682 */
6683 _onError: function(error) {
6684 Strophe.error("Websocket error " + error);
6685 this._conn._changeConnectStatus(
6686 Strophe.Status.CONNFAIL,
6687 "The WebSocket connection could not be established or was disconnected."
6688 );
6689 this._disconnect();
6690 },
6691
6692 /** PrivateFunction: _onIdle
6693 * _Private_ function called by Strophe.Connection._onIdle
6694 *
6695 * sends all queued stanzas
6696 */
6697 _onIdle: function () {
6698 var data = this._conn._data;
6699 if (data.length > 0 && !this._conn.paused) {
6700 for (var i = 0; i < data.length; i++) {
6701 if (data[i] !== null) {
6702 var stanza, rawStanza;
6703 if (data[i] === "restart") {
6704 stanza = this._buildStream().tree();
6705 } else {
6706 stanza = data[i];
6707 }
6708 rawStanza = Strophe.serialize(stanza);
6709 this._conn.xmlOutput(stanza);
6710 this._conn.rawOutput(rawStanza);
6711 this.socket.send(rawStanza);
6712 }
6713 }
6714 this._conn._data = [];
6715 }
6716 },
6717
6718 /** PrivateFunction: _onMessage
6719 * _Private_ function to handle websockets messages.
6720 *
6721 * This function parses each of the messages as if they are full documents.
6722 * [TODO : We may actually want to use a SAX Push parser].
6723 *
6724 * Since all XMPP traffic starts with
6725 * <stream:stream version='1.0'
6726 * xml:lang='en'
6727 * xmlns='jabber:client'
6728 * xmlns:stream='http://etherx.jabber.org/streams'
6729 * id='3697395463'
6730 * from='SERVER'>
6731 *
6732 * The first stanza will always fail to be parsed.
6733 *
6734 * Additionally, the seconds stanza will always be <stream:features> with
6735 * the stream NS defined in the previous stanza, so we need to 'force'
6736 * the inclusion of the NS in this stanza.
6737 *
6738 * Parameters:
6739 * (string) message - The websocket message.
6740 */
6741 _onMessage: function(message) {
6742 var elem, data;
6743 // check for closing stream
6744 var close = '<close xmlns="urn:ietf:params:xml:ns:xmpp-framing" />';
6745 if (message.data === close) {
6746 this._conn.rawInput(close);
6747 this._conn.xmlInput(message);
6748 if (!this._conn.disconnecting) {
6749 this._conn._doDisconnect();
6750 }
6751 return;
6752 } else if (message.data.search("<open ") === 0) {
6753 // This handles stream restarts
6754 elem = new DOMParser().parseFromString(message.data, "text/xml").documentElement;
6755 if (!this._handleStreamStart(elem)) {
6756 return;
6757 }
6758 } else {
6759 data = this._streamWrap(message.data);
6760 elem = new DOMParser().parseFromString(data, "text/xml").documentElement;
6761 }
6762
6763 if (this._check_streamerror(elem, Strophe.Status.ERROR)) {
6764 return;
6765 }
6766
6767 //handle unavailable presence stanza before disconnecting
6768 if (this._conn.disconnecting &&
6769 elem.firstChild.nodeName === "presence" &&
6770 elem.firstChild.getAttribute("type") === "unavailable") {
6771 this._conn.xmlInput(elem);
6772 this._conn.rawInput(Strophe.serialize(elem));
6773 // if we are already disconnecting we will ignore the unavailable stanza and
6774 // wait for the </stream:stream> tag before we close the connection
6775 return;
6776 }
6777 this._conn._dataRecv(elem, message.data);
6778 },
6779
6780 /** PrivateFunction: _onOpen
6781 * _Private_ function to handle websockets connection setup.
6782 *
6783 * The opening stream tag is sent here.
6784 */
6785 _onOpen: function() {
6786 Strophe.info("Websocket open");
6787 var start = this._buildStream();
6788 this._conn.xmlOutput(start.tree());
6789
6790 var startString = Strophe.serialize(start);
6791 this._conn.rawOutput(startString);
6792 this.socket.send(startString);
6793 },
6794
6795 /** PrivateFunction: _reqToData
6796 * _Private_ function to get a stanza out of a request.
6797 *
6798 * WebSockets don't use requests, so the passed argument is just returned.
6799 *
6800 * Parameters:
6801 * (Object) stanza - The stanza.
6802 *
6803 * Returns:
6804 * The stanza that was passed.
6805 */
6806 _reqToData: function (stanza) {
6807 return stanza;
6808 },
6809
6810 /** PrivateFunction: _send
6811 * _Private_ part of the Connection.send function for WebSocket
6812 *
6813 * Just flushes the messages that are in the queue
6814 */
6815 _send: function () {
6816 this._conn.flush();
6817 },
6818
6819 /** PrivateFunction: _sendRestart
6820 *
6821 * Send an xmpp:restart stanza.
6822 */
6823 _sendRestart: function () {
6824 clearTimeout(this._conn._idleTimeout);
6825 this._conn._onIdle.bind(this._conn)();
6826 }
6827};
6828return Strophe;
6829}));
6830
6831(function(root){
6832 if(typeof define === 'function' && define.amd){
6833 define('strophe',[
6834 "strophe-core",
6835 "strophe-bosh",
6836 "strophe-websocket"
6837 ], function (wrapper) {
6838 return wrapper;
6839 });
6840 } else if (typeof exports === 'object') {
6841 var core = require('./core');
6842 require('./bosh');
6843 require('./websocket');
6844 module.exports = core;
6845 }
6846})(this);
6847
6848
6849require(["strophe-polyfill"]);
6850/* jshint ignore:start */
6851 //The modules for your project will be inlined above
6852 //this snippet. Ask almond to synchronously require the
6853 //module value for 'main' here and return it as the
6854 //value to use for the public API for the built file.
6855 return require('strophe');
6856}));
6857/* jshint ignore:end */
6858
6859 // End thirdparty/strophe/strophe.js
6860
6861 // Start thirdparty/strophe/strophe.pubsub.min.js
6862Strophe.addConnectionPlugin("pubsub",{_connection:null,init:function(conn){this._connection=conn;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(jid,service,node,options,call_back){var iqid=this._connection.getUniqueId("pubsubcreatenode");var iq=$iq({from:jid,to:service,type:"set",id:iqid});var c_options=Strophe.xmlElement("configure",[]);var x=Strophe.xmlElement("x",[["xmlns","jabber:x:data"]]);var form_field=Strophe.xmlElement("field",[["var","FORM_TYPE"],["type","hidden"]]);var value=Strophe.xmlElement("value",[]);var text=Strophe.xmlTextNode(Strophe.NS.PUBSUB+"#node_config");value.appendChild(text);form_field.appendChild(value);x.appendChild(form_field);for(var i in options){var val=options[i];x.appendChild(val)}if(options.length&&options.length!=0){c_options.appendChild(x)}iq.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).c("create",{node:node}).up().cnode(c_options);this._connection.addHandler(call_back,null,"iq",null,iqid,null);this._connection.send(iq.tree());return iqid},subscribe:function(jid,service,node,options,event_cb,call_back){var subid=this._connection.getUniqueId("subscribenode");var sub_options=Strophe.xmlElement("options",[]);var x=Strophe.xmlElement("x",[["xmlns","jabber:x:data"]]);var form_field=Strophe.xmlElement("field",[["var","FORM_TYPE"],["type","hidden"]]);var value=Strophe.xmlElement("value",[]);var text=Strophe.xmlTextNode(Strophe.NS.PUBSUB_SUBSCRIBE_OPTIONS);value.appendChild(text);form_field.appendChild(value);x.appendChild(form_field);var sub=$iq({from:jid,to:service,type:"set",id:subid});if(options&&options.length&&options.length!==0){for(var i=0;i<options.length;i++){var val=options[i];x.appendChild(val)}sub_options.appendChild(x);sub.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).c("subscribe",{node:node,jid:jid}).up().cnode(sub_options)}else{sub.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).c("subscribe",{node:node,jid:jid})}this._connection.addHandler(call_back,null,"iq",null,subid,null);this._connection.addHandler(event_cb,null,"message",null,null,null);this._connection.send(sub.tree());return subid},unsubscribe:function(jid,service,node,call_back){var subid=this._connection.getUniqueId("unsubscribenode");var sub=$iq({from:jid,to:service,type:"set",id:subid});sub.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).c("unsubscribe",{node:node,jid:jid});this._connection.addHandler(call_back,null,"iq",null,subid,null);this._connection.send(sub.tree());return subid},publish:function(jid,service,node,items,call_back){var pubid=this._connection.getUniqueId("publishnode");var publish_elem=Strophe.xmlElement("publish",[["node",node],["jid",jid]]);for(var i in items){var item=Strophe.xmlElement("item",[]);var entry=Strophe.xmlElement("entry",[]);var t=Strophe.xmlTextNode(items[i]);entry.appendChild(t);item.appendChild(entry);publish_elem.appendChild(item)}var pub=$iq({from:jid,to:service,type:"set",id:pubid});pub.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).cnode(publish_elem);this._connection.addHandler(call_back,null,"iq",null,pubid,null);this._connection.send(pub.tree());return pubid},items:function(jid,service,node,ok_callback,error_back){var pub=$iq({from:jid,to:service,type:"get"});pub.c("pubsub",{xmlns:Strophe.NS.PUBSUB}).c("items",{node:node});return this._connection.sendIQ(pub.tree(),ok_callback,error_back)}});
6863
6864 // End thirdparty/strophe/strophe.pubsub.min.js
6865
6866 // Start thirdparty/strophe/strophe.ping.js
6867/*
6868* Based on Ping Strophejs plugins (https://github.com/metajack/strophejs-plugins/tree/master/ping)
6869* This plugin is distributed under the terms of the MIT licence.
6870* Please see the LICENCE file for details.
6871*
6872* Copyright (c) Markus Kohlhase, 2010
6873* Refactored by Pavel Lang, 2011
6874* AMD Support added by Thierry
6875*/
6876/**
6877* File: strophe.ping.js
6878* A Strophe plugin for XMPP Ping ( http://xmpp.org/extensions/xep-0199.html )
6879*/
6880(function (root, factory) {
6881 if (typeof define === 'function' && define.amd) {
6882 // AMD. Register as an anonymous module.
6883 define([
6884 "strophe.js"
6885 ], function (Strophe) {
6886 factory(
6887 Strophe.Strophe,
6888 Strophe.$build,
6889 Strophe.$iq ,
6890 Strophe.$msg,
6891 Strophe.$pres
6892 );
6893 return Strophe;
6894 });
6895 } else {
6896 // Browser globals
6897 factory(
6898 root.Strophe,
6899 root.$build,
6900 root.$iq ,
6901 root.$msg,
6902 root.$pres
6903 );
6904 }
6905}(this, function (Strophe, $build, $iq, $msg, $pres) {
6906 Strophe.addConnectionPlugin('ping', {
6907 _c: null,
6908
6909 // called by the Strophe.Connection constructor
6910 init: function(conn) {
6911 this._c = conn;
6912 Strophe.addNamespace('PING', "urn:xmpp:ping");
6913 },
6914
6915 /**
6916 * Function: ping
6917 *
6918 * Parameters:
6919 * (String) to - The JID you want to ping
6920 * (Function) success - Callback function on success
6921 * (Function) error - Callback function on error
6922 * (Integer) timeout - Timeout in milliseconds
6923 */
6924 ping: function(jid, success, error, timeout) {
6925 var id = this._c.getUniqueId('ping');
6926 var iq = $iq({type: 'get', to: jid, id: id}).c(
6927 'ping', {xmlns: Strophe.NS.PING});
6928 this._c.sendIQ(iq, success, error, timeout);
6929 },
6930
6931 /**
6932 * Function: pong
6933 *
6934 * Parameters:
6935 * (Object) ping - The ping stanza from the server.
6936 */
6937 pong: function(ping) {
6938 var from = ping.getAttribute('from');
6939 var id = ping.getAttribute('id');
6940 var iq = $iq({type: 'result', to: from,id: id});
6941 this._c.sendIQ(iq);
6942 },
6943
6944 /**
6945 * Function: addPingHandler
6946 *
6947 * Parameters:
6948 * (Function) handler - Ping handler
6949 *
6950 * Returns:
6951 * A reference to the handler that can be used to remove it.
6952 */
6953 addPingHandler: function(handler) {
6954 return this._c.addHandler(handler, Strophe.NS.PING, "iq", "get");
6955 }
6956 });
6957}));
6958
6959
6960 // End thirdparty/strophe/strophe.ping.js
6961
6962 // Start PubSubExt.js
6963/**
6964 * @fileOverview This strophe plugin has method to retrieve all node subscriptions for
6965 * a given user.
6966 *
6967 * @author <a href="mailto:pperiasa@cisco.com">Prabhu Periasamy</a>
6968 * @name PubSubExt
6969 * @requires strophe
6970 */
6971Strophe.addConnectionPlugin("pubsubext", {
6972 _connection: null,
6973 init: function (conn) {
6974
6975 // console.log('pubsubext : init has been invoked with conn' + conn);
6976 this._connection = conn;
6977 },
6978 subscriptions: function (to, ok_callback, error_callback) {
6979 var pub = $iq(
6980 {
6981 type: 'get',
6982 to: to // 'pubsub.finesse25.autobot.cvp'
6983 }
6984 ).c('pubsub', { xmlns: 'http://jabber.org/protocol/pubsub' })
6985 .c('subscriptions');
6986
6987 // console.log('pubsubext sending a request to get subscriptions: ' + pub.tree());
6988
6989 return this._connection.sendIQ(pub.tree(), ok_callback, error_callback);
6990 }
6991});
6992
6993 // End PubSubExt.js
6994
6995 // Start EventTunnel.js
6996/**
6997 * @fileOverview The Event Tunnel resides on the Finesse system to establish
6998 * an event Websocket connection with the notification server. The idea is for
6999 * the master frame to create a hidden IFRAME pointing to the Event
7000 * Tunnel HTML page, importing this JS. Since there is a cross-domain
7001 * restriction between frames, window.postMessage is used to communicate.
7002 * This allows the Websocket connection with the strophe library to be
7003 * established within third-party containers.
7004 * @author <a href="mailto:habui@cisco.com">Harry Bui</a>
7005 * @name EventTunnel
7006 * @requires strophe, finesse.Utilities
7007 */
7008
7009/** @namespace */
7010var finesse = finesse || {};
7011
7012/**
7013 * @class
7014 * Create a Websocket connection (using strophe) with the notification server to
7015 * receive events. Events are delivered through the communication tunnel with
7016 * the parent frame. After the creation of the EventTunnel object, the init
7017 * must be called to start listening for messages from the parent frame. Websocket
7018 * connection establishment starts when the parent frame sends the credentials
7019 * (via message tunnel).
7020 * @constructor
7021 * @throws {Error} If required constructor parameter is missing.
7022 */
7023finesse.EventTunnel = function () {
7024
7025 var
7026
7027 /**
7028 * Required for scope issues
7029 * @private
7030 */
7031 _this = this,
7032
7033 /**
7034 * Flag to determine whether running in Internet Explorer. Used for disconnect logic
7035 */
7036 _isIE,
7037
7038 /**
7039 * Short reference to the Finesse utility.
7040 * @private
7041 */
7042 _util = finesse.EventTunnel.Utilities,
7043
7044 /**
7045 * The JabbwerWerx client object after connection has been made.
7046 * @private
7047 */
7048 _jwClient,
7049
7050 /**
7051 * The XMPP client object after connection has been made. (via Strophe.js)
7052 * @private
7053 */
7054 _xmppClient,
7055
7056
7057 /**
7058 * The XMPP client connection status after connection has been made.
7059 * @private
7060 */
7061 _xmppClientStatus,
7062
7063
7064 /**
7065 * The XMPP client connection RESOURCE.
7066 * @private
7067 */
7068 _xmppResource,
7069 /**
7070 * The ID of the user logged into notification server.
7071 * @private
7072 */
7073 _id,
7074
7075 /**
7076 * The resource to use for the Websocket connection.
7077 * @private
7078 */
7079 _resource,
7080
7081 /**
7082 * The domain of the XMPP server, representing the portion of the JID
7083 * following '@': userid@domain.com
7084 * @private
7085 */
7086 _xmppDomain,
7087
7088 /**
7089 * The password of the user logged into notification server.
7090 * @private
7091 */
7092 _password,
7093
7094 /**
7095 * The jid of the pubsub service on the XMPP server
7096 * @private
7097 */
7098 _pubsubDomain,
7099
7100 /**
7101 * The xmpp protocol type : websocket or BOSH
7102 * We are setting the default value as bosh so that custom clients upgrading to 12.0
7103 * will not break(backward compatibility), the custom clients will continue to work on bosh and finesse desktop
7104 * will use the desktop properties to pass the notificationType. By default finesse desktop
7105 * will connect using websocket.
7106 * @private
7107 */
7108 _notificationConnectionType="bosh",
7109
7110 /**
7111 * Internal store of strophe.PubSubNode objects, used for unload cleanup
7112 * @private
7113 */
7114 _pubsubNodes = [],
7115
7116 /**
7117 * The different types of messages that could be sent to the parent frame.
7118 * The types here should be understood by the parent frame and used to
7119 * identify how the message is formatted.
7120 * @private
7121 */
7122 _TYPES = {
7123 EVENT: 0,
7124 ID: 1,
7125 PASSWORD: 2,
7126 RESOURCEID: 3,
7127 STATUS: 4,
7128 XMPPDOMAIN: 5,
7129 PUBSUBDOMAIN: 6,
7130 SUBSCRIBE: 7,
7131 UNSUBSCRIBE: 8,
7132 PRESENCE: 9,
7133 CONNECT_REQ: 10,
7134 DISCONNECT_REQ: 11,
7135 NOTIFICATION_CONNECTION_TYPE: 12,
7136 LOGGING: 13,
7137 SUBSCRIPTIONS_REQ: 14
7138 },
7139
7140 /**
7141 * Describes the states of the strophe Websocket connection. Other than the
7142 * "loaded", these states map directly with strophe's status mapping.
7143 * @private
7144 */
7145 _STATUS = [
7146 "loaded",
7147 "connecting",
7148 "connected",
7149 "disconnected",
7150 "disconnecting",
7151 "reconnecting",
7152 "unloading"
7153 ],
7154
7155 /**
7156 * Flag to indicate whether Openfire is connected or not
7157 * @type {Boolean}
7158 * @private
7159 */
7160 _isConnected = false,
7161
7162 /**
7163 * Flag to indicate whether initial presence has been received by the XMPP
7164 * server.
7165 *
7166 * @private
7167 */
7168 _initPresence = false,
7169
7170 _pingCounter = 0,
7171
7172 _pingIntervalHandler = null,
7173
7174 /**
7175 * Communicates with the parent frame (should be the Master gadget) by
7176 * sending a message formatted as follows "type|message".
7177 * @param {Number} type
7178 * The category type of the message.
7179 * @param {String} message
7180 * The message to be sent to the parent frame.
7181 * @private
7182 */
7183 _sendMessage = function (type, message) {
7184 message = type + "|" + message;
7185 _util.sendMessage(message, parent);
7186 },
7187
7188 _log = function (msg) {
7189 _sendMessage(_TYPES.LOGGING, msg);
7190 },
7191
7192 _isInternetExplorer = function () {
7193 var ua = window.navigator.userAgent;
7194
7195 var msie = ua.indexOf('MSIE ');
7196 if (msie > 0) {
7197 // IE 10 or older => return version number
7198 return true;
7199 }
7200
7201 var trident = ua.indexOf('Trident/');
7202 if (trident > 0) {
7203 // IE 11 => return version number
7204 var rv = ua.indexOf('rv:');
7205 return true;
7206 }
7207
7208 var edge = ua.indexOf('Edge/');
7209 if (edge > 0) {
7210 // Edge (IE 12+) => return version number
7211 return true;
7212 }
7213
7214 // other browser
7215 return false;
7216 },
7217 /**
7218 * Common utility for sending event notifications through the tunnel.
7219 * @param {String} node
7220 * The node that the event was published on
7221 * @param {String} payload
7222 * The payload of the notification
7223 * @private
7224 */
7225 _sendEvent = function (node, payload) {
7226 //Since the node path matches the REST URL, URI encoding/decoding should keep it safe.
7227 node = encodeURI(node ? node : "");
7228 payload = (payload ? payload : "");
7229
7230 //We are sending the node path string concatenated before the XML update event:
7231 // '/finesse/api/User/1015<Update>...</Update>'
7232 _sendMessage(_TYPES.EVENT, node + payload);
7233 },
7234
7235
7236 /**
7237 * The Websocket event handler IMPLEMENTATION, which consumes the event and publishes it to the
7238 * parent frame.
7239 * @param {Object} event
7240 * The event object for the notification as given by strophe
7241 * @private
7242 */
7243 eventHandlerImpl = function (msgElement) {
7244
7245 if (msgElement === null) {
7246 _log("EventTunnel.eventHandlerImpl() - recieved an event with null event");
7247 return true;
7248 }
7249
7250 var messageText = msgElement.textContent,
7251 to = msgElement.getAttribute('to'),
7252 //We need to extract the node path and sent it back to MasterTunnel/MasterPublisher
7253 items = msgElement.getElementsByTagName('items')[0];
7254
7255 if (messageText === null || to === null || items === null) {
7256 _log("EventTunnel.eventHandlerImpl() - - received an event with null text/to/items");
7257 return true;
7258 }
7259
7260 _log("EventTunnel.eventHandlerImpl() - Event received::" + messageText);
7261
7262 var node = items.getAttribute('node');
7263
7264 if (node === null) {
7265 _log("EventTunnel.eventHandlerImpl() - Node info not present - cannot send message " + messageText);
7266 return true;
7267 }
7268
7269 _sendEvent(node, messageText);
7270
7271 //need to return true else this callback will not be called again
7272 return true;
7273 },
7274
7275
7276 /**
7277 * The Websocket event handler, which consumes the event and publishes it to the
7278 * parent frame.
7279 * @param {Object} event
7280 * The event object for the notification as given by strophe
7281 * @private
7282 */
7283 _eventHandler = function (msgElement) {
7284 try {
7285 return eventHandlerImpl(msgElement);
7286 } catch (e) {
7287 _log("EventTunnel._eventHandler() - exception in event handling .. " + e.stack);
7288 }
7289 return true;
7290 },
7291
7292
7293 /**
7294 * Disconnects the Websocket connection
7295 * @private
7296 */
7297 _disconnect = function () {
7298 if (_xmppClient) {
7299 _log("EventTunnel._disconnect() - disconnect requested ...");
7300 _xmppClient.disconnect('Requested to disconnect');
7301 }
7302 },
7303
7304 /**
7305 * Iterates through all nodes with active subscriptions and unsubscribes from them
7306 * @private
7307 */
7308
7309 _unsubscribeAllNodes = function () {
7310 _pubsubNodes.forEach(
7311 function (node) {
7312 _unsubscribe(node);
7313 });
7314 },
7315
7316
7317
7318 /**
7319 * Utility to unsubscribe from all nodes and disconnect the Websocket session
7320 * @private
7321 */
7322 _cleanupSession = function () {
7323 //Tell clients that we are unloading the page
7324 _sendMessage(_TYPES.STATUS, _STATUS[6]);
7325 _unsubscribeAllNodes();
7326 //We may want to consider moving this before destruction if it becomes a problem; not sure if destroy will work after disconnect.
7327 _disconnect();
7328 },
7329
7330 /**
7331 * Utility to clean everything up upon unloading
7332 * @private
7333 */
7334 _unload = function () {
7335 _cleanupSession();
7336 },
7337
7338 /**
7339 * Checks to see whether the strophe client is both connected and the
7340 * connectedUser has established their available presence to the openfire
7341 * server. If so, the "connected" status for the Websocket connection will be
7342 * sent back to the parent frame.
7343 *
7344 * @private
7345 */
7346 _checkToSendConnected = function () {
7347 _log("EventTunnel._checkToSendConnected(): status=" + _xmppClientStatus + ", _initPresence=" + _initPresence);
7348 if (_xmppClientStatus !== undefined && _xmppClientStatus === Strophe.Status.CONNECTED && _initPresence === true) {
7349 _log("EventTunnel._checkToSendConnected(): sending connected");
7350 _sendMessage(_TYPES.RESOURCEID, _xmppResource);
7351 _sendMessage(_TYPES.STATUS, "connected");
7352 }
7353 },
7354
7355
7356 /**
7357 * Called by the presence handler. This function will determine whether the
7358 * jid belongs to the "finesse" xmpp user and if so, set the _initPresence
7359 * flag to true to indicate that the strophe client's connectedUser has
7360 * successfully set their presence to available. This is a workaround
7361 * because at this time, Openfire 3.7.0 does not support the latest spec on
7362 * IM and presence (RFC 6121) which states that when the xmpp server
7363 * receives initial presence, it should send that presence back to the
7364 * client. In other words, since Openfire sends the presence of subscribed
7365 * users upon initial presence being received, that mechanism is being used
7366 * to indicate that the initial presence has been received by Openfire.
7367 *
7368 * @param {String}
7369 * jid The full jid of the user that sent their presence.
7370 * @private
7371 */
7372 _checkFinesseUser = function (jid) {
7373 var user,
7374 index = jid.indexOf("@");
7375
7376 if (index > 0) {
7377 user = jid.substr(0, index);
7378 // If presence is for Finesse user, set initPresence to true
7379 if (user === "finesse") {
7380 _initPresence = true;
7381 } else if (user.indexOf("cuic.") > -1 || user.indexOf("ccx.") > -1) {
7382 //Need to initPresence for CUIC and CCX
7383 _initPresence = true;
7384 }
7385 }
7386 },
7387
7388 /**
7389 * The presence handler Implementation, which consumes the presence event and
7390 * publishes the presence to the parent frame.
7391 * @param {Object} event
7392 * The presence update event.
7393 * @private
7394 */
7395 _presenceHandlerImpl = function (msgElement) {
7396
7397 _log("EventTunnel._presenceHandlerImpl() - receieved presence message .."+msgElement);
7398
7399 if (msgElement === null) {
7400 return true;
7401 }
7402
7403 var from = msgElement.getAttribute('from');
7404 var type = msgElement.getAttribute('type');
7405
7406 if (from === null) {
7407 _log("EventTunnel._presenceHandlerImpl() - could not handle presence message since from is null..");
7408 return true;
7409 }
7410
7411
7412 _log("EventTunnel._presenceHandlerImpl() - receieved presence message .." + "from:" + from + "type:" + type);
7413
7414 // Only check the following during the init phase of the
7415 // desktop, once initial presence has been confirmed, disable
7416 // this check
7417 if (!_initPresence) {
7418 _checkFinesseUser(from);
7419 _checkToSendConnected();
7420 }
7421
7422
7423 var presence = {
7424 from: from,
7425 type: type
7426 };
7427
7428 var presenceObject = {"presence":presence};
7429
7430 presenceXml = finesse.Converter.json2xml(presenceObject);
7431
7432 if (presenceXml) {
7433 _log("EventTunnel._presenceHandlerImpl() - broacasting presence message .." + presenceXml);
7434 _sendMessage(_TYPES.PRESENCE, presenceXml);
7435 }
7436
7437 //need to return true else this callback will not be called again
7438 return true;
7439 },
7440
7441 /**
7442 * The presence handler, which consumes the presence event and
7443 * publishes the presence to the parent frame.
7444 * @param {Object} event
7445 * The presence update event.
7446 * @private
7447 */
7448
7449 _presenceHandler = function (msgElement) {
7450
7451 try
7452 {
7453 return _presenceHandlerImpl(msgElement);
7454 }
7455 catch (e) {
7456 _log("EventTunnel._presenceHandler() - exception in event handling of presence .. " + e.stack);
7457 }
7458
7459 return true;
7460 },
7461
7462 _pingSuccess = function(data) {
7463 _pingCounter = 0;
7464 //_log('EventTunnel._pingSuccess - Ping response was successful.');
7465 },
7466
7467 _pingError = function(error) {
7468 if(_pingCounter < 3) {
7469 _ping();
7470 }
7471 _log('EventTunnel._pingError - Ping response was unsuccessful.' + JSON.stringify(error));
7472 },
7473
7474
7475 _ping = function() {
7476 _pingCounter++;
7477 try {
7478 _xmppClient.ping.ping( _xmppDomain, _pingSuccess, _pingError, 10000 );
7479 } catch(error) {
7480 _pingCounter = 3;
7481 }
7482 if(_pingCounter > 2) {
7483 _onConnect(Strophe.Status.DISCONNECTED);
7484 }
7485 },
7486
7487 /**
7488 * start a custom ping to openfire, this is required becuase when network disconnect happens the websocket connection
7489 * is not throwing a excception or getting closed. the logic here takes cares of this scenario. We start a custom ping and
7490 * wait for succesful ping response, if there is succcesful response we send the next ping and so on. if there was no response
7491 * for a ping we check for two unsuccesful pings and closed the websocket connection and failover is triggered.
7492 */
7493 _startPing = function () {
7494 _pingCounter = 0;
7495 clearInterval(_pingIntervalHandler);
7496 _pingIntervalHandler = setInterval( _ping, 20000);
7497 },
7498
7499 _resetConnection = function() {
7500 if(_xmppClient) {
7501 _xmppClient.reset();
7502 }
7503
7504 clearInterval(_pingIntervalHandler);
7505 _pingIntervalHandler = null;
7506 },
7507
7508 _onConnect = function (status,errorCondition) {
7509
7510 _log("EventTunnel._onConnect() - XMPP client onCONNECT fired");
7511
7512 var unloadEvent = (_isIE) ? "unload" : "beforeunload";
7513 var statusStr;
7514
7515 _isConnected = false;
7516
7517 _log("EventTunnel._onConnect() - XMPP client onConnect status is: " + status + ":"+errorCondition);
7518
7519 // THESE STATUS CODES ARE SELF EXPLANATORY
7520 if (status === Strophe.Status.ERROR && errorCondition === "conflict") {
7521 statusStr = "conflict";
7522 _initPresence = false;
7523 _log("EventTunnel._onConnect() - XMPP client about to reset because of status:" + statusStr);
7524 _resetConnection();
7525 }
7526 else if (status === Strophe.Status.AUTHFAIL) {
7527 statusStr = "unauthorized";
7528 _initPresence = false;
7529 _log("EventTunnel._onConnect() - XMPP client about to reset because of status:" + statusStr);
7530 _resetConnection();
7531 }
7532 else if (status === Strophe.Status.CONNECTING) {
7533 _xmppClientStatus = Strophe.Status.CONNECTING;
7534 } else if (status === Strophe.Status.CONNFAIL) {
7535 _xmppClientStatus = Strophe.Status.CONNFAIL;
7536 } else if (status === Strophe.Status.DISCONNECTING) {
7537 _xmppClientStatus = Strophe.Status.DISCONNECTING;
7538 } else if (status === Strophe.Status.DISCONNECTED) {
7539 statusStr = "disconnected";
7540 _xmppClientStatus = Strophe.Status.DISCONNECTED;
7541 jQuery(window).unbind(unloadEvent, _unload);
7542 _initPresence = false;
7543 _log("EventTunnel._onConnect() - XMPP client about to reset because of status:" + statusStr);
7544 _resetConnection();
7545 } else if (status === Strophe.Status.CONNECTED) {
7546 statusStr = "connected";
7547 _xmppClientStatus = Strophe.Status.CONNECTED;
7548 jQuery(window).bind(unloadEvent, _unload);
7549 // We add a listener that will catch the opposite user's msgs
7550 _xmppClient.addHandler(_eventHandler, null, 'message', null, null, null);
7551 _xmppClient.addHandler(_presenceHandler, null, 'presence', null, null, null);
7552 if (_notificationConnectionType.toLowerCase() === "websocket") {
7553 _startPing();
7554 }
7555 // We send online presense to the server. (pub sub concept)
7556 //We must send a presence when the connection's status is Strophe.Status.CONNECTING
7557 _xmppClient.send($pres().tree());
7558 // Do not send the connected status immediately. The
7559 // connected status is gated on the strophe client's
7560 // connectedUser presence because OpenFire will not route events to
7561 // the session for this user unless their presence is available.
7562 _checkToSendConnected();
7563 // Set to undefined so that it will not be sent to the
7564 // parent frame just yet.
7565 statusStr = undefined;
7566 _isConnected = true;
7567 }
7568
7569 //Send the resource ID and the new connection status to parent frame.
7570 if (statusStr) {
7571 _sendMessage(_TYPES.RESOURCEID, _xmppResource);
7572 _sendMessage(_TYPES.STATUS, statusStr);
7573 }
7574
7575 return true;
7576
7577 },
7578
7579
7580 /**
7581 * Get's the list of subscriptions to other nodes.
7582 * @return
7583 * JSON Array with objects like below:
7584 *
7585 * <pre>
7586 * {
7587 * "node": "/finesse/api/User/1001050/Media",
7588 * "jid": "<userid>@<host>",
7589 * "status": "subscribed"
7590 * }
7591 * </pre>
7592 */
7593 _subscriptionsReq = function() {
7594 var successCallback = function(resp) {
7595 var associatedNodes = resp.getElementsByTagName('subscriptions')[0].childNodes;
7596
7597 var defaultSubscriptions = [];
7598
7599 associatedNodes.forEach(function(sub) {
7600 defaultSubscriptions.push(
7601 {
7602 'node': sub.getAttribute('node'), // Subscribed 'to' target node
7603 'jid': sub.getAttribute('jid'), // JID of the node which had subscribed to target node
7604 'status': sub.getAttribute('subscription') // should be "subscribed"
7605 }
7606 );
7607 });
7608
7609 //_log("EventTunnel._subscriptionsReq() succ cb. returning :" + JSON.stringify(defaultSubscriptions,null,2));
7610 _sendMessage(_TYPES.SUBSCRIPTIONS_REQ, defaultSubscriptions);
7611 };
7612
7613 var errorCallback = function(resp) {
7614 //_log("EventTunnel._subscriptionsReq() err cb. unable to get subscriptions .. " + resp);
7615 _sendMessage(_TYPES.SUBSCRIPTIONS_REQ, resp+"|<ApiErrors/>");
7616 };
7617
7618 _xmppClient.pubsubext.subscriptions('to', successCallback, errorCallback);
7619 },
7620
7621 /**
7622 * Establish websocket connection using strophe library.
7623 * @param {String} ID
7624 * The ID of the user configured on the XMPP notification server.
7625 * @param {String} password
7626 * The password belonging to the user.
7627 * @param {String} domain
7628 * The domain of the notification server (i.e. subdomain.host.com)
7629 * @param {String} [resource]
7630 * The resource ID of the user's device used to log in. The ID will be
7631 * auto-generated if none is specified.
7632 * @param {String} notificationConnectionType
7633 * The XMPP connection protocol : websocket or BOSH
7634 * @private
7635 */
7636 _connect = function (ID, password, domain, resource, notificationConnectionType) {
7637 ID = ID.toLowerCase();
7638 _log("EventTunnel.connect() - ID:"+ ID +", domain:"+domain+", resource:"+ resource);
7639
7640 //Construct JID with username and domain.
7641 var jid = ID + "@" + domain + "/" + resource,
7642 scheme = "",
7643 bindURL = "";
7644 _log("EventTunnel.connect() - XMPP client JID for connection = "+ jid);
7645
7646 if (notificationConnectionType.toLowerCase() === "bosh") {
7647 scheme = window.location.protocol;
7648 bindURL = "/http-bind/";
7649 } else {
7650 // Else fallback to default websocket
7651 scheme = window.location.protocol === "https:" ? "wss:" : "ws:";
7652 bindURL = "/ws/";
7653 }
7654 var connectURL = scheme + "//" + window.location.host + bindURL;
7655 _log("EventTunnel.connect() - XMPP client connect url = " + connectURL);
7656
7657
7658 if (_xmppClient) {
7659 _log("EventTunnel.connect() - XMPP client about to CONNECT..Found existing tunnel - about to kill them");
7660 _xmppClient.disconnect();
7661 _xmppClient.reset();
7662 }
7663
7664 _xmppResource = resource;
7665
7666 _log("EventTunnel.connect() - Initialize Strophe XMPP client");
7667 _xmppClient = new Strophe.Connection(connectURL);
7668 _log("EventTunnel.connect() - Strophe XMPP client created");
7669
7670 _log("EventTunnel.connect() - Calling Strophe XMPP connect");
7671 //Connect to Websocket connection.
7672 _xmppClient.connect(jid, password, _onConnect);
7673
7674 },
7675
7676 /**
7677 * Utility for sending a subscription request to the XMPP notification server.
7678 * This is likely invoked by a _TYPES.SUBSCRIBE message and responds with the same.
7679 * @param {String} node
7680 * The path of the node of interest to be subscribed
7681 * @private
7682 */
7683 _subscribe = function (node) {
7684 // first make sure we are in connected state
7685 if (!_isConnected) {
7686 _sendMessage(_TYPES.SUBSCRIBE, node + "|<ApiErrors/>");
7687 return;
7688 }
7689
7690 if (_pubsubDomain) {
7691 _xmppClient.pubsub.subscribe(
7692 _xmppClient.jid,
7693 _pubsubDomain,
7694 node,
7695 [],
7696 function () {
7697
7698 },
7699 function () {
7700 var msg = node;
7701 _pubsubNodes.push(node);
7702 _sendMessage(_TYPES.SUBSCRIBE, msg);
7703 }
7704 );
7705 }
7706 },
7707
7708 /**
7709 * Utility for sending an unsubscribe request to the XMPP notification server.
7710 * This is likely invoked by a _TYPES.UNSUBSCRIBE message and responds with the same.
7711 * @param {String} node
7712 * The path of the node of interest to be unsubscribed
7713 * @private
7714 */
7715 _unsubscribe = function (node) {
7716 // first make sure we are in connected state
7717 if (!_isConnected) {
7718 _sendMessage(_TYPES.UNSUBSCRIBE, node + "|<ApiErrors/>");
7719 return;
7720 }
7721
7722 _xmppClient.pubsub.unsubscribe(_xmppClient.jid, _pubsubDomain, node, function() {
7723 _log("EventTunnel._unsubscribe() request on node"+ node + "was successful");
7724 //_pubsubNodes[node].destroy();
7725 delete _pubsubNodes[node];
7726 _sendMessage(_TYPES.UNSUBSCRIBE, node);
7727 });
7728 },
7729
7730 /**
7731 * Method to attempt to establish a Websocket connection with the XMPP server through strophe
7732 * @param {String} connInfo
7733 * xml string describing a connInfo object containing id, password, and xmppDomain elements
7734 * @private
7735 */
7736 _processConnectReq = function (connInfo) {
7737 // Convert connInfo to json obj
7738 var connInfoObj = JSON.parse(finesse.Converter.xml2json(jQuery.parseXML(connInfo), ""));
7739
7740 _connect(connInfoObj.connInfo.id, connInfoObj.connInfo.password, connInfoObj.connInfo.xmppDomain, _resource, connInfoObj.connInfo.notificationConnectionType);
7741 },
7742
7743 /**
7744 * Handler for messages delivered by window.postMessage. Listens for
7745 * credentials to be passed by parent frame in order to establish a bosh/Websocket
7746 * connection.
7747 * @param {Object} e
7748 * The message object as provided by the window.postMessage feature.
7749 * @private
7750 */
7751 _messageHandler = function (e) {
7752 var
7753
7754 //Extract the message type and message data. The expected format is
7755 //"type|data" where type is a number represented by the TYPES object.
7756 delimPos = e.data.indexOf("|"),
7757 type = Number(e.data.substr(0, delimPos)),
7758 data = e.data.substr(delimPos + 1);
7759
7760 //Since the ID and password is being delivered by the parent frame
7761 //separately, store the credentials until both fields come in
7762 //before attempting to connect.
7763
7764 switch (type) {
7765 case _TYPES.ID:
7766 _id = data;
7767 break;
7768 case _TYPES.XMPPDOMAIN:
7769 _xmppDomain = data;
7770 break;
7771 case _TYPES.PASSWORD:
7772 _password = data;
7773 break;
7774 case _TYPES.RESOURCEID:
7775 _resource = data;
7776 break;
7777 case _TYPES.PUBSUBDOMAIN:
7778 _pubsubDomain = data;
7779 return;
7780 case _TYPES.SUBSCRIBE:
7781 _subscribe(data);
7782 return;
7783 case _TYPES.UNSUBSCRIBE:
7784 _unsubscribe(data);
7785 return;
7786 case _TYPES.SUBSCRIPTIONS_REQ:
7787 _subscriptionsReq();
7788 return;
7789 case _TYPES.CONNECT_REQ:
7790 _processConnectReq(data);
7791 return;
7792 case _TYPES.DISCONNECT_REQ:
7793 _unload();
7794 return;
7795 case _TYPES.NOTIFICATION_CONNECTION_TYPE:
7796 _notificationConnectionType = data;
7797 break;
7798 }
7799
7800 //Ensure that ID, domain and password credentials have been received
7801 //before attempting to establish a Websocket connection.
7802 if (_id !== undefined && _password !== undefined && _xmppDomain !== undefined && _notificationConnectionType !== undefined) {
7803 try {
7804 _connect(_id, _password, _xmppDomain, _resource, _notificationConnectionType);
7805 } catch (err) {
7806 _log(err);
7807 }
7808
7809 //Remove reference to password.
7810 _password = undefined;
7811 }
7812 };
7813
7814 /**
7815 * Initiate the message listener to wait for actions from the parent frame.
7816 */
7817 this.init = function () {
7818 // Determine whether the we're running in an Internet Explorer
7819 // browser. Flag is used for disconnect logic which is browser
7820 // dependent.
7821 _isIE = _isInternetExplorer(); //window.navigator.userAgent.indexOf("MSIE") !== -1;
7822
7823 _util.receiveMessage(_messageHandler);
7824
7825 //Send a "loaded" status message to indicate to the parent frame to
7826 //send domains/credentials.
7827 _sendMessage(_TYPES.STATUS, _STATUS[0]);
7828 };
7829
7830 //BEGIN TEST CODE//
7831 /**
7832 * Test code added to expose private functions that are used by unit test
7833 * framework. This section of code is removed during the build process
7834 * before packaging production code. The [begin|end]TestSection are used
7835 * by the build to identify the section to strip.
7836 * @ignore
7837 */
7838 this.beginTestSection = 0;
7839
7840 /**
7841 * @ignore
7842 */
7843 this.getTestObject = function () {
7844 //Load mock dependencies.
7845 var _mock = new MockControl();
7846 _util = _mock.createMock(finesse.EventTunnel.Utilities);
7847
7848 return {
7849 //Expose mock dependencies
7850 mock: _mock,
7851 util: _util,
7852
7853 //Expose internal private functions
7854 jwClient: _jwClient,
7855 types: _TYPES,
7856 sendMessage: _sendMessage,
7857 eventHandler: _eventHandler,
7858 connect: _connect,
7859 setConnect: function (connect) {
7860 _connect = connect;
7861 },
7862 messageHandler: _messageHandler
7863 };
7864 };
7865
7866 /**
7867 * @ignore
7868 */
7869 this.endTestSection = 0;
7870 //END TEST CODE//
7871};
7872
7873finesse.EventTunnel.Utilities = (function () {
7874 return {
7875 /**
7876 * Creates a message listener for window.postMessage messages.
7877 * @param {Function} callback
7878 * The callback that will be invoked with the message. The callback
7879 * is responsible for any security checks.
7880 * @param {String} [origin]
7881 * The origin to check against for security. Allows all messages
7882 * if no origin is provided.
7883 * @returns {Function}
7884 * The callback function used to register with the message listener.
7885 * This is different than the one provided as a parameter because
7886 * the function is overloaded with origin checks.
7887 * @throws {Error} If the callback provided is not a function.
7888 */
7889 receiveMessage: function (callback, origin) {
7890 if (typeof callback !== "function") {
7891 throw new Error("Callback is not a function.");
7892 }
7893
7894 //Create a function closure to perform origin check.
7895 var cb = function (e) {
7896 // If an origin check is requested (provided), we'll only invoke the callback if it passes
7897 if (typeof origin !== "string" || (typeof origin === "string" && typeof e.origin === "string" && e.origin.toLowerCase() === origin.toLowerCase())) {
7898 callback(e);
7899 }
7900 };
7901
7902 if (window.addEventListener) { //Firefox, Opera, Chrome, Safari
7903 window.addEventListener("message", cb, false);
7904 } else { //Internet Explorer
7905 window.attachEvent("onmessage", cb);
7906 }
7907
7908 //Return callback used to register with listener so that invoker
7909 //could use it to remove.
7910 return cb;
7911 },
7912
7913 /**
7914 * Sends a message to a target frame using window.postMessage.
7915 * @param {Function} message
7916 * Message to be sent to target frame.
7917 * @param {Object} [target="parent"]
7918 * An object reference to the target frame. Default us the parent.
7919 * @param {String} [targetOrigin="*"]
7920 * The URL of the frame this frame is sending the message to.
7921 */
7922 sendMessage: function (message, target, targetOrigin) {
7923 //Default to any target URL if none is specified.
7924 targetOrigin = targetOrigin || "*";
7925
7926 //Default to parent target if none is specified.
7927 target = target || parent;
7928
7929 //Ensure postMessage is supported by browser before invoking.
7930 if (window.postMessage) {
7931 target.postMessage(message, targetOrigin);
7932 }
7933 }
7934 };
7935}());
7936
7937 // End EventTunnel.js