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", "text/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                         //we moved the sourceurl notation to # instead of @ because ie does not cover it correctly
416                         //newer browsers understand # including ie since windows 8.1
417                         //see http://updates.html5rocks.com/2013/06/sourceMappingURL-and-sourceURL-syntax-changed
418                         _T.globalEval(xhr.responseText.replace("\n", "\r\n") + "\r\n//# sourceURL=" + src);
419                     } else {
420                         //TODO not ideal we maybe ought to move to something else here
421                         //but since it is not in use yet, it is ok
422                         setTimeout(function() {
423                             _T.globalEval(xhr.responseText.replace("\n", "\r\n") + "\r\n//# sourceURL=" + src);
424                         }, 1);
425                     }
426                 } else {
427                     throw Error(xhr.responseText);
428                 }
429             } else {
430                 throw Error("Loading of script " + src + " failed ");
431             }
432 
433         };
434 
435         /**
436          * load script functionality which utilizes the browser internal
437          * script loading capabilities
438          *
439          * @param {String} src  the source of the script
440          * @param {String} type the type of the script
441          * @param {Boolean} defer  defer true or false, same as the javascript tag defer param
442          * @param {String} charSet the charset under which the script has to be loaded
443          */
444         this.loadScriptByBrowser = function(src, type, defer, charSet, async) {
445             //if a head is already present then it is safer to simply
446             //use the body, some browsers prevent head alterations
447             //after the first initial rendering
448 
449             //ok this is nasty we have to do a head modification for ie pre 8
450             //the rest can be finely served with body
451             var position = "head";
452             var UDEF = "undefined";
453             try {
454                 var holder = document.getElementsByTagName(position)[0];
455                 if (UDEF == typeof holder || null == holder) {
456                     holder = document.createElement(position);
457                     var html = document.getElementsByTagName("html");
458                     html.appendChild(holder);
459                 }
460                 var script = document.createElement("script");
461 
462                 script.type = type || "text/javascript";
463                 script.src = src;
464                 if (charSet) {
465                     script.charset = charSet;
466                 }
467                 if (defer) {
468                     script.defer = defer;
469                 }
470                 /*html5 capable browsers can deal with script.async for
471                  * proper head loading*/
472                 if (UDEF != typeof script.async) {
473                     script.async = async;
474                 }
475                 holder.appendChild(script);
476 
477             } catch (e) {
478                 //in case of a loading error we retry via eval
479                 return false;
480             }
481 
482             return true;
483         };
484 
485         this.loadScript = function(src, type, defer, charSet, async) {
486             //the chrome engine has a nasty javascript bug which prevents
487             //a correct order of scripts being loaded
488             //if you use script source on the head, we  have to revert
489             //to xhr+ globalEval for those
490             var b = _T.browser;
491             if (!b.isFF && !b.isWebkit && !b.isOpera >= 10) {
492                 _T.loadScriptEval(src, type, defer, charSet);
493             } else {
494                 //only firefox keeps the order, sorry ie...
495                 _T.loadScriptByBrowser(src, type, defer, charSet, async);
496             }
497         };
498 
499         //Base Patterns, Inheritance, Delegation and Singleton
500 
501 
502 
503         /*
504          * prototype based delegation inheritance
505          *
506          * implements prototype delegaton inheritance dest <- a
507          *
508          * usage
509          * <pre>
510          *  var newClass = _T.extends( function (var1, var2) {
511          *                                          _T._callSuper("constructor", var1,var2);
512          *                                     };
513          *                                  ,origClass);
514          *
515          *       newClass.prototype.myMethod = function(arg1) {
516          *              _T._callSuper("myMethod", arg1,"hello world");
517          *       ....
518          *
519          * other option
520          *
521          * myfaces._impl._core._Runtime.extends("myNamespace.newClass", parent, {
522          *                              init: function() {constructor...},
523          *                              method1: function(f1, f2) {},
524          *                              method2: function(f1, f2,f3) {
525          *                                  _T._callSuper("method2", F1,"hello world");
526          *                              }
527          *              });
528          * </p>
529          * @param {function|String} newCls either a unnamed function which can be assigned later or a namespace
530          * @param {function} extendCls the function class to be extended
531          * @param {Object} protoFuncs (Map) an optional map of prototype functions which in case of overwriting a base function get an inherited method
532          *
533          * To explain further
534          * prototype functions:
535          * <pre>
536          *  newClass.prototype.<prototypeFunction>
537          * namspace function
538          *  newCls.<namespaceFunction> = function() {...}
539          *  </pre>
540          */
541 
542         this.extendClass = function(newCls, extendCls, protoFuncs, nmsFuncs) {
543 
544             if (!_T.isString(newCls)) {
545                 throw Error("new class namespace must be of type String");
546             }
547             var className = newCls;
548 
549             if (_T._reservedNMS[newCls]) {
550                 return _T.fetchNamespace(newCls);
551             }
552             var constr = "constructor_";
553             var parClassRef = "_mfClazz";
554             if(!protoFuncs[constr]) {
555               protoFuncs[constr] =  (extendCls[parClassRef]  || (extendCls.prototype && extendCls.prototype[parClassRef])) ?
556                       function() {this._callSuper("constructor_");}: function() {};
557               var assigned = true;
558             }
559 
560             if ('function' != typeof newCls) {
561                 newCls = _reserveClsNms(newCls, protoFuncs);
562                 if (!newCls) return null;
563             }
564             //if the type information is known we use that one
565             //with this info we can inherit from objects also
566             //instead of only from classes
567             //sort of like   this.extendClass(newCls, extendObj._mfClazz...
568 
569             if (extendCls[parClassRef]) {
570                 extendCls = extendCls[parClassRef];
571             }
572 
573             if ('undefined' != typeof extendCls && null != extendCls) {
574                 //first we have to get rid of the constructor calling problem
575                 //problem
576                 var tmpFunc = function() {
577                 };
578                 tmpFunc.prototype = extendCls.prototype;
579 
580                 var newClazz = newCls;
581                 newClazz.prototype = new tmpFunc();
582                 tmpFunc = null;
583                 var clzProto = newClazz.prototype;
584                 clzProto.constructor = newCls;
585                 clzProto._parentCls = extendCls.prototype;
586                 //in case of overrides the namespace is altered with mfclazz
587                 //we want the final namespace
588                 clzProto._nameSpace = className.replace(/(\._mfClazz)+$/,"");
589                 /**
590                  * @ignore
591                  */
592                 clzProto._callSuper = function(methodName) {
593                     var passThrough = (arguments.length == 1) ? [] : Array.prototype.slice.call(arguments, 1);
594                     var accDescLevel = "_mfClsDescLvl";
595                     //we store the descension level of each method under a mapped
596                     //name to avoid name clashes
597                     //to avoid name clashes with internal methods of array
598                     //if we don't do this we trap the callSuper in an endless
599                     //loop after descending one level
600                     var _mappedName = ["_",methodName,"_mf_r"].join("");
601                     this[accDescLevel] = this[accDescLevel] || new Array();
602                     var descLevel = this[accDescLevel];
603                     //we have to detect the descension level
604                     //we now check if we are in a super descension for the current method already
605                     //if not we are on this level
606                     var _oldDescLevel = this[accDescLevel][_mappedName] || this;
607                     //we now step one level down
608                     var _parentCls = _oldDescLevel._parentCls;
609                     var ret = null;
610                     try {
611                         //we now store the level position as new descension level for callSuper
612                         descLevel[_mappedName] = _parentCls;
613                         //and call the code on this
614                         if(!_parentCls[methodName]) {
615                             throw Error("Method _callSuper('"+ methodName+"')  called from "+className+" Method does not exist ");
616                         }
617                         ret = _parentCls[methodName].apply(this, passThrough);
618                     } finally {
619                         descLevel[_mappedName] = _oldDescLevel;
620                     }
621                     if('undefined' != typeof ret) {
622                         return ret;
623                     }
624                 };
625                 //reference to its own type
626                 clzProto[parClassRef] = newCls;
627                 _T._registeredClasses.push(clzProto);
628             }
629 
630             //we now map the function map in
631             _T._applyFuncs(newCls, protoFuncs, true);
632             //we could add inherited but that would make debugging harder
633             //see http://www.ruzee.com/blog/2008/12/javascript-inheritance-via-prototypes-and-closures on how to do it
634 
635             _T._applyFuncs(newCls, nmsFuncs, false);
636 
637             return newCls;
638         };
639 
640 
641 
642         /**
643          * Extends a class and puts a singleton instance at the reserved namespace instead
644          * of its original class
645          *
646          * @param {function|String} newCls either a unnamed function which can be assigned later or a namespace
647          * @param {function} extendsCls the function class to be extended
648          * @param {Object} protoFuncs (Map) an optional map of prototype functions which in case of overwriting a base function get an inherited method
649          */
650         this.singletonExtendClass = function(newCls, extendsCls, protoFuncs, nmsFuncs) {
651             _T._registeredSingletons[newCls] = true;
652             return _T._makeSingleton(_T.extendClass, newCls, extendsCls, protoFuncs, nmsFuncs);
653         };
654 
655 
656 
657         //since the object is self contained and only
658         //can be delegated we can work with real private
659         //functions here, the other parts of the
660         //system have to emulate them via _ prefixes
661         this._makeSingleton = function(ooFunc, newCls, delegateObj, protoFuncs, nmsFuncs) {
662             if (_T._reservedNMS[newCls]) {
663                 return _T._reservedNMS[newCls];
664             }
665 
666             var clazz = ooFunc(newCls + "._mfClazz", delegateObj, protoFuncs, nmsFuncs);
667             if (clazz != null) {
668                 _T.applyToGlobalNamespace(newCls, new clazz());
669             }
670             return _T.fetchNamespace(newCls)["_mfClazz"] = clazz;
671         };
672 
673         //internal class namespace reservation depending on the type (string or function)
674         var _reserveClsNms = function(newCls, protoFuncs) {
675             var constr = null;
676             var UDEF = "undefined";
677             if (UDEF != typeof protoFuncs && null != protoFuncs) {
678                 constr = (UDEF != typeof null != protoFuncs['constructor_'] && null != protoFuncs['constructor_']) ? protoFuncs['constructor_'] : function() {
679                 };
680             } else {
681                 constr = function() {
682                 };
683             }
684 
685             if (!_T.reserveNamespace(newCls, constr)) {
686                 return null;
687             }
688             newCls = _T.fetchNamespace(newCls);
689             return newCls;
690         };
691 
692         this._applyFuncs = function (newCls, funcs, proto) {
693             if (funcs) {
694                 for (var key in funcs) {
695                     //constructor already passed, callSuper already assigned
696                     if ('undefined' == typeof key || null == key || key == "_callSuper") {
697                         continue;
698                     }
699                     if (!proto)
700                         newCls[key] = funcs[key];
701                     else
702                         newCls.prototype[key] = funcs[key];
703                 }
704             }
705         };
706 
707         /**
708          * general type assertion routine
709          *
710          * @param probe the probe to be checked for the correct type
711          * @param theType the type to be checked for
712          */
713         this.assertType = function(probe, theType) {
714             return _T.isString(theType) ? probe == typeof theType : probe instanceof theType;
715         };
716 
717         /**
718          * onload wrapper for chaining the onload cleanly
719          * @param func the function which should be added to the load
720          * chain (note we cannot rely on return values here, hence jsf.util.chain will fail)
721          */
722         this.addOnLoad = function(target, func) {
723             var oldonload = (target) ? target.onload : null;
724             target.onload = (!oldonload) ? func : function() {
725                 try {
726                     oldonload();
727                 } catch (e) {
728                     throw e;
729                 } finally {
730                     func();
731                 }
732             };
733         };
734 
735         /**
736          * returns the internationalisation setting
737          * for the given browser so that
738          * we can i18n our messages
739          *
740          * @returns a map with following entires:
741          * <ul>
742          *      <li>language: the lowercase language iso code</li>
743          *      <li>variant: the uppercase variant iso code</li>
744          * </ul>
745          * null is returned if the browser fails to determine the language settings
746          */
747         this.getLanguage = function(lOverride) {
748             var deflt = {language: "en", variant: "UK"}; //default language and variant
749             try {
750                 var lang = lOverride || navigator.language || navigator.browserLanguage;
751                 if (!lang || lang.length < 2) return deflt;
752                 return {
753                     language: lang.substr(0, 2),
754                     variant: (lang.length >= 5) ? lang.substr(3, 5) : null
755                 };
756             } catch(e) {
757                 return deflt;
758             }
759         };
760 
761         //implemented in extruntime
762         this.singletonDelegateObj = function()  {};
763 
764         /**
765         * browser detection code
766         * cross ported from dojo 1.2
767         *
768         * dojos browser detection code is very sophisticated
769         * hence we port it over it allows a very fine grained detection of
770         * browsers including the version number
771         * this however only can work out if the user
772         * does not alter the user agent, which they normally dont!
773         *
774         * the exception is the ie detection which relies on specific quirks in ie
775         */
776        var n = navigator;
777        var dua = n.userAgent,
778                dav = n.appVersion,
779                tv = parseFloat(dav);
780        var _T = this;
781        _T.browser = {};
782        myfaces._impl.core._EvalHandlers.browser = _T.browser;
783        var d = _T.browser;
784 
785        if (dua.indexOf("Opera") >= 0) {
786            _T.isOpera = tv;
787        }
788        if (dua.indexOf("AdobeAIR") >= 0) {
789            d.isAIR = 1;
790        }
791        if (dua.indexOf("BlackBerry") >= 0) {
792            d.isBlackBerry = tv;
793        }
794        d.isKhtml = (dav.indexOf("Konqueror") >= 0) ? tv : 0;
795        d.isWebKit = parseFloat(dua.split("WebKit/")[1]) || undefined;
796        d.isChrome = parseFloat(dua.split("Chrome/")[1]) || undefined;
797 
798        // safari detection derived from:
799        //		http://developer.apple.com/internet/safari/faq.html#anchor2
800        //		http://developer.apple.com/internet/safari/uamatrix.html
801        var index = Math.max(dav.indexOf("WebKit"), dav.indexOf("Safari"), 0);
802        if (index && !d.isChrome) {
803            // try to grab the explicit Safari version first. If we don't get
804            // one, look for less than 419.3 as the indication that we're on something
805            // "Safari 2-ish".
806            d.isSafari = parseFloat(dav.split("Version/")[1]);
807            if (!d.isSafari || parseFloat(dav.substr(index + 7)) <= 419.3) {
808                d.isSafari = 2;
809            }
810        }
811 
812        //>>excludeStart("webkitMobile", kwArgs.webkitMobile);
813 
814        if (dua.indexOf("Gecko") >= 0 && !d.isKhtml && !d.isWebKit) {
815            d.isMozilla = d.isMoz = tv;
816        }
817        if (d.isMoz) {
818            //We really need to get away from _T. Consider a sane isGecko approach for the future.
819            d.isFF = parseFloat(dua.split("Firefox/")[1] || dua.split("Minefield/")[1] || dua.split("Shiretoko/")[1]) || undefined;
820        }
821 
822        if (document.all && !d.isOpera && !d.isBlackBerry) {
823            d.isIE = parseFloat(dav.split("MSIE ")[1]) || undefined;
824            d.isIEMobile = parseFloat(dua.split("IEMobile")[1]);
825            //In cases where the page has an HTTP header or META tag with
826            //X-UA-Compatible, then it is in emulation mode, for a previous
827            //version. Make sure isIE reflects the desired version.
828            //document.documentMode of 5 means quirks mode.
829 
830            /** @namespace document.documentMode */
831            if (d.isIE >= 8 && document.documentMode != 5) {
832                d.isIE = document.documentMode;
833            }
834        }
835     };
836 }
837 
838