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