1 /* Licensed to the Apache Software Foundation (ASF) under one or more
  2  * contributor license agreements.  See the NOTICE file distributed with
  3  * this work for additional information regarding copyright ownership.
  4  * The ASF licenses this file to you under the Apache License, Version 2.0
  5  * (the "License"); you may not use this file except in compliance with
  6  * the License.  You may obtain a copy of the License at
  7  *
  8  *      http://www.apache.org/licenses/LICENSE-2.0
  9  *
 10  * Unless required by applicable law or agreed to in writing, software
 11  * distributed under the License is distributed on an "AS IS" BASIS,
 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  * See the License for the specific language governing permissions and
 14  * limitations under the License.
 15  */
 16 
 17 
 18 /**
 19  * Runtime/Startup class
 20  * this is the central class which initializes all base mechanisms
 21  * used by the rest of the system such as
 22  * a) namespacing system
 23  * b) browser detection
 24  * c) loose configuration coupling
 25  * d) utils methods to fetch the implementation
 26  * e) ajaxed script loading
 27  * f) global eval (because it is used internally)
 28  * g) Structural base patterns as singleton, delegate and inheritance
 29  *
 30  * Note this class is self contained and must!!! be loaded
 31  * as absolute first class before going into anything else
 32  *
 33  *
 34  */
 35 /** @namespace myfaces._impl.core._Runtime*/
 36 
 37 myfaces._impl.core = (myfaces._impl.core) ? myfaces._impl.core : {};
 38 //now this is the only time we have to do this cascaded and manually
 39 //for the rest of the classes our reserveNamespace function will do the trick
 40 //Note, this class uses the classical closure approach (to save code)
 41 //it cannot be inherited by our inheritance mechanism, but must be delegated
 42 //if you want to derive from it
 43 //closures and prototype inheritance do not mix, closures and delegation however do
 44 /**
 45  * @ignore
 46  */
 47 if (!myfaces._impl.core._Runtime) {
 48     /**
 49      * @memberOf myfaces._impl.core
 50      * @namespace
 51      * @name _Runtime
 52      */
 53     myfaces._impl.core._Runtime = new function() {
 54         //the rest of the namespaces can be handled by our namespace feature
 55         //helper to avoid unneeded hitches
 56         /**
 57          * @borrows myfaces._impl.core._Runtime as _T
 58          */
 59         var _T = this;
 60 
 61         //namespace idx to speed things up by hitting eval way less
 62         this._reservedNMS = {};
 63         this._registeredSingletons = {};
 64         this._registeredClasses = [];
 65         /**
 66          * replacement counter for plugin classes
 67          */
 68         this._classReplacementCnt = 0;
 69 
 70         /**
 71          * global eval on scripts
 72          * @param {String} code
 73          * @name myfaces._impl.core._Runtime.globalEval
 74          * @function
 75          */
 76         _T.globalEval = function(code) {
 77             return myfaces._impl.core._EvalHandlers.globalEval(code);
 78         };
 79 
 80         /**
 81          * applies an object to a namespace
 82          * basically does what bla.my.name.space = obj does
 83          * note we cannot use var myNameSpace = fetchNamespace("my.name.space")
 84          * myNameSpace = obj because the result of fetch is already the object
 85          * which the namespace points to, hence this function
 86          *
 87          * @param {String} nms the namespace to be assigned to
 88          * @param {Object} obj the  object to be assigned
 89          * @name myfaces._impl.core._Runtime.applyToGlobalNamespace
 90          * @function
 91          */
 92         _T.applyToGlobalNamespace = function(nms, obj) {
 93             var splitted = nms.split(/\./);
 94             if (splitted.length == 1) {
 95                 window[nms] = obj;
 96                 return;
 97             }
 98             var parent = splitted.slice(0, splitted.length - 1);
 99             var child = splitted[splitted.length - 1];
100             var parentNamespace = _T.fetchNamespace(parent.join("."));
101             parentNamespace[child] = obj;
102         };
103 
104         /**
105          * fetches the object the namespace points to
106          * @param {String} nms the namespace which has to be fetched
107          * @return the object the namespace points to or null if nothing is found
108          */
109         this.fetchNamespace = function(nms) {
110             if ('undefined' == typeof nms || null == nms || !_T._reservedNMS[nms]) {
111                 return null;
112             }
113 
114             var ret = null;
115             try {
116                 //blackberries have problems as well in older non webkit versions
117                 if (!_T.browser.isIE) {
118                     //in ie 6 and 7 we get an error entry despite the suppression
119                     ret = _T.globalEval("window." + nms);
120                 }
121                 //namespace could point to numeric or boolean hence full
122                 //save check
123 
124             } catch (e) {/*wanted*/
125             }
126             //ie fallback for some ie versions path because it cannot eval namespaces
127             //ie in any version does not like that particularily
128             //we do it the hard way now
129             if ('undefined' != typeof ret && null != ret) {
130                 return ret;
131             }
132             return _T._manuallyResolveNMS(nms);
133 
134         };
135 
136         _T._manuallyResolveNMS = function(nms) {
137              //ie fallback for some ie versions path because it cannot eval namespaces
138             //ie in any version does not like that particularily
139             //we do it the hard way now
140 
141             nms = nms.split(/\./);
142             var ret = window;
143             var len = nms.length;
144 
145             for (var cnt = 0; cnt < len; cnt++) {
146                 ret = ret[nms[cnt]];
147                 if ('undefined' == typeof ret || null == ret) {
148                     return null;
149                 }
150             }
151             return ret;
152         };
153 
154         /**
155          * Backported from dojo
156          * a failsafe string determination method
157          * (since in javascript String != "" typeof alone fails!)
158          * @param {Object} it  the object to be checked for being a string
159          * @return {boolean} true in case of being a string false otherwise
160          */
161         this.isString = function(/*anything*/ it) {
162             //	summary:
163             //		Return true if it is a String
164             return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); // Boolean
165         };
166 
167         /**
168          * reserves a namespace in the specific scope
169          *
170          * usage:
171          * if(_T.reserve("org.apache.myfaces.MyUtils")) {
172          *      org.apache.myfaces.MyUtils = function() {
173          *      }
174          * }
175          *
176          * reserves a namespace and if the namespace is new the function itself is reserved
177          *
178          *
179          *
180          * or:
181          * _T.reserve("org.apache.myfaces.MyUtils", function() { .. });
182          *
183          * reserves a namespace and if not already registered directly applies the function the namespace
184          *
185          * note for now the reserved namespaces reside as global maps justl like jsf.js but
186          * we also use a speedup index which is kept internally to reduce the number of evals or loops to walk through those
187          * namespaces (eval is a heavy operation and loops even only for namespace resolution introduce (O)2 runtime
188          * complexity while a simple map lookup is (O)log n with additional speedup from the engine.
189          *
190          *
191          * @param {String} nms
192          * @returns {boolean} true if it was not provided
193          * false otherwise for further action
194          */
195         this.reserveNamespace = function(nms, obj) {
196 
197             if (!_T.isString(nms)) {
198                 throw Error("Namespace must be a string with . as delimiter");
199             }
200             if (_T._reservedNMS[nms] || null != _T.fetchNamespace(nms)) {
201                 return false;
202             }
203 
204             var entries = nms.split(/\./);
205             var currNms = window;
206 
207             var tmpNmsName = [];
208             var  UDEF = "undefined";
209             for (var cnt = 0; cnt < entries.length; cnt++) {
210                 var subNamespace = entries[cnt];
211                 tmpNmsName.push(subNamespace);
212                 if (UDEF == typeof currNms[subNamespace]) {
213                     currNms[subNamespace] = {};
214                 }
215                 if (cnt == entries.length - 1 && UDEF != typeof obj) {
216                     currNms[subNamespace] = obj;
217                 } else {
218                     currNms = currNms[subNamespace];
219                 }
220                 _T._reservedNMS[tmpNmsName.join(".")] = true;
221             }
222             return true;
223         };
224 
225         /**
226          * iterates over all registered singletons in the namespace
227          * @param operator a closure which applies a certain function
228          * on the namespace singleton
229          */
230         this.iterateSingletons = function(operator) {
231             var singletons = _T._registeredSingletons;
232             for(var key in singletons) {
233                 var nms = _T.fetchNamespace(key);
234                 operator(nms);
235             }
236         };
237         /**
238          * iterates over all registered singletons in the namespace
239          * @param operator a closure which applies a certain function
240          * on the namespace singleton
241          */
242         this.iterateClasses = function(operator) {
243             var classes = _T._registeredClasses;
244             for(var cnt  = 0; cnt < classes.length; cnt++) {
245                 operator(classes[cnt], cnt);
246             }
247         };
248 
249         /**
250          * check if an element exists in the root
251          * also allows to check for subelements
252          * usage
253          * _T.exists(rootElem,"my.name.space")
254          * @param {Object} root the root element
255          * @param {String} subNms the namespace
256          */
257         this.exists = function(root, subNms) {
258             if (!root) {
259                 return false;
260             }
261             //special case locally reserved namespace
262             if (root == window && _T._reservedNMS[subNms]) {
263                 return true;
264             }
265 
266             //initial condition root set element not set or null
267             //equals to element exists
268             if (!subNms) {
269                 return true;
270             }
271             var UDEF = "undefined";
272             try {
273                 //special condition subnamespace exists as full blown key with . instead of function map
274                 if (UDEF != typeof root[subNms]) {
275                     return true;
276                 }
277 
278                 //crossported from the dojo toolkit
279                 // summary: determine if an object supports a given method
280                 // description: useful for longer api chains where you have to test each object in the chain
281                 var p = subNms.split(".");
282                 var len = p.length;
283                 for (var i = 0; i < len; i++) {
284                     //the original dojo code here was false because
285                     //they were testing against ! which bombs out on exists
286                     //which has a value set to false
287                     // (TODO send in a bugreport to the Dojo people)
288 
289                     if (UDEF == typeof root[p[i]]) {
290                         return false;
291                     } // Boolean
292                     root = root[p[i]];
293                 }
294                 return true; // Boolean
295 
296             } catch (e) {
297                 //ie (again) has a special handling for some object attributes here which automatically throw an unspecified error if not existent
298                 return false;
299             }
300         };
301 
302 
303 
304         /**
305          * fetches a global config entry
306          * @param {String} configName the name of the configuration entry
307          * @param {Object} defaultValue
308          *
309          * @return either the config entry or if none is given the default value
310          */
311         this.getGlobalConfig = function(configName, defaultValue) {
312             /**
313              * note we could use exists but this is an heavy operation, since the config name usually
314              * given this function here is called very often
315              * is a single entry without . in between we can do the lighter shortcut
316              */
317             return (myfaces["config"] && 'undefined' != typeof myfaces.config[configName] ) ?
318                     myfaces.config[configName]
319                     :
320                     defaultValue;
321         };
322 
323         /**
324          * gets the local or global options with local ones having higher priority
325          * if no local or global one was found then the default value is given back
326          *
327          * @param {String} configName the name of the configuration entry
328          * @param {String} localOptions the local options root for the configuration myfaces as default marker is added implicitely
329          *
330          * @param {Object} defaultValue
331          *
332          * @return either the config entry or if none is given the default value
333          */
334         this.getLocalOrGlobalConfig = function(localOptions, configName, defaultValue) {
335             /*use(myfaces._impl._util)*/
336             var _local = !!localOptions;
337             var _localResult;
338             var MYFACES = "myfaces";
339 
340             if (_local) {
341                 //note we also do not use exist here due to performance improvement reasons
342                 //not for now we loose the subnamespace capabilities but we do not use them anyway
343                 //this code will give us a performance improvement of 2-3%
344                 _localResult = (localOptions[MYFACES]) ? localOptions[MYFACES][configName] : undefined;
345                 _local = "undefined" != typeof _localResult;
346             }
347 
348             return (!_local) ? _T.getGlobalConfig(configName, defaultValue) : _localResult;
349         };
350 
351         /**
352          * determines the xhr level which either can be
353          * 1 for classical level1
354          * 1.5 for mozillas send as binary implementation
355          * 2 for xhr level 2
356          */
357         this.getXHRLvl = function() {
358             if (!_T.XHR_LEVEL) {
359                 _T.getXHRObject();
360             }
361             return _T.XHR_LEVEL;
362         };
363 
364         /**
365          * encapsulated xhr object which tracks down various implementations
366          * of the xhr object in a browser independent fashion
367          * (ie pre 7 used to have non standard implementations because
368          * the xhr object standard came after IE had implemented it first
369          * newer ie versions adhere to the standard and all other new browsers do anyway)
370          *
371          * @return the xhr object according to the browser type
372          */
373         this.getXHRObject = function() {
374             var _ret = new XMLHttpRequest();
375             //we now check the xhr level
376             //sendAsBinary = 1.5 which means mozilla only
377             //upload attribute present == level2
378             var XHR_LEVEL = "XHR_LEVEL";
379             if (!_T[XHR_LEVEL]) {
380                 var _e = _T.exists;
381                 _T[XHR_LEVEL] = (_e(_ret, "sendAsBinary")) ? 1.5 : 1;
382                 _T[XHR_LEVEL] = (_e(_ret, "upload") && 'undefined' != typeof FormData) ? 2 : _T.XHR_LEVEL;
383             }
384             return _ret;
385         };
386 
387         /**
388          * loads a script and executes it under a global scope
389          * @param {String} src  the source of the script
390          * @param {String} type the type of the script
391          * @param {Boolean} defer  defer true or false, same as the javascript tag defer param
392          * @param {String} charSet the charset under which the script has to be loaded
393          * @param {Boolean} async tells whether the script can be asynchronously loaded or not, currently
394          * not used
395          */
396         this.loadScriptEval = function(src, type, defer, charSet, async) {
397             var xhr = _T.getXHRObject();
398             xhr.open("GET", src, false);
399 
400             if (charSet) {
401                 xhr.setRequestHeader("Content-Type", "application/x-javascript; charset:" + charSet);
402             }
403 
404             xhr.send(null);
405 
406             //since we are synchronous we do it after not with onReadyStateChange
407 
408             if (xhr.readyState == 4) {
409                 if (xhr.status == 200) {
410                     //defer also means we have to process after the ajax response
411                     //has been processed
412                     //we can achieve that with a small timeout, the timeout
413                     //triggers after the processing is done!
414                     if (!defer) {
415                         _T.globalEval(xhr.responseText.replace("\n", "\r\n") + "\r\n//@ sourceURL=" + src);
416                     } else {
417                         //TODO not ideal we maybe ought to move to something else here
418                         //but since it is not in use yet, it is ok
419                         setTimeout(function() {
420                             _T.globalEval(xhr.responseText + "\r\n//@ sourceURL=" + src);
421                         }, 1);
422                     }
423                 } else {
424                     throw Error(xhr.responseText);
425                 }
426             } else {
427                 throw Error("Loading of script " + src + " failed ");
428             }
429 
430         };
431 
432         /**
433          * load script functionality which utilizes the browser internal
434          * script loading capabilities
435          *
436          * @param {String} src  the source of the script
437          * @param {String} type the type of the script
438          * @param {Boolean} defer  defer true or false, same as the javascript tag defer param
439          * @param {String} charSet the charset under which the script has to be loaded
440          */
441         this.loadScriptByBrowser = function(src, type, defer, charSet, async) {
442             //if a head is already present then it is safer to simply
443             //use the body, some browsers prevent head alterations
444             //after the first initial rendering
445 
446             //ok this is nasty we have to do a head modification for ie pre 8
447             //the rest can be finely served with body
448             var position = "head";
449             var UDEF = "undefined";
450             try {
451                 var holder = document.getElementsByTagName(position)[0];
452                 if (UDEF == typeof holder || null == holder) {
453                     holder = document.createElement(position);
454                     var html = document.getElementsByTagName("html");
455                     html.appendChild(holder);
456                 }
457                 var script = document.createElement("script");
458 
459                 script.type = type || "text/javascript";
460                 script.src = src;
461                 if (charSet) {
462                     script.charset = charSet;
463                 }
464                 if (defer) {
465                     script.defer = defer;
466                 }
467                 /*html5 capable browsers can deal with script.async for
468                  * proper head loading*/
469                 if (UDEF != typeof script.async) {
470                     script.async = async;
471                 }
472                 holder.appendChild(script);
473 
474             } catch (e) {
475                 //in case of a loading error we retry via eval
476                 return false;
477             }
478 
479             return true;
480         };
481 
482         this.loadScript = function(src, type, defer, charSet, async) {
483             //the chrome engine has a nasty javascript bug which prevents
484             //a correct order of scripts being loaded
485             //if you use script source on the head, we  have to revert
486             //to xhr+ globalEval for those
487             var b = _T.browser;
488             if (!b.isFF && !b.isWebkit && !b.isOpera >= 10) {
489                 _T.loadScriptEval(src, type, defer, charSet);
490             } else {
491                 //only firefox keeps the order, sorry ie...
492                 _T.loadScriptByBrowser(src, type, defer, charSet, async);
493             }
494         };
495 
496         //Base Patterns, Inheritance, Delegation and Singleton
497 
498 
499 
500         /*
501          * prototype based delegation inheritance
502          *
503          * implements prototype delegaton inheritance dest <- a
504          *
505          * usage
506          * <pre>
507          *  var newClass = _T.extends( function (var1, var2) {
508          *                                          _T._callSuper("constructor", var1,var2);
509          *                                     };
510          *                                  ,origClass);
511          *
512          *       newClass.prototype.myMethod = function(arg1) {
513          *              _T._callSuper("myMethod", arg1,"hello world");
514          *       ....
515          *
516          * other option
517          *
518          * myfaces._impl._core._Runtime.extends("myNamespace.newClass", parent, {
519          *                              init: function() {constructor...},
520          *                              method1: function(f1, f2) {},
521          *                              method2: function(f1, f2,f3) {
522          *                                  _T._callSuper("method2", F1,"hello world");
523          *                              }
524          *              });
525          * </p>
526          * @param {function|String} newCls either a unnamed function which can be assigned later or a namespace
527          * @param {function} extendCls the function class to be extended
528          * @param {Object} protoFuncs (Map) an optional map of prototype functions which in case of overwriting a base function get an inherited method
529          *
530          * To explain further
531          * prototype functions:
532          * <pre>
533          *  newClass.prototype.<prototypeFunction>
534          * namspace function
535          *  newCls.<namespaceFunction> = function() {...}
536          *  </pre>
537          */
538 
539         this.extendClass = function(newCls, extendCls, protoFuncs, nmsFuncs) {
540 
541             if (!_T.isString(newCls)) {
542                 throw Error("new class namespace must be of type String");
543             }
544             var className = newCls;
545 
546             if (_T._reservedNMS[newCls]) {
547                 return _T.fetchNamespace(newCls);
548             }
549             var constr = "constructor_";
550             var parClassRef = "_mfClazz";
551             if(!protoFuncs[constr]) {
552               protoFuncs[constr] =  (extendCls[parClassRef]  || (extendCls.prototype && extendCls.prototype[parClassRef])) ?
553                       function() {this._callSuper("constructor_");}: function() {};
554               var assigned = true;
555             }
556 
557             if ('function' != typeof newCls) {
558                 newCls = _reserveClsNms(newCls, protoFuncs);
559                 if (!newCls) return null;
560             }
561             //if the type information is known we use that one
562             //with this info we can inherit from objects also
563             //instead of only from classes
564             //sort of like   this.extendClass(newCls, extendObj._mfClazz...
565 
566             if (extendCls[parClassRef]) {
567                 extendCls = extendCls[parClassRef];
568             }
569 
570             if ('undefined' != typeof extendCls && null != extendCls) {
571                 //first we have to get rid of the constructor calling problem
572                 //problem
573                 var tmpFunc = function() {
574                 };
575                 tmpFunc.prototype = extendCls.prototype;
576 
577                 var newClazz = newCls;
578                 newClazz.prototype = new tmpFunc();
579                 tmpFunc = null;
580                 var clzProto = newClazz.prototype;
581                 clzProto.constructor = newCls;
582                 clzProto._parentCls = extendCls.prototype;
583                 //in case of overrides the namespace is altered with mfclazz
584                 //we want the final namespace
585                 clzProto._nameSpace = className.replace(/(\._mfClazz)+$/,"");
586                 /**
587                  * @ignore
588                  */
589                 clzProto._callSuper = function(methodName) {
590                     var passThrough = (arguments.length == 1) ? [] : Array.prototype.slice.call(arguments, 1);
591                     var accDescLevel = "_mfClsDescLvl";
592                     //we store the descension level of each method under a mapped
593                     //name to avoid name clashes
594                     //to avoid name clashes with internal methods of array
595                     //if we don't do this we trap the callSuper in an endless
596                     //loop after descending one level
597                     var _mappedName = ["_",methodName,"_mf_r"].join("");
598                     this[accDescLevel] = this[accDescLevel] || new Array();
599                     var descLevel = this[accDescLevel];
600                     //we have to detect the descension level
601                     //we now check if we are in a super descension for the current method already
602                     //if not we are on this level
603                     var _oldDescLevel = this[accDescLevel][_mappedName] || this;
604                     //we now step one level down
605                     var _parentCls = _oldDescLevel._parentCls;
606                     var ret = null;
607                     try {
608                         //we now store the level position as new descension level for callSuper
609                         descLevel[_mappedName] = _parentCls;
610                         //and call the code on this
611                         if(!_parentCls[methodName]) {
612                             throw Error("Method _callSuper('"+ methodName+"')  called from "+className+" Method does not exist ");
613                         }
614                         ret = _parentCls[methodName].apply(this, passThrough);
615                     } finally {
616                         descLevel[_mappedName] = _oldDescLevel;
617                     }
618                     if('undefined' != typeof ret) {
619                         return ret;
620                     }
621                 };
622                 //reference to its own type
623                 clzProto[parClassRef] = newCls;
624                 _T._registeredClasses.push(clzProto);
625             }
626 
627             //we now map the function map in
628             _T._applyFuncs(newCls, protoFuncs, true);
629             //we could add inherited but that would make debugging harder
630             //see http://www.ruzee.com/blog/2008/12/javascript-inheritance-via-prototypes-and-closures on how to do it
631 
632             _T._applyFuncs(newCls, nmsFuncs, false);
633 
634             return newCls;
635         };
636 
637 
638 
639         /**
640          * Extends a class and puts a singleton instance at the reserved namespace instead
641          * of its original class
642          *
643          * @param {function|String} newCls either a unnamed function which can be assigned later or a namespace
644          * @param {function} extendsCls the function class to be extended
645          * @param {Object} protoFuncs (Map) an optional map of prototype functions which in case of overwriting a base function get an inherited method
646          */
647         this.singletonExtendClass = function(newCls, extendsCls, protoFuncs, nmsFuncs) {
648             _T._registeredSingletons[newCls] = true;
649             return _T._makeSingleton(_T.extendClass, newCls, extendsCls, protoFuncs, nmsFuncs);
650         };
651 
652 
653 
654         //since the object is self contained and only
655         //can be delegated we can work with real private
656         //functions here, the other parts of the
657         //system have to emulate them via _ prefixes
658         this._makeSingleton = function(ooFunc, newCls, delegateObj, protoFuncs, nmsFuncs) {
659             if (_T._reservedNMS[newCls]) {
660                 return _T._reservedNMS[newCls];
661             }
662 
663             var clazz = ooFunc(newCls + "._mfClazz", delegateObj, protoFuncs, nmsFuncs);
664             if (clazz != null) {
665                 _T.applyToGlobalNamespace(newCls, new clazz());
666             }
667             return _T.fetchNamespace(newCls)["_mfClazz"] = clazz;
668         };
669 
670         //internal class namespace reservation depending on the type (string or function)
671         var _reserveClsNms = function(newCls, protoFuncs) {
672             var constr = null;
673             var UDEF = "undefined";
674             if (UDEF != typeof protoFuncs && null != protoFuncs) {
675                 constr = (UDEF != typeof null != protoFuncs['constructor_'] && null != protoFuncs['constructor_']) ? protoFuncs['constructor_'] : function() {
676                 };
677             } else {
678                 constr = function() {
679                 };
680             }
681 
682             if (!_T.reserveNamespace(newCls, constr)) {
683                 return null;
684             }
685             newCls = _T.fetchNamespace(newCls);
686             return newCls;
687         };
688 
689         this._applyFuncs = function (newCls, funcs, proto) {
690             if (funcs) {
691                 for (var key in funcs) {
692                     //constructor already passed, callSuper already assigned
693                     if ('undefined' == typeof key || null == key || key == "_callSuper") {
694                         continue;
695                     }
696                     if (!proto)
697                         newCls[key] = funcs[key];
698                     else
699                         newCls.prototype[key] = funcs[key];
700                 }
701             }
702         };
703 
704         /**
705          * general type assertion routine
706          *
707          * @param probe the probe to be checked for the correct type
708          * @param theType the type to be checked for
709          */
710         this.assertType = function(probe, theType) {
711             return _T.isString(theType) ? probe == typeof theType : probe instanceof theType;
712         };
713 
714         /**
715          * onload wrapper for chaining the onload cleanly
716          * @param func the function which should be added to the load
717          * chain (note we cannot rely on return values here, hence jsf.util.chain will fail)
718          */
719         this.addOnLoad = function(target, func) {
720             var oldonload = (target) ? target.onload : null;
721             target.onload = (!oldonload) ? func : function() {
722                 try {
723                     oldonload();
724                 } catch (e) {
725                     throw e;
726                 } finally {
727                     func();
728                 }
729             };
730         };
731 
732         /**
733          * returns the internationalisation setting
734          * for the given browser so that
735          * we can i18n our messages
736          *
737          * @returns a map with following entires:
738          * <ul>
739          *      <li>language: the lowercase language iso code</li>
740          *      <li>variant: the uppercase variant iso code</li>
741          * </ul>
742          * null is returned if the browser fails to determine the language settings
743          */
744         this.getLanguage = function(lOverride) {
745             var deflt = {language: "en", variant: "UK"}; //default language and variant
746             try {
747                 var lang = lOverride || navigator.language || navigator.browserLanguage;
748                 if (!lang || lang.length < 2) return deflt;
749                 return {
750                     language: lang.substr(0, 2),
751                     variant: (lang.length >= 5) ? lang.substr(3, 5) : null
752                 };
753             } catch(e) {
754                 return deflt;
755             }
756         };
757 
758         //implemented in extruntime
759         this.singletonDelegateObj = function()  {};
760 
761         /**
762         * browser detection code
763         * cross ported from dojo 1.2
764         *
765         * dojos browser detection code is very sophisticated
766         * hence we port it over it allows a very fine grained detection of
767         * browsers including the version number
768         * this however only can work out if the user
769         * does not alter the user agent, which they normally dont!
770         *
771         * the exception is the ie detection which relies on specific quirks in ie
772         */
773        var n = navigator;
774        var dua = n.userAgent,
775                dav = n.appVersion,
776                tv = parseFloat(dav);
777        var _T = this;
778        _T.browser = {};
779        myfaces._impl.core._EvalHandlers.browser = _T.browser;
780        var d = _T.browser;
781 
782        if (dua.indexOf("Opera") >= 0) {
783            _T.isOpera = tv;
784        }
785        if (dua.indexOf("AdobeAIR") >= 0) {
786            d.isAIR = 1;
787        }
788        if (dua.indexOf("BlackBerry") >= 0) {
789            d.isBlackBerry = tv;
790        }
791        d.isKhtml = (dav.indexOf("Konqueror") >= 0) ? tv : 0;
792        d.isWebKit = parseFloat(dua.split("WebKit/")[1]) || undefined;
793        d.isChrome = parseFloat(dua.split("Chrome/")[1]) || undefined;
794 
795        // safari detection derived from:
796        //		http://developer.apple.com/internet/safari/faq.html#anchor2
797        //		http://developer.apple.com/internet/safari/uamatrix.html
798        var index = Math.max(dav.indexOf("WebKit"), dav.indexOf("Safari"), 0);
799        if (index && !d.isChrome) {
800            // try to grab the explicit Safari version first. If we don't get
801            // one, look for less than 419.3 as the indication that we're on something
802            // "Safari 2-ish".
803            d.isSafari = parseFloat(dav.split("Version/")[1]);
804            if (!d.isSafari || parseFloat(dav.substr(index + 7)) <= 419.3) {
805                d.isSafari = 2;
806            }
807        }
808 
809        //>>excludeStart("webkitMobile", kwArgs.webkitMobile);
810 
811        if (dua.indexOf("Gecko") >= 0 && !d.isKhtml && !d.isWebKit) {
812            d.isMozilla = d.isMoz = tv;
813        }
814        if (d.isMoz) {
815            //We really need to get away from _T. Consider a sane isGecko approach for the future.
816            d.isFF = parseFloat(dua.split("Firefox/")[1] || dua.split("Minefield/")[1] || dua.split("Shiretoko/")[1]) || undefined;
817        }
818 
819        if (document.all && !d.isOpera && !d.isBlackBerry) {
820            d.isIE = parseFloat(dav.split("MSIE ")[1]) || undefined;
821            d.isIEMobile = parseFloat(dua.split("IEMobile")[1]);
822            //In cases where the page has an HTTP header or META tag with
823            //X-UA-Compatible, then it is in emulation mode, for a previous
824            //version. Make sure isIE reflects the desired version.
825            //document.documentMode of 5 means quirks mode.
826 
827            /** @namespace document.documentMode */
828            if (d.isIE >= 8 && document.documentMode != 5) {
829                d.isIE = document.documentMode;
830            }
831        }
832     };
833 }
834 
835