1 var Xmpp4Js = {};
  2 
  3 
  4 
  5 Xmpp4Js.logLevel = Log4js.Level.ALL;
  6 Xmpp4Js.appender = new Log4js.ConsoleAppender();
  7 Xmpp4Js.loggers = [];
  8 
  9 Xmpp4Js.createLogger = function(logId) {
 10     var logger = Log4js.getLogger( logId ); 
 11     logger.setLevel( Xmpp4Js.logLevel ); 
 12     logger.addAppender( Xmpp4Js.appender );
 13 
 14     Xmpp4Js.loggers.push( logger );
 15     
 16     return logger;
 17 }
 18 //javascript:Xmpp4Js.setLogLevel( Log4js.Level.OFF );
 19 Xmpp4Js.setLogLevel = function(level) {
 20     Xmpp4Js.logLevel = level;
 21     
 22     for(var i = 0; i < Xmpp4Js.loggers.length; i++ ) {
 23         Xmpp4Js.loggers[i].setLevel( level );
 24     }
 25 }
 26 
 27 
 28 /**
 29  * Do not fork for a browser if it can be avoided.  Use feature detection when
 30  * you can.  Use the user agent as a last resort.  YAHOO.env.ua stores a version
 31  * number for the browser engine, 0 otherwise.  This value may or may not map
 32  * to the version number of the browser using the engine.  The value is 
 33  * presented as a float so that it can easily be used for boolean evaluation 
 34  * as well as for looking for a particular range of versions.  Because of this, 
 35  * some of the granularity of the version info may be lost (e.g., Gecko 1.8.0.9 
 36  * reports 1.8).
 37  * @class YAHOO.env.ua
 38  * @static
 39  */
 40 Xmpp4Js.UA = function() {
 41     var o={
 42 
 43         /**
 44          * Internet Explorer version number or 0.  Example: 6
 45          * @property ie
 46          * @type float
 47          */
 48         ie:0,
 49 
 50         /**
 51          * Opera version number or 0.  Example: 9.2
 52          * @property opera
 53          * @type float
 54          */
 55         opera:0,
 56 
 57         /**
 58          * Gecko engine revision number.  Will evaluate to 1 if Gecko 
 59          * is detected but the revision could not be found. Other browsers
 60          * will be 0.  Example: 1.8
 61          * <pre>
 62          * Firefox 1.0.0.4: 1.7.8   <-- Reports 1.7
 63          * Firefox 1.5.0.9: 1.8.0.9 <-- Reports 1.8
 64          * Firefox 2.0.0.3: 1.8.1.3 <-- Reports 1.8
 65          * Firefox 3 alpha: 1.9a4   <-- Reports 1.9
 66          * </pre>
 67          * @property gecko
 68          * @type float
 69          */
 70         gecko:0,
 71 
 72         /**
 73          * AppleWebKit version.  KHTML browsers that are not WebKit browsers 
 74          * will evaluate to 1, other browsers 0.  Example: 418.9.1
 75          * <pre>
 76          * Safari 1.3.2 (312.6): 312.8.1 <-- Reports 312.8 -- currently the 
 77          *                                   latest available for Mac OSX 10.3.
 78          * Safari 2.0.2:         416     <-- hasOwnProperty introduced
 79          * Safari 2.0.4:         418     <-- preventDefault fixed
 80          * Safari 2.0.4 (419.3): 418.9.1 <-- One version of Safari may run
 81          *                                   different versions of webkit
 82          * Safari 2.0.4 (419.3): 419     <-- Tiger installations that have been
 83          *                                   updated, but not updated
 84          *                                   to the latest patch.
 85          * Webkit 212 nightly:   522+    <-- Safari 3.0 precursor (with native SVG
 86          *                                   and many major issues fixed).  
 87          * 3.x yahoo.com, flickr:422     <-- Safari 3.x hacks the user agent
 88          *                                   string when hitting yahoo.com and 
 89          *                                   flickr.com.
 90          * Safari 3.0.4 (523.12):523.12  <-- First Tiger release - automatic update
 91          *                                   from 2.x via the 10.4.11 OS patch
 92          * Webkit nightly 1/2008:525+    <-- Supports DOMContentLoaded event.
 93          *                                   yahoo.com user agent hack removed.
 94          *                                   
 95          * </pre>
 96          * http://developer.apple.com/internet/safari/uamatrix.html
 97          * @property webkit
 98          * @type float
 99          */
100         webkit: 0,
101 
102         /**
103          * The mobile property will be set to a string containing any relevant
104          * user agent information when a modern mobile browser is detected.
105          * Currently limited to Safari on the iPhone/iPod Touch, Nokia N-series
106          * devices with the WebKit-based browser, and Opera Mini.  
107          * @property mobile 
108          * @type string
109          */
110         mobile: null,
111 
112         /**
113          * Adobe AIR version number or 0.  Only populated if webkit is detected.
114          * Example: 1.0
115          * @property air
116          * @type float
117          */
118         air: 0
119 
120     };
121 
122     var ua=navigator.userAgent, m;
123 
124     // Modern KHTML browsers should qualify as Safari X-Grade
125     if ((/KHTML/).test(ua)) {
126         o.webkit=1;
127     }
128     // Modern WebKit browsers are at least X-Grade
129     m=ua.match(/AppleWebKit\/([^\s]*)/);
130     if (m&&m[1]) {
131         o.webkit=parseFloat(m[1]);
132 
133         // Mobile browser check
134         if (/ Mobile\//.test(ua)) {
135             o.mobile = "Apple"; // iPhone or iPod Touch
136         } else {
137             m=ua.match(/NokiaN[^\/]*/);
138             if (m) {
139                 o.mobile = m[0]; // Nokia N-series, ex: NokiaN95
140             }
141         }
142 
143         m=ua.match(/AdobeAIR\/([^\s]*)/);
144         if (m) {
145             o.air = m[0]; // Adobe AIR 1.0 or better
146         }
147 
148     }
149 
150     if (!o.webkit) { // not webkit
151         // @todo check Opera/8.01 (J2ME/MIDP; Opera Mini/2.0.4509/1316; fi; U; ssr)
152         m=ua.match(/Opera[\s\/]([^\s]*)/);
153         if (m&&m[1]) {
154             o.opera=parseFloat(m[1]);
155             m=ua.match(/Opera Mini[^;]*/);
156             if (m) {
157                 o.mobile = m[0]; // ex: Opera Mini/2.0.4509/1316
158             }
159         } else { // not opera or webkit
160             m=ua.match(/MSIE\s([^;]*)/);
161             if (m&&m[1]) {
162                 o.ie=parseFloat(m[1]);
163             } else { // not opera, webkit, or ie
164                 m=ua.match(/Gecko\/([^\s]*)/);
165                 if (m) {
166                     o.gecko=1; // Gecko detected, look for revision
167                     m=ua.match(/rv:([^\s\)]*)/);
168                     if (m&&m[1]) {
169                         o.gecko=parseFloat(m[1]);
170                     }
171                 }
172             }
173         }
174     }
175     
176     return o;
177 }();
178 
179 Xmpp4Js.Lang = {
180     logger: Xmpp4Js.createLogger("xmpp4js.lang"),
181     
182     /**
183      * Determines whether or not the property was added
184      * to the object instance.  Returns false if the property is not present
185      * in the object, or was inherited from the prototype.
186      * This abstraction is provided to enable hasOwnProperty for Safari 1.3.x.
187      * There is a discrepancy between YAHOO.lang.hasOwnProperty and
188      * Object.prototype.hasOwnProperty when the property is a primitive added to
189      * both the instance AND prototype with the same value:
190      * <pre>
191      * var A = function() {};
192      * A.prototype.foo = 'foo';
193      * var a = new A();
194      * a.foo = 'foo';
195      * alert(a.hasOwnProperty('foo')); // true
196      * alert(YAHOO.lang.hasOwnProperty(a, 'foo')); // false when using fallback
197      * </pre>
198      * @method hasOwnProperty
199      * @param {any} o The object being testing
200      * @param prop {string} the name of the property to test
201      * @return {boolean} the result
202      */
203     hasOwnProperty : (Object.prototype.hasOwnProperty) ?
204       function(o, prop) {
205           return o && o.hasOwnProperty(prop);
206       } : function(o, prop) {
207           return !Xmpp4Js.Lang.isUndefined(o[prop]) && 
208                   o.constructor.prototype[prop] !== o[prop];
209       },
210     
211     namespace: function(namespace, root) {
212         if( !root ) { root = window; }
213         var splitNS = namespace.split(/\./);
214         
215         var ctx = root;
216         for( var i = 0; i < splitNS.length; i++ ) {
217             var nsPiece = splitNS[i];
218             
219             if( ctx[nsPiece] === undefined ) {
220                 ctx[nsPiece] = {};
221             }
222             
223             ctx = ctx[nsPiece];
224         }
225         
226         return ctx;
227     },
228     
229     /**
230      * IE will not enumerate native functions in a derived object even if the
231      * function was overridden.  This is a workaround for specific functions 
232      * we care about on the Object prototype. 
233      * @property _IEEnumFix
234      * @param {Function} r  the object to receive the augmentation
235      * @param {Function} s  the object that supplies the properties to augment
236      * @static
237      * @private
238      */
239     _IEEnumFix: (Xmpp4Js.UA.ie) ? function(r, s) {
240         
241             // ADD = ["toString", "valueOf", "hasOwnProperty"],
242             var ADD = ["toString", "valueOf"];
243         
244             for (var i=0;i<ADD.length;i=i+1) {
245                 var fname=ADD[i],f=s[fname];
246                 if (Xmpp4Js.Lang.isFunction(f) && f!=Object.prototype[fname]) {
247                     r[fname]=f;
248                 }
249             }
250     } : function(){},
251     
252     /**
253      * Determines whether or not the provided object is a function
254      * @method isFunction
255      * @param {any} o The object being testing
256      * @return {boolean} the result
257      */
258     isFunction: function(o) {
259         return typeof o === 'function';
260     },
261     
262     /**
263      * Determines whether or not the provided object is undefined
264      * @method isUndefined
265      * @param {any} o The object being testing
266      * @return {boolean} the result
267      */
268     isUndefined: function(o) {
269         return typeof o === 'undefined';
270     },
271     
272     /**
273      * Utility to set up the prototype, constructor and superclass properties to
274      * support an inheritance strategy that can chain constructors and methods.
275      * Static members will not be inherited.
276      *
277      * @method extend
278      * @static
279      * @param {Function} subc   the object to modify
280      * @param {Function} superc the object to inherit
281      * @param {Object} overrides  additional properties/methods to add to the
282      *                              subclass prototype.  These will override the
283      *                              matching items obtained from the superclass 
284      *                              if present.
285      */
286     extend: function(subc, superc, overrides) {
287         if (!superc||!subc) {
288             throw new Error("extend failed, please check that " +
289                             "all dependencies are included.");
290         }
291         var F = function() {};
292         F.prototype=superc.prototype;
293         subc.prototype=new F();
294         subc.prototype.constructor=subc;
295         subc.superclass=superc.prototype;
296         if (superc.prototype.constructor == Object.prototype.constructor) {
297             superc.prototype.constructor=superc;
298         }
299     
300         if (overrides) {
301             for (var i in overrides) {
302                 if (Xmpp4Js.Lang.hasOwnProperty(overrides, i)) {
303                     subc.prototype[i]=overrides[i];
304                 }
305             }
306 
307             Xmpp4Js.Lang._IEEnumFix(subc.prototype, overrides);
308         }
309     },
310     
311     id: function(prefix) {
312         if(!prefix) { prefix = "soashable-"; }
313         
314         return prefix+Math.random(0, 50000);
315     },
316     
317     /**
318      * Applies all properties in the supplier to the receiver if the
319      * receiver does not have these properties yet.  Optionally, one or 
320      * more methods/properties can be specified (as additional 
321      * parameters).  This option will overwrite the property if receiver 
322      * has it already.  If true is passed as the third parameter, all 
323      * properties will be applied and _will_ overwrite properties in 
324      * the receiver.
325      *
326      * @method augmentObject
327      * @static
328      * @since 2.3.0
329      * @param {Function} r  the object to receive the augmentation
330      * @param {Function} s  the object that supplies the properties to augment
331      * @param {String*|boolean}  arguments zero or more properties methods 
332      *        to augment the receiver with.  If none specified, everything
333      *        in the supplier will be used unless it would
334      *        overwrite an existing property in the receiver. If true
335      *        is specified as the third parameter, all properties will
336      *        be applied and will overwrite an existing property in
337      *        the receiver
338      */
339     augmentObject: function(r, s) {
340         if (!s||!r) {
341             throw new Error("Absorb failed, verify dependencies.");
342         }
343         var a=arguments, i, p, override=a[2];
344         if (override && override!==true) { // only absorb the specified properties
345             for (i=2; i<a.length; i=i+1) {
346                 r[a[i]] = s[a[i]];
347             }
348         } else { // take everything, overwriting only if the third parameter is true
349             for (p in s) { 
350                 if (override || !(p in r)) {
351                     r[p] = s[p];
352                 }
353             }
354             
355             Xmpp4Js.Lang._IEEnumFix(r, s);
356         }
357     },
358     
359     asyncRequest: function(request) {
360       var xhr = Xmpp4Js.Lang.createXhr();
361       
362       xhr.open(request.method, request.url, request.async != undefined ? request.async : true);
363       for( var header in request.headers ) {
364           xhr.setRequestHeader( header, request.headers[ header ] );
365       }
366       xhr.onreadystatechange = function() {
367         if (xhr.readyState == 4 ) {
368           var success = xhr.status == 200;
369           
370               request.callback.call( request.scope, request, success, xhr );
371         }
372       };
373       
374       var xml = request.xmlNode.toString();
375       
376       xhr.send(xml);
377     },
378     
379     createXhr : function () {
380         var xhr = false;
381         if(window.XMLHttpRequest) {
382             xhr = new XMLHttpRequest();
383             if(xhr.overrideMimeType) {
384                 xhr.overrideMimeType('text/xml');
385             }
386         } else if(window.ActiveXObject) {
387             try {
388                 xhr = new ActiveXObject('Msxml2.XMLHTTP');
389             } catch(e) {
390                 try {
391                     xhr = new ActiveXObject('Microsoft.XMLHTTP');
392                 } catch(e) {
393                     xhr = false;
394                 }
395             }
396         }
397         return xhr;
398     },
399     
400     urlEncode: function (clearString) {
401       var output = '';
402       var x = 0;
403       clearString = clearString.toString();
404       var regex = /(^[a-zA-Z0-9_.]*)/;
405       while (x < clearString.length) {
406         var match = regex.exec(clearString.substr(x));
407         if (match != null && match.length > 1 && match[1] != '') {
408           output += match[1];
409           x += match[1].length;
410         } else {
411           if (clearString[x] == ' ') {
412             output += '+';
413           } else {
414             var charCode = clearString.charCodeAt(x);
415             var hexVal = charCode.toString(16);
416             output += '%' + ( hexVal.length < 2 ? '0' : '' ) + hexVal.toUpperCase();
417           }
418          x++;
419         }
420       }
421       return output;
422     },
423 
424     noOp: function(){},
425     
426     bind: function( fn, scope ) {
427         var args = Array.prototype.slice.call(arguments);
428         args.shift(); args.shift(); // remove fn and scope
429 
430         return function() {
431             var fnArgs = Array.prototype.slice.call(arguments)
432             return fn.apply(scope, args.concat(fnArgs));
433         }
434     }
435 }
436 
437 Function.prototype.bind = function(scope) {
438 //;;;   Xmpp4Js.Lang.logger.warn( "Using Function.prototype.bind" );
439     var args = Array.prototype.slice.call(arguments);
440     args.unshift( this ); // add fn argument to the beginning
441     return Xmpp4Js.Lang.bind.apply( this, args );
442 }
443 
444 
445 Xmpp4Js.Lang.TaskRunner = function(interval) {
446     this.interval = interval;
447     this.tasks = [];
448     
449     this.intervalId = setInterval(this.onInterval.bind(this), this.interval);
450 }
451 
452 Xmpp4Js.Lang.TaskRunner.prototype = {
453     start: function(task) {
454         this.tasks.push( task );
455     },
456     
457     stop: function(task) {
458         var removeIdxs = [];
459         
460         for( var i = 0; i < this.tasks.length; i++ ) {
461             if( this.tasks[i] === task ) {
462                 removeIdxs.push( i );
463             }
464         }
465         
466         this.removeTasks( removeIdxs );
467     },
468     
469     removeTasks: function( removeIdxs ) {
470         // JS is single threaded, so this shouldn't have concurrency issues
471         for( var i = 0; i < removeIdxs.length; i++ ) {
472             var task = this.tasks[i];
473             
474             // fire a stop event if present
475             if( task.onStop ) {
476                 task.onStop.apply( task.scope ? task.scope : task );
477             }
478             
479             this.tasks.splice( i, 1 );
480         }
481     },
482     
483     stopAll: function() {
484         var removeIdxs = [];
485         
486         // this is kind of stupid...
487         for( var i = 0; i < this.tasks.length; i++ ) {
488             removeIdxs.push(i);
489         }
490         
491         this.removeTasks( removeIdxs );
492     },
493     
494     onInterval: function() {
495         for( var i = 0; i < this.tasks.length; i++ ) {
496             var task = this.tasks[i];
497             
498             task.run.apply( task.scope ? task.scope : task );
499         }
500     }
501     
502 }
503 
504 
505 
506 
507