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