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 /**
 38 * @namespace
 39 * @name window
 40 * @description supplimental window methods.
 41 */
 42 
 43 
 44 
 45 if(!window.myfaces) {
 46     /**
 47      * @namespace
 48      * @name myfaces
 49      */
 50     var myfaces = new function() {};
 51     window.myfaces = myfaces;
 52 }
 53 
 54 
 55 
 56 /**
 57 * @memberOf myfaces
 58 * @namespace
 59 * @name _impl
 60 */
 61 myfaces._impl = (myfaces._impl) ? myfaces._impl : {};
 62 /**
 63  * @memberOf myfaces._impl
 64  * @namespace
 65  * @name core
 66  */
 67 myfaces._impl.core = (myfaces._impl.core) ? myfaces._impl.core : {};
 68 //now this is the only time we have to do this cascaded and manually
 69 //for the rest of the classes our reserveNamespace function will do the trick
 70 //Note, this class uses the classical closure approach (to save code)
 71 //it cannot be inherited by our inheritance mechanism, but must be delegated
 72 //if you want to derive from it
 73 //closures and prototype inheritance do not mix, closures and delegation however do
 74 /**
 75  * @ignore
 76  */
 77 if (!myfaces._impl.core._Runtime) {
 78     /**
 79      * @memberOf myfaces._impl.core
 80      * @namespace
 81      * @name _Runtime
 82      */
 83     myfaces._impl.core._Runtime = new function() {
 84         //the rest of the namespaces can be handled by our namespace feature
 85         //helper to avoid unneeded hitches
 86         /**
 87          * @borrows myfaces._impl.core._Runtime as _T
 88          */
 89         var _T = this;
 90 
 91         //namespace idx to speed things up by hitting eval way less
 92         this._reservedNMS = {};
 93 
 94         /**
 95          * replacement counter for plugin classes
 96          */
 97         this._classReplacementCnt = 0;
 98 
 99         /*cascaded eval methods depending upon the browser*/
100 
101         /**
102          * @function
103          * @param code
104 
105          *
106          * evals a script globally using exec script (ie6 fallback)
107          * @param {String} code the code which has to be evaluated
108          * @borrows myfaces._impl.core._Runtime as _T
109          */
110         _T._evalExecScript = function(code) {
111             //execScript definitely only for IE otherwise we might have a custom
112             //window extension with undefined behavior on our necks
113             //window.execScript does not return anything
114             //on htmlunit it return "null object"
115             var ret = window.execScript(code);
116             if ('undefined' != typeof ret && ret == "null" /*htmlunit bug*/) {
117                 return null;
118             }
119             return ret;
120         };
121 
122         /**
123          * flakey head appendix method which does not work in the correct
124          * order or at all for all modern browsers
125          * but seems to be the only method which works on blackberry correctly
126          * hence we are going to use it as fallback
127          *
128          * @param {String} code the code part to be evaled
129          * @borrows myfaces._impl.core._Runtime as _T
130          */
131         _T._evalBBOld = function(code) {
132             var location = document.getElementsByTagName("head")[0] || document.documentElement;
133             var placeHolder = document.createElement("script");
134             placeHolder.type = "text/javascript";
135             placeHolder.text = code;
136             location.insertBefore(placeHolder, location.firstChild);
137             location.removeChild(placeHolder);
138             return null;
139         };
140 
141         /**
142          * @name myfaces._impl.core._Runtime._standardGlobalEval
143          * @private
144          * @param {String} code
145          */
146         _T._standardGlobalEval = function(code) {
147             //fix which works in a cross browser way
148             //we used to scope an anonymous function
149             //but I think this is better
150             //the reason is firefox applies a wrong scope
151             //if we call eval by not scoping
152 
153             var gEval = function () {
154 
155                 var ret = window.eval.call(window, code);
156                 if ('undefined' == typeof ret) return null;
157                 return ret;
158             };
159             var ret = gEval();
160             if ('undefined' == typeof ret) return null;
161             return ret;
162         };
163 
164         /**
165          * global eval on scripts
166          * @param {String}�code
167          * @name myfaces._impl.core._Runtime.globalEval
168          * @function
169          */
170         _T.globalEval = function(code) {
171             //TODO add a config param which allows to evaluate global scripts even if the call
172             //is embedded in an iframe
173             //We lazy init the eval type upon the browsers
174             //capabilities
175             if ('undefined' == typeof _T._evalType) {
176                 _T._evalType = window.execScript ? "_evalExecScript" : null;
177                 _T._evalType = !_T._evalType && window.eval && (!_T.browser.isBlackBerry || _T.browser.isBlackBerry >= 6) ? "_standardGlobalEval" : null;
178                 _T._evalType = (window.eval && !_T._evalType) ? "_evalBBOld" : null;
179             }
180             if (_T._evalType) {
181                 return _T[_T._evalType](code);
182             }
183             //we probably have covered all browsers, but this is a safety net which might be triggered
184             //by some foreign browser which is not covered by the above cases
185             eval.call(window, code);
186             return null;
187         };
188 
189         /**
190          * applies an object to a namespace
191          * basically does what bla.my.name.space = obj does
192          * note we cannot use var myNameSpace = fetchNamespace("my.name.space")
193          * myNameSpace = obj because the result of fetch is already the object
194          * which the namespace points to, hence this function
195          *
196          * @param {String} nms the namespace to be assigned to
197          * @param {Any}�obj the  object to be assigned
198          * @name myfaces._impl.core._Runtime.applyToGlobalNamespace
199          * @function
200          */
201         _T.applyToGlobalNamespace = function(nms, obj) {
202             var splitted = nms.split(/\./);
203             if (splitted.length == 1) {
204                 window[nms] = obj;
205                 return;
206             }
207             var parent = splitted.slice(0, splitted.length - 1);
208             var child = splitted[splitted.length - 1];
209             var parentNamespace = _T.fetchNamespace(parent.join("."));
210             parentNamespace[child] = obj;
211         };
212 
213         /**
214          * fetches the object the namespace points to
215          * @param {String} nms the namespace which has to be fetched
216          * @return the object the namespace points to or null if nothing is found
217          */
218         this.fetchNamespace = function(nms) {
219             if ('undefined' == typeof nms || null == nms || !_T._reservedNMS[nms]) {
220                 return null;
221             }
222 
223             var ret = null;
224             try {
225                 //blackberries have problems as well in older non webkit versions
226                 if (!_T.browser.isIE) {
227                     //in ie 6 and 7 we get an error entry despite the suppression
228                     ret = _T.globalEval("window." + nms);
229                 }
230                 //namespace could point to numeric or boolean hence full
231                 //save check
232 
233             } catch (e) {/*wanted*/
234             }
235             //ie fallback for some ie versions path because it cannot eval namespaces
236             //ie in any version does not like that particularily
237             //we do it the hard way now
238             if ('undefined' != typeof ret && null != ret) {
239                 return ret;
240             }
241             nms = nms.split(/\./);
242             ret = window;
243             var len = nms.length;
244 
245             for (var cnt = 0; cnt < len; cnt++) {
246                 ret = ret[nms[cnt]];
247                 if ('undefined' == typeof ret || null == ret) {
248                     return null;
249                 }
250             }
251             return ret;
252 
253         };
254 
255         /**
256          * Backported from dojo
257          * a failsafe string determination method
258          * (since in javascript String != "" typeof alone fails!)
259          * @param {Object} it  the object to be checked for being a string
260          * @return {boolean} true in case of being a string false otherwise
261          */
262         this.isString = function(/*anything*/ it) {
263             //	summary:
264             //		Return true if it is a String
265             return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); // Boolean
266         };
267 
268         /**
269          * reserves a namespace in the specific scope
270          *
271          * usage:
272          * if(_T.reserve("org.apache.myfaces.MyUtils")) {
273          *      org.apache.myfaces.MyUtils = function() {
274          *      }
275          * }
276          *
277          * reserves a namespace and if the namespace is new the function itself is reserved
278          *
279          *
280          *
281          * or:
282          * _T.reserve("org.apache.myfaces.MyUtils", function() { .. });
283          *
284          * reserves a namespace and if not already registered directly applies the function the namespace
285          *
286          * note for now the reserved namespaces reside as global maps justl like jsf.js but
287          * we also use a speedup index which is kept internally to reduce the number of evals or loops to walk through those
288          * namespaces (eval is a heavy operation and loops even only for namespace resolution introduce (O)2 runtime
289          * complexity while a simple map lookup is (O)log n with additional speedup from the engine.
290          *
291          *
292          * @param {String} nms
293          * @returns {boolean} true if it was not provided
294          * false otherwise for further action
295          */
296         this.reserveNamespace = function(nms, obj) {
297 
298             if (!_T.isString(nms)) {
299                 throw Error("Namespace must be a string with . as delimiter");
300             }
301             if (_T._reservedNMS[nms] || null != _T.fetchNamespace(nms)) {
302                 return false;
303             }
304 
305             var entries = nms.split(/\./);
306             var currNms = window;
307 
308             var tmpNmsName = [];
309 
310             for (var cnt = 0; cnt < entries.length; cnt++) {
311                 var subNamespace = entries[cnt];
312                 tmpNmsName.push(subNamespace);
313                 if ('undefined' == typeof currNms[subNamespace]) {
314                     currNms[subNamespace] = {};
315                 }
316                 if (cnt == entries.length - 1 && 'undefined' != typeof obj) {
317                     currNms[subNamespace] = obj;
318                 } else {
319                     currNms = currNms[subNamespace];
320                 }
321                 _T._reservedNMS[tmpNmsName.join(".")] = true;
322             }
323             return true;
324         };
325 
326         /**
327          * check if an element exists in the root
328          * also allows to check for subelements
329          * usage
330          * _T.exists(rootElem,"my.name.space")
331          * @param {Object} root the root element
332          * @param {String} subNms the namespace
333          */
334         this.exists = function(root, subNms) {
335             if (!root) {
336                 return false;
337             }
338             //special case locally reserved namespace
339             if (root == window && _T._reservedNMS[subNms]) {
340                 return true;
341             }
342 
343             //initial condition root set element not set or null
344             //equals to element exists
345             if (!subNms) {
346                 return true;
347             }
348             try {
349                 //special condition subnamespace exists as full blown key with . instead of function map
350                 if ('undefined' != typeof root[subNms]) {
351                     return true;
352                 }
353 
354                 //crossported from the dojo toolkit
355                 // summary: determine if an object supports a given method
356                 // description: useful for longer api chains where you have to test each object in the chain
357                 var p = subNms.split(".");
358                 var len = p.length;
359                 for (var i = 0; i < len; i++) {
360                     //the original dojo code here was false because
361                     //they were testing against ! which bombs out on exists
362                     //which has a value set to false
363                     // (TODO send in a bugreport to the Dojo people)
364 
365                     if ('undefined' == typeof root[p[i]]) {
366                         return false;
367                     } // Boolean
368                     root = root[p[i]];
369                 }
370                 return true; // Boolean
371 
372             } catch (e) {
373                 //ie (again) has a special handling for some object attributes here which automatically throw an unspecified error if not existent
374                 return false;
375             }
376         };
377 
378         /**
379          * A dojo like require to load scripts dynamically, note
380          * to use this mechanism you have to set your global config param
381          * myfacesScriptRoot to the root of your script files (aka under normal circumstances
382          * resources/scripts)
383          *
384          * @param {String} nms, the subnamespace to be required
385          */
386         this.require = function(nms) {
387             //namespace exists
388             if (_T.exists(nms)) return;
389             var rootPath = _T.getGlobalConfig("myfacesScriptRoot", "");
390             _T.loadScriptEval(rootPath + "/" + nms.replace(/\./g, "/") + ".js");
391         },
392 
393         /**
394          * fetches a global config entry
395          * @param {String} configName the name of the configuration entry
396          * @param {Object} defaultValue
397          *
398          * @return either the config entry or if none is given the default value
399          */
400          this.getGlobalConfig = function(configName, defaultValue) {
401                     /**
402                      * note we could use exists but this is an heavy operation, since the config name usually
403                      * given this function here is called very often
404                      * is a single entry without . in between we can do the lighter shortcut
405                      */
406                     return (myfaces["config"] && 'undefined' != typeof myfaces.config[configName] ) ?
407                             myfaces.config[configName]
408                             :
409                             defaultValue;
410                 };
411 
412         /**
413          * gets the local or global options with local ones having higher priority
414          * if no local or global one was found then the default value is given back
415          *
416          * @param {String} configName the name of the configuration entry
417          * @param {String} localOptions the local options root for the configuration myfaces as default marker is added implicitely
418          *
419          * @param {Object} defaultValue
420          *
421          * @return either the config entry or if none is given the default value
422          */
423         this.getLocalOrGlobalConfig = function(localOptions, configName, defaultValue) {
424             /*use(myfaces._impl._util)*/
425             var _local = !!localOptions;
426             var _localResult;
427             if (_local) {
428                 //note we also do not use exist here due to performance improvement reasons
429                 //not for now we loose the subnamespace capabilities but we do not use them anyway
430                 //this code will give us a performance improvement of 2-3%
431                 _localResult = (localOptions["myfaces"]) ? localOptions["myfaces"][configName] : undefined;
432                 _local = 'undefined' != typeof _localResult;
433             }
434 
435             return (!_local) ? _T.getGlobalConfig(configName, defaultValue) : _localResult;
436         };
437 
438         /**
439          * determines the xhr level which either can be
440          * 1 for classical level1
441          * 1.5 for mozillas send as binary implementation
442          * 2 for xhr level 2
443          */
444         this.getXHRLvl = function() {
445             if (!_T.XHR_LEVEL) {
446                 _T.getXHRObject();
447             }
448             return _T.XHR_LEVEL;
449         };
450 
451         /**
452          * encapsulated xhr object which tracks down various implementations
453          * of the xhr object in a browser independent fashion
454          * (ie pre 7 used to have non standard implementations because
455          * the xhr object standard came after IE had implemented it first
456          * newer ie versions adhere to the standard and all other new browsers do anyway)
457          *
458          * @return the xhr object according to the browser type
459          */
460         this.getXHRObject = function() {
461             //since this is a global object ie hates it if we do not check for undefined
462             if (window.XMLHttpRequest) {
463                 var _ret = new XMLHttpRequest();
464                 //we now check the xhr level
465                 //sendAsBinary = 1.5 which means mozilla only
466                 //upload attribute present == level2
467                 /*
468                  if (!_T.XHR_LEVEL) {
469                  var _e = _T.exists;
470                  _T.XHR_LEVEL = (_e(_ret, "sendAsBinary")) ? 1.5 : 1;
471                  _T.XHR_LEVEL = (_e(_ret, "upload") && 'undefined' != typeof FormData) ? 2 : _T.XHR_LEVEL;
472                  }*/
473                 return _ret;
474             }
475             //IE
476             try {
477                 _T.XHR_LEVEL = 1;
478                 return new ActiveXObject("Msxml2.XMLHTTP");
479             } catch (e) {
480 
481             }
482             return new ActiveXObject('Microsoft.XMLHTTP');
483         };
484 
485         /**
486          * loads a script and executes it under a global scope
487          * @param {String} src  the source of the script
488          * @param {String} type the type of the script
489          * @param {Boolean} defer  defer true or false, same as the javascript tag defer param
490          * @param {String} charSet the charset under which the script has to be loaded
491          * @param {Boolean} async tells whether the script can be asynchronously loaded or not, currently
492          * not used
493          */
494         this.loadScriptEval = function(src, type, defer, charSet, async) {
495             var xhr = _T.getXHRObject();
496             xhr.open("GET", src, false);
497 
498             if (charSet) {
499                 xhr.setRequestHeader("Content-Type", "application/x-javascript; charset:" + charSet);
500             }
501 
502             xhr.send(null);
503 
504             //since we are synchronous we do it after not with onReadyStateChange
505 
506             if (xhr.readyState == 4) {
507                 if (xhr.status == 200) {
508                     //defer also means we have to process after the ajax response
509                     //has been processed
510                     //we can achieve that with a small timeout, the timeout
511                     //triggers after the processing is done!
512                     if (!defer) {
513                         _T.globalEval(xhr.responseText.replace("\n", "\r\n") + "\r\n//@ sourceURL=" + src);
514                     } else {
515                         //TODO not ideal we maybe ought to move to something else here
516                         //but since it is not in use yet, it is ok
517                         setTimeout(function() {
518                             _T.globalEval(xhr.responseText + "\r\n//@ sourceURL=" + src);
519                         }, 1);
520                     }
521                 } else {
522                     throw Error(xhr.responseText);
523                 }
524             } else {
525                 throw Error("Loading of script " + src + " failed ");
526             }
527 
528         };
529 
530         /**
531          * load script functionality which utilizes the browser internal
532          * script loading capabilities
533          *
534          * @param {String} src  the source of the script
535          * @param {String} type the type of the script
536          * @param {Boolean} defer  defer true or false, same as the javascript tag defer param
537          * @param {String} charSet the charset under which the script has to be loaded
538          */
539         this.loadScriptByBrowser = function(src, type, defer, charSet, async) {
540             //if a head is already present then it is safer to simply
541             //use the body, some browsers prevent head alterations
542             //after the first initial rendering
543 
544             //ok this is nasty we have to do a head modification for ie pre 8
545             //the rest can be finely served with body
546             var d = _T.browser;
547             var position = "head"
548 
549             try {
550                 var holder = document.getElementsByTagName(position)[0];
551                 if ('undefined' == typeof holder || null == holder) {
552                     holder = document.createElement(position);
553                     var html = document.getElementsByTagName("html");
554                     html.appendChild(holder);
555                 }
556                 var script = document.createElement("script");
557 
558                 script.type = type || "text/javascript";
559                 script.src = src;
560                 if (charSet) {
561                     script.charset = charSet;
562                 }
563                 if (defer) {
564                     script.defer = defer;
565                 }
566                 /*html5 capable browsers can deal with script.async for
567                  * proper head loading*/
568                 if ('undefined' != typeof script.async) {
569                     script.async = async;
570                 }
571                 holder.appendChild(script);
572 
573             } catch (e) {
574                 //in case of a loading error we retry via eval
575                 return false;
576             }
577 
578             return true;
579         };
580 
581         this.loadScript = function(src, type, defer, charSet, async) {
582             //the chrome engine has a nasty javascript bug which prevents
583             //a correct order of scripts being loaded
584             //if you use script source on the head, we  have to revert
585             //to xhr+ globalEval for those
586             if (!_T.browser.isFF && !_T.browser.isWebkit && !_T.browser.isOpera >= 10) {
587                 _T.loadScriptEval(src, type, defer, charSet);
588             } else {
589                 //only firefox keeps the order, sorry ie...
590                 _T.loadScriptByBrowser(src, type, defer, charSet, async);
591             }
592         };
593 
594         //Base Patterns, Inheritance, Delegation and Singleton
595 
596         /**
597          * delegation pattern
598          * usage:
599          * this.delegateObject("my.name.space", delegate,
600          * {
601          *  constructor_ :function(bla, bla1) {
602          *      _T._callDelegate("constructor", bla1);
603          *  },
604          *  myFunc: function(yyy) {
605          *      DoSomething;
606          *      _T._callDelegate("someOtherFunc", yyyy);
607          *  }, null
608          * });
609          *
610          * or
611          * usage var newClass = this.delegateObject(
612          * function (var1, var2) {
613          *  _T._callDelegate("constructor", var1,var2);
614          * };
615          * ,delegateObject);
616          * newClass.prototype.myMethod = function(arg1) {
617          *      _T._callDelegate("myMethod", arg1,"hello world");
618          *
619          *
620          * @param {String} newCls the new class name to be generated
621          * @param {Object} delegateObj the delegation object
622          * @param {Map} protoFuncs the prototype functions which should be attached
623          * @param {Map} nmsFuncs the namespace functions which should be attached to the namespace
624          */
625         this.delegateObj = function( newCls, delegateObj, protoFuncs, nmsFuncs) {
626             if (!_T.isString(newCls)) {
627                 throw Error("new class namespace must be of type String");
628             }
629 
630             if ('function' != typeof newCls) {
631                 newCls = _reserveClsNms(newCls, protoFuncs);
632                 if (!newCls) return null;
633             }
634 
635             //central delegation mapping core
636             var proto = newCls.prototype;
637 
638             //the trick here is to isolate the entries to bind the
639             //keys in a private scope see
640             //http://www.ruzee.com/blog/2008/12/javascript-inheritance-via-prototypes-and-closures
641             for (var key in delegateObj) (function(key, delFn) {
642                 //The isolation is needed otherwise the last _key assigend would be picked
643                 //up internally
644                 if (key && typeof delFn == "function") {
645                     proto[key] = function(/*arguments*/) {
646                         return delFn.apply(delegateObj, arguments);
647                     };
648                 }
649             })(key, delegateObj[key]);
650 
651             proto._delegateObj = delegateObj;
652             proto.constructor = newCls;
653 
654             proto._callDelegate = function(methodName) {
655                 var passThrough = (arguments.length == 1) ? [] : Array.prototype.slice.call(arguments, 1);
656                 var ret = this._delegateObj[methodName].apply(this._delegateObj, passThrough);
657                 if ('undefined' != ret) return ret;
658             };
659 
660             //we now map the function map in
661             _applyFuncs(newCls, protoFuncs, true);
662             _applyFuncs(newCls, nmsFuncs, false);
663 
664             return newCls;
665         };
666 
667         /*
668          * prototype based delegation inheritance
669          *
670          * implements prototype delegaton inheritance dest <- a
671          *
672          * usage
673          * <pre>
674          *  var newClass = _T.extends( function (var1, var2) {
675          *                                          _T._callSuper("constructor", var1,var2);
676          *                                     };
677          *                                  ,origClass);
678          *
679          *       newClass.prototype.myMethod = function(arg1) {
680          *              _T._callSuper("myMethod", arg1,"hello world");
681          *       ....
682          *
683          * other option
684          *
685          * myfaces._impl._core._Runtime.extends("myNamespace.newClass", parent, {
686          *                              init: function() {constructor...},
687          *                              method1: function(f1, f2) {},
688          *                              method2: function(f1, f2,f3) {
689          *                                  _T._callSuper("method2", F1,"hello world");
690          *                              }
691          *              });
692          * </p>
693          * @param {function|String} newCls either a unnamed function which can be assigned later or a namespace
694          * @param {function} extendCls the function class to be extended
695          * @param {Object} protoFuncs (Map) an optional map of prototype functions which in case of overwriting a base function get an inherited method
696          *
697          * To explain further
698          * prototype functions:
699          * <pre>
700          *  newClass.prototype.<prototypeFunction>
701          * namspace function
702          *  newCls.<namespaceFunction> = function() {...}
703          *  </pre>
704          */
705 
706         this.extendClass = function(newCls, extendCls, protoFuncs, nmsFuncs) {
707 
708             if (!_T.isString(newCls)) {
709                 throw Error("new class namespace must be of type String");
710             }
711 
712             if (_T._reservedNMS[newCls]) {
713                 return;
714             }
715 
716             if ('function' != typeof newCls) {
717                 newCls = _reserveClsNms(newCls, protoFuncs);
718                 if (!newCls) return null;
719             }
720             //if the type information is known we use that one
721             //with this info we can inherit from objects also
722             //instead of only from classes
723             //sort of like   this.extendClass(newCls, extendObj._mfClazz...
724 
725             if (extendCls._mfClazz) {
726                 extendCls = extendCls._mfClazz;
727             }
728 
729             if ('undefined' != typeof extendCls && null != extendCls) {
730                 //first we have to get rid of the constructor calling problem
731                 //problem
732                 var tmpFunc = function() {
733                 };
734                 tmpFunc.prototype = extendCls.prototype;
735 
736                 var newClazz = newCls;
737                 newClazz.prototype = new tmpFunc();
738                 tmpFunc = null;
739                 newClazz.prototype.constructor = newCls;
740                 newClazz.prototype._parentCls = extendCls.prototype;
741                 /**
742                  * @ignore
743                  */
744                 newClazz.prototype._callSuper = function(methodName) {
745                     var passThrough = (arguments.length == 1) ? [] : Array.prototype.slice.call(arguments, 1);
746 
747                     //we store the descension level of each method under a mapped
748                     //name to avoid name clashes
749                     //to avoid name clashes with internal methods of array
750                     //if we don't do this we trap the callSuper in an endless
751                     //loop after descending one level
752                     var _mappedName = ["_",methodName,"_mf_r"].join("");
753                     this._mfClsDescLvl = this._mfClsDescLvl || new Array();
754                     var descLevel = this._mfClsDescLvl;
755                     //we have to detect the descension level
756                     //we now check if we are in a super descension for the current method already
757                     //if not we are on this level
758                     var _oldDescLevel = this._mfClsDescLvl[_mappedName] || this;
759                     //we now step one level down
760                     var _parentCls = _oldDescLevel._parentCls;
761 
762                     try {
763                         //we now store the level position as new descension level for callSuper
764                         descLevel[_mappedName] = _parentCls;
765                         //and call the code on this
766                         _parentCls[methodName].apply(this, passThrough);
767                     } finally {
768                         descLevel[_mappedName] = _oldDescLevel;
769                     }
770                 };
771                 //reference to its own type
772                 newClazz.prototype._mfClazz = newCls;
773             }
774 
775             //we now map the function map in
776             _applyFuncs(newCls, protoFuncs, true);
777             //we could add inherited but that would make debugging harder
778             //see http://www.ruzee.com/blog/2008/12/javascript-inheritance-via-prototypes-and-closures on how to do it
779 
780             _applyFuncs(newCls, nmsFuncs, false);
781 
782             return newCls;
783         };
784 
785         /**
786          * convenience method which basically replaces an existing class
787          * with a new one under the same namespace, note all old functionality will be
788          * presereced by pushing the original class into an new nampespace
789          *
790          * @param classNms the namespace for the class, must already be existing
791          * @param protoFuncs the new prototype functions which are plugins for the old ones
792          * @param overWrite if set to true replaces the old funcs entirely otherwise just does an implicit
793          * inheritance with super being remapped
794          *
795          * TODO do not use this function yet it needs some refinement, it will be interesting later
796          * anyway
797          */
798         this.pluginClass = function(classNms, protoFuncs, overWrite) {
799             var oldClass = _T.fetchNamespace(classNms);
800             if (!oldClass) throw new Error("The class namespace " + classNms + " is not existent");
801 
802             if (!overWrite) {
803                 var preserveNMS = classNms + "." + ("" + _T._classReplacementCnt++);
804                 _T.reserveNamespace(preserveNMS, oldClass);
805 
806                 return _T.extendClass(classNms, preserveNMS, protoFuncs);
807             } else {
808                 //TODO constructor mapping?
809                 if (protoFuncs.constructor_) {
810                     //TODO needs testing if this works!
811                     newCls.prototype.constructor = protoFuncs.constructor_;
812                 }
813                 _applyFuncs(oldClass, protoFuncs, true);
814             }
815         },
816 
817         /**
818          * Extends a class and puts a singleton instance at the reserved namespace instead
819          * of its original class
820          *
821          * @param {function|String} newCls either a unnamed function which can be assigned later or a namespace
822          * @param {function} extendsCls the function class to be extended
823          * @param {Object} protoFuncs (Map) an optional map of prototype functions which in case of overwriting a base function get an inherited method
824          */
825                 this.singletonExtendClass = function(newCls, extendsCls, protoFuncs, nmsFuncs) {
826                     return _makeSingleton(_T.extendClass, newCls, extendsCls, protoFuncs, nmsFuncs);
827                 };
828 
829         /**
830          * delegation pattern which attached singleton generation
831          *
832          * @param newCls the new namespace object to be generated as singletoin
833          * @param delegateObj the object which has to be delegated
834          * @param protoFuncs the prototype functions which are attached on prototype level
835          * @param nmsFuncs the functions which are attached on the classes namespace level
836          */
837         this.singletonDelegateObj = function(newCls, delegateObj, protoFuncs, nmsFuncs) {
838             if (_T._reservedNMS[newCls]) {
839                 return;
840             }
841             return _makeSingleton(_T.delegateObj, newCls, delegateObj, protoFuncs, nmsFuncs);
842         };
843 
844         //since the object is self contained and only
845         //can be delegated we can work with real private
846         //functions here, the other parts of the
847         //system have to emulate them via _ prefixes
848         var _makeSingleton = function(ooFunc, newCls, delegateObj, protoFuncs, nmsFuncs) {
849             if (_T._reservedNMS[newCls]) {
850                 return;
851             }
852 
853             var clazz = ooFunc(newCls + "._mfClazz", delegateObj, protoFuncs, nmsFuncs);
854             if (clazz != null) {
855                 _T.applyToGlobalNamespace(newCls, new clazz());
856             }
857             _T.fetchNamespace(newCls)["_mfClazz"] = clazz;
858         };
859 
860         //internal class namespace reservation depending on the type (string or function)
861         var _reserveClsNms = function(newCls, protoFuncs) {
862             var constr = null;
863 
864             if ('undefined' != typeof protoFuncs && null != protoFuncs) {
865                 constr = ('undefined' != typeof null != protoFuncs['constructor_'] && null != protoFuncs['constructor_']) ? protoFuncs['constructor_'] : function() {
866                 };
867             } else {
868                 constr = function() {
869                 };
870             }
871 
872             if (!_T.reserveNamespace(newCls, constr)) {
873                 return null;
874             }
875             newCls = _T.fetchNamespace(newCls);
876             return newCls;
877         };
878 
879         var _applyFuncs = function (newCls, funcs, proto) {
880             if (funcs) {
881                 for (var key in funcs) {
882                     //constructor already passed, callSuper already assigned
883                     if ('undefined' == typeof key || null == key || key == "_callSuper") {
884                         continue;
885                     }
886                     if (!proto)
887                         newCls[key] = funcs[key];
888                     else
889                         newCls.prototype[key] = funcs[key];
890                 }
891             }
892         };
893 
894         /**
895          * general type assertion routine
896          *
897          * @param probe the probe to be checked for the correct type
898          * @param theType the type to be checked for
899          */
900         this.assertType = function(probe, theType) {
901             return _T.isString(theType) ? probe == typeof theType : probe instanceof theType;
902         };
903 
904         /**
905          * onload wrapper for chaining the onload cleanly
906          * @param func the function which should be added to the load
907          * chain (note we cannot rely on return values here, hence jsf.util.chain will fail)
908          */
909         this.addOnLoad = function(target, func) {
910             var oldonload = (target) ? target.onload : null;
911             target.onload = (!oldonload) ? func : function() {
912                 try {
913                     oldonload();
914                 } catch (e) {
915                     throw e;
916                 } finally {
917                     func();
918                 }
919             };
920         };
921 
922         /**
923          * returns the internationalisation setting
924          * for the given browser so that
925          * we can i18n our messages
926          *
927          * @returns a map with following entires:
928          * <ul>
929          *      <li>language: the lowercase language iso code</li>
930          *      <li>variant: the uppercase variant iso code</li>
931          * </ul>
932          * null is returned if the browser fails to determine the language settings
933          */
934         this.getLanguage = function(lOverride) {
935             var deflt = {language: "en", variant: "UK"}; //default language and variant
936             try {
937                 var lang = lOverride || navigator.language || navigator.browserLanguage;
938                 if (!lang || lang.length < 2) return deflt;
939                 return {
940                     language: lang.substr(0, 2),
941                     variant: (lang.length >= 5) ? lang.substr(3, 5) : null
942                 };
943             } catch(e) {
944                 return deflt;
945             }
946         };
947 
948         //initial browser detection, we encapsule it in a closure
949         //to drop all temporary variables from ram as soon as possible
950         (function() {
951             /**
952              * browser detection code
953              * cross ported from dojo 1.2
954              *
955              * dojos browser detection code is very sophisticated
956              * hence we port it over it allows a very fine grained detection of
957              * browsers including the version number
958              * this however only can work out if the user
959              * does not alter the user agent, which they normally dont!
960              *
961              * the exception is the ie detection which relies on specific quirks in ie
962              */
963             var n = navigator;
964             var dua = n.userAgent,
965                     dav = n.appVersion,
966                     tv = parseFloat(dav);
967 
968             _T.browser = {};
969             var d = _T.browser;
970 
971             if (dua.indexOf("Opera") >= 0) {
972                 _T.isOpera = tv;
973             }
974             if (dua.indexOf("AdobeAIR") >= 0) {
975                 d.isAIR = 1;
976             }
977             if (dua.indexOf("BlackBerry") >= 0) {
978                 d.isBlackBerry = tv;
979             }
980             d.isKhtml = (dav.indexOf("Konqueror") >= 0) ? tv : 0;
981             d.isWebKit = parseFloat(dua.split("WebKit/")[1]) || undefined;
982             d.isChrome = parseFloat(dua.split("Chrome/")[1]) || undefined;
983 
984             // safari detection derived from:
985             //		http://developer.apple.com/internet/safari/faq.html#anchor2
986             //		http://developer.apple.com/internet/safari/uamatrix.html
987             var index = Math.max(dav.indexOf("WebKit"), dav.indexOf("Safari"), 0);
988             if (index && !d.isChrome) {
989                 // try to grab the explicit Safari version first. If we don't get
990                 // one, look for less than 419.3 as the indication that we're on something
991                 // "Safari 2-ish".
992                 d.isSafari = parseFloat(dav.split("Version/")[1]);
993                 if (!d.isSafari || parseFloat(dav.substr(index + 7)) <= 419.3) {
994                     d.isSafari = 2;
995                 }
996             }
997 
998             //>>excludeStart("webkitMobile", kwArgs.webkitMobile);
999 
1000             if (dua.indexOf("Gecko") >= 0 && !d.isKhtml && !d.isWebKit) {
1001                 d.isMozilla = d.isMoz = tv;
1002             }
1003             if (d.isMoz) {
1004                 //We really need to get away from _T. Consider a sane isGecko approach for the future.
1005                 d.isFF = parseFloat(dua.split("Firefox/")[1] || dua.split("Minefield/")[1] || dua.split("Shiretoko/")[1]) || undefined;
1006             }
1007 
1008             if (document.all && !d.isOpera && !d.isBlackBerry) {
1009                 d.isIE = parseFloat(dav.split("MSIE ")[1]) || undefined;
1010                 d.isIEMobile = parseFloat(dua.split("IEMobile")[1]);
1011                 //In cases where the page has an HTTP header or META tag with
1012                 //X-UA-Compatible, then it is in emulation mode, for a previous
1013                 //version. Make sure isIE reflects the desired version.
1014                 //document.documentMode of 5 means quirks mode.
1015 
1016                 /** @namespace document.documentMode */
1017                 if (d.isIE >= 8 && document.documentMode != 5) {
1018                     d.isIE = document.documentMode;
1019                 }
1020             }
1021         })();
1022 
1023     };
1024 }
1025