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