1 /* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to you under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18 /** 19 * Runtime/Startup class 20 * this is the central class which initializes all base mechanisms 21 * used by the rest of the system such as 22 * a) namespacing system 23 * b) browser detection 24 * c) loose configuration coupling 25 * d) utils methods to fetch the implementation 26 * e) ajaxed script loading 27 * f) global eval (because it is used internally) 28 * g) Structural base patterns as singleton, delegate and inheritance 29 * 30 * Note this class is self contained and must!!! be loaded 31 * as absolute first class before going into anything else 32 * 33 * 34 */ 35 /** @namespace myfaces._impl.core._Runtime*/ 36 37 myfaces._impl.core = (myfaces._impl.core) ? myfaces._impl.core : {}; 38 //now this is the only time we have to do this cascaded and manually 39 //for the rest of the classes our reserveNamespace function will do the trick 40 //Note, this class uses the classical closure approach (to save code) 41 //it cannot be inherited by our inheritance mechanism, but must be delegated 42 //if you want to derive from it 43 //closures and prototype inheritance do not mix, closures and delegation however do 44 /** 45 * @ignore 46 */ 47 if (!myfaces._impl.core._Runtime) { 48 /** 49 * @memberOf myfaces._impl.core 50 * @namespace 51 * @name _Runtime 52 */ 53 myfaces._impl.core._Runtime = new function() { 54 //the rest of the namespaces can be handled by our namespace feature 55 //helper to avoid unneeded hitches 56 /** 57 * @borrows myfaces._impl.core._Runtime as _T 58 */ 59 var _T = this; 60 61 //namespace idx to speed things up by hitting eval way less 62 this._reservedNMS = {}; 63 this._registeredSingletons = {}; 64 this._registeredClasses = []; 65 /** 66 * replacement counter for plugin classes 67 */ 68 this._classReplacementCnt = 0; 69 70 /** 71 * global eval on scripts 72 * @param {String} code 73 * @name myfaces._impl.core._Runtime.globalEval 74 * @function 75 */ 76 _T.globalEval = function(code) { 77 return myfaces._impl.core._EvalHandlers.globalEval(code); 78 }; 79 80 /** 81 * applies an object to a namespace 82 * basically does what bla.my.name.space = obj does 83 * note we cannot use var myNameSpace = fetchNamespace("my.name.space") 84 * myNameSpace = obj because the result of fetch is already the object 85 * which the namespace points to, hence this function 86 * 87 * @param {String} nms the namespace to be assigned to 88 * @param {Object} obj the object to be assigned 89 * @name myfaces._impl.core._Runtime.applyToGlobalNamespace 90 * @function 91 */ 92 _T.applyToGlobalNamespace = function(nms, obj) { 93 var splitted = nms.split(/\./); 94 if (splitted.length == 1) { 95 window[nms] = obj; 96 return; 97 } 98 var parent = splitted.slice(0, splitted.length - 1); 99 var child = splitted[splitted.length - 1]; 100 var parentNamespace = _T.fetchNamespace(parent.join(".")); 101 parentNamespace[child] = obj; 102 }; 103 104 /** 105 * fetches the object the namespace points to 106 * @param {String} nms the namespace which has to be fetched 107 * @return the object the namespace points to or null if nothing is found 108 */ 109 this.fetchNamespace = function(nms) { 110 if ('undefined' == typeof nms || null == nms || !_T._reservedNMS[nms]) { 111 return null; 112 } 113 114 var ret = null; 115 try { 116 //blackberries have problems as well in older non webkit versions 117 if (!_T.browser.isIE) { 118 //in ie 6 and 7 we get an error entry despite the suppression 119 ret = _T.globalEval("window." + nms); 120 } 121 //namespace could point to numeric or boolean hence full 122 //save check 123 124 } catch (e) {/*wanted*/ 125 } 126 //ie fallback for some ie versions path because it cannot eval namespaces 127 //ie in any version does not like that particularily 128 //we do it the hard way now 129 if ('undefined' != typeof ret && null != ret) { 130 return ret; 131 } 132 return _T._manuallyResolveNMS(nms); 133 134 }; 135 136 _T._manuallyResolveNMS = function(nms) { 137 //ie fallback for some ie versions path because it cannot eval namespaces 138 //ie in any version does not like that particularily 139 //we do it the hard way now 140 141 nms = nms.split(/\./); 142 var ret = window; 143 var len = nms.length; 144 145 for (var cnt = 0; cnt < len; cnt++) { 146 ret = ret[nms[cnt]]; 147 if ('undefined' == typeof ret || null == ret) { 148 return null; 149 } 150 } 151 return ret; 152 }; 153 154 /** 155 * Backported from dojo 156 * a failsafe string determination method 157 * (since in javascript String != "" typeof alone fails!) 158 * @param {Object} it the object to be checked for being a string 159 * @return {boolean} true in case of being a string false otherwise 160 */ 161 this.isString = function(/*anything*/ it) { 162 // summary: 163 // Return true if it is a String 164 return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); // Boolean 165 }; 166 167 /** 168 * reserves a namespace in the specific scope 169 * 170 * usage: 171 * if(_T.reserve("org.apache.myfaces.MyUtils")) { 172 * org.apache.myfaces.MyUtils = function() { 173 * } 174 * } 175 * 176 * reserves a namespace and if the namespace is new the function itself is reserved 177 * 178 * 179 * 180 * or: 181 * _T.reserve("org.apache.myfaces.MyUtils", function() { .. }); 182 * 183 * reserves a namespace and if not already registered directly applies the function the namespace 184 * 185 * note for now the reserved namespaces reside as global maps justl like jsf.js but 186 * we also use a speedup index which is kept internally to reduce the number of evals or loops to walk through those 187 * namespaces (eval is a heavy operation and loops even only for namespace resolution introduce (O)2 runtime 188 * complexity while a simple map lookup is (O)log n with additional speedup from the engine. 189 * 190 * 191 * @param {String} nms 192 * @returns {boolean} true if it was not provided 193 * false otherwise for further action 194 */ 195 this.reserveNamespace = function(nms, obj) { 196 197 if (!_T.isString(nms)) { 198 throw Error("Namespace must be a string with . as delimiter"); 199 } 200 if (_T._reservedNMS[nms] || null != _T.fetchNamespace(nms)) { 201 return false; 202 } 203 204 var entries = nms.split(/\./); 205 var currNms = window; 206 207 var tmpNmsName = []; 208 var UDEF = "undefined"; 209 for (var cnt = 0; cnt < entries.length; cnt++) { 210 var subNamespace = entries[cnt]; 211 tmpNmsName.push(subNamespace); 212 if (UDEF == typeof currNms[subNamespace]) { 213 currNms[subNamespace] = {}; 214 } 215 if (cnt == entries.length - 1 && UDEF != typeof obj) { 216 currNms[subNamespace] = obj; 217 } else { 218 currNms = currNms[subNamespace]; 219 } 220 _T._reservedNMS[tmpNmsName.join(".")] = true; 221 } 222 return true; 223 }; 224 225 /** 226 * iterates over all registered singletons in the namespace 227 * @param operator a closure which applies a certain function 228 * on the namespace singleton 229 */ 230 this.iterateSingletons = function(operator) { 231 var singletons = _T._registeredSingletons; 232 for(var key in singletons) { 233 var nms = _T.fetchNamespace(key); 234 operator(nms); 235 } 236 }; 237 /** 238 * iterates over all registered singletons in the namespace 239 * @param operator a closure which applies a certain function 240 * on the namespace singleton 241 */ 242 this.iterateClasses = function(operator) { 243 var classes = _T._registeredClasses; 244 for(var cnt = 0; cnt < classes.length; cnt++) { 245 operator(classes[cnt], cnt); 246 } 247 }; 248 249 /** 250 * check if an element exists in the root 251 * also allows to check for subelements 252 * usage 253 * _T.exists(rootElem,"my.name.space") 254 * @param {Object} root the root element 255 * @param {String} subNms the namespace 256 */ 257 this.exists = function(root, subNms) { 258 if (!root) { 259 return false; 260 } 261 //special case locally reserved namespace 262 if (root == window && _T._reservedNMS[subNms]) { 263 return true; 264 } 265 266 //initial condition root set element not set or null 267 //equals to element exists 268 if (!subNms) { 269 return true; 270 } 271 var UDEF = "undefined"; 272 try { 273 //special condition subnamespace exists as full blown key with . instead of function map 274 if (UDEF != typeof root[subNms]) { 275 return true; 276 } 277 278 //crossported from the dojo toolkit 279 // summary: determine if an object supports a given method 280 // description: useful for longer api chains where you have to test each object in the chain 281 var p = subNms.split("."); 282 var len = p.length; 283 for (var i = 0; i < len; i++) { 284 //the original dojo code here was false because 285 //they were testing against ! which bombs out on exists 286 //which has a value set to false 287 // (TODO send in a bugreport to the Dojo people) 288 289 if (UDEF == typeof root[p[i]]) { 290 return false; 291 } // Boolean 292 root = root[p[i]]; 293 } 294 return true; // Boolean 295 296 } catch (e) { 297 //ie (again) has a special handling for some object attributes here which automatically throw an unspecified error if not existent 298 return false; 299 } 300 }; 301 302 303 304 /** 305 * fetches a global config entry 306 * @param {String} configName the name of the configuration entry 307 * @param {Object} defaultValue 308 * 309 * @return either the config entry or if none is given the default value 310 */ 311 this.getGlobalConfig = function(configName, defaultValue) { 312 /** 313 * note we could use exists but this is an heavy operation, since the config name usually 314 * given this function here is called very often 315 * is a single entry without . in between we can do the lighter shortcut 316 */ 317 return (myfaces["config"] && 'undefined' != typeof myfaces.config[configName] ) ? 318 myfaces.config[configName] 319 : 320 defaultValue; 321 }; 322 323 /** 324 * gets the local or global options with local ones having higher priority 325 * if no local or global one was found then the default value is given back 326 * 327 * @param {String} configName the name of the configuration entry 328 * @param {String} localOptions the local options root for the configuration myfaces as default marker is added implicitely 329 * 330 * @param {Object} defaultValue 331 * 332 * @return either the config entry or if none is given the default value 333 */ 334 this.getLocalOrGlobalConfig = function(localOptions, configName, defaultValue) { 335 /*use(myfaces._impl._util)*/ 336 var _local = !!localOptions; 337 var _localResult; 338 var MYFACES = "myfaces"; 339 340 if (_local) { 341 //note we also do not use exist here due to performance improvement reasons 342 //not for now we loose the subnamespace capabilities but we do not use them anyway 343 //this code will give us a performance improvement of 2-3% 344 _localResult = (localOptions[MYFACES]) ? localOptions[MYFACES][configName] : undefined; 345 _local = "undefined" != typeof _localResult; 346 } 347 348 return (!_local) ? _T.getGlobalConfig(configName, defaultValue) : _localResult; 349 }; 350 351 /** 352 * determines the xhr level which either can be 353 * 1 for classical level1 354 * 1.5 for mozillas send as binary implementation 355 * 2 for xhr level 2 356 */ 357 this.getXHRLvl = function() { 358 if (!_T.XHR_LEVEL) { 359 _T.getXHRObject(); 360 } 361 return _T.XHR_LEVEL; 362 }; 363 364 /** 365 * encapsulated xhr object which tracks down various implementations 366 * of the xhr object in a browser independent fashion 367 * (ie pre 7 used to have non standard implementations because 368 * the xhr object standard came after IE had implemented it first 369 * newer ie versions adhere to the standard and all other new browsers do anyway) 370 * 371 * @return the xhr object according to the browser type 372 */ 373 this.getXHRObject = function() { 374 var _ret = new XMLHttpRequest(); 375 //we now check the xhr level 376 //sendAsBinary = 1.5 which means mozilla only 377 //upload attribute present == level2 378 var XHR_LEVEL = "XHR_LEVEL"; 379 if (!_T[XHR_LEVEL]) { 380 var _e = _T.exists; 381 _T[XHR_LEVEL] = (_e(_ret, "sendAsBinary")) ? 1.5 : 1; 382 _T[XHR_LEVEL] = (_e(_ret, "upload") && 'undefined' != typeof FormData) ? 2 : _T.XHR_LEVEL; 383 } 384 return _ret; 385 }; 386 387 /** 388 * loads a script and executes it under a global scope 389 * @param {String} src the source of the script 390 * @param {String} type the type of the script 391 * @param {Boolean} defer defer true or false, same as the javascript tag defer param 392 * @param {String} charSet the charset under which the script has to be loaded 393 * @param {Boolean} async tells whether the script can be asynchronously loaded or not, currently 394 * not used 395 */ 396 this.loadScriptEval = function(src, type, defer, charSet, async) { 397 var xhr = _T.getXHRObject(); 398 xhr.open("GET", src, false); 399 400 if (charSet) { 401 xhr.setRequestHeader("Content-Type", "application/x-javascript; charset:" + charSet); 402 } 403 404 xhr.send(null); 405 406 //since we are synchronous we do it after not with onReadyStateChange 407 408 if (xhr.readyState == 4) { 409 if (xhr.status == 200) { 410 //defer also means we have to process after the ajax response 411 //has been processed 412 //we can achieve that with a small timeout, the timeout 413 //triggers after the processing is done! 414 if (!defer) { 415 _T.globalEval(xhr.responseText.replace("\n", "\r\n") + "\r\n//@ sourceURL=" + src); 416 } else { 417 //TODO not ideal we maybe ought to move to something else here 418 //but since it is not in use yet, it is ok 419 setTimeout(function() { 420 _T.globalEval(xhr.responseText + "\r\n//@ sourceURL=" + src); 421 }, 1); 422 } 423 } else { 424 throw Error(xhr.responseText); 425 } 426 } else { 427 throw Error("Loading of script " + src + " failed "); 428 } 429 430 }; 431 432 /** 433 * load script functionality which utilizes the browser internal 434 * script loading capabilities 435 * 436 * @param {String} src the source of the script 437 * @param {String} type the type of the script 438 * @param {Boolean} defer defer true or false, same as the javascript tag defer param 439 * @param {String} charSet the charset under which the script has to be loaded 440 */ 441 this.loadScriptByBrowser = function(src, type, defer, charSet, async) { 442 //if a head is already present then it is safer to simply 443 //use the body, some browsers prevent head alterations 444 //after the first initial rendering 445 446 //ok this is nasty we have to do a head modification for ie pre 8 447 //the rest can be finely served with body 448 var position = "head"; 449 var UDEF = "undefined"; 450 try { 451 var holder = document.getElementsByTagName(position)[0]; 452 if (UDEF == typeof holder || null == holder) { 453 holder = document.createElement(position); 454 var html = document.getElementsByTagName("html"); 455 html.appendChild(holder); 456 } 457 var script = document.createElement("script"); 458 459 script.type = type || "text/javascript"; 460 script.src = src; 461 if (charSet) { 462 script.charset = charSet; 463 } 464 if (defer) { 465 script.defer = defer; 466 } 467 /*html5 capable browsers can deal with script.async for 468 * proper head loading*/ 469 if (UDEF != typeof script.async) { 470 script.async = async; 471 } 472 holder.appendChild(script); 473 474 } catch (e) { 475 //in case of a loading error we retry via eval 476 return false; 477 } 478 479 return true; 480 }; 481 482 this.loadScript = function(src, type, defer, charSet, async) { 483 //the chrome engine has a nasty javascript bug which prevents 484 //a correct order of scripts being loaded 485 //if you use script source on the head, we have to revert 486 //to xhr+ globalEval for those 487 var b = _T.browser; 488 if (!b.isFF && !b.isWebkit && !b.isOpera >= 10) { 489 _T.loadScriptEval(src, type, defer, charSet); 490 } else { 491 //only firefox keeps the order, sorry ie... 492 _T.loadScriptByBrowser(src, type, defer, charSet, async); 493 } 494 }; 495 496 //Base Patterns, Inheritance, Delegation and Singleton 497 498 499 500 /* 501 * prototype based delegation inheritance 502 * 503 * implements prototype delegaton inheritance dest <- a 504 * 505 * usage 506 * <pre> 507 * var newClass = _T.extends( function (var1, var2) { 508 * _T._callSuper("constructor", var1,var2); 509 * }; 510 * ,origClass); 511 * 512 * newClass.prototype.myMethod = function(arg1) { 513 * _T._callSuper("myMethod", arg1,"hello world"); 514 * .... 515 * 516 * other option 517 * 518 * myfaces._impl._core._Runtime.extends("myNamespace.newClass", parent, { 519 * init: function() {constructor...}, 520 * method1: function(f1, f2) {}, 521 * method2: function(f1, f2,f3) { 522 * _T._callSuper("method2", F1,"hello world"); 523 * } 524 * }); 525 * </p> 526 * @param {function|String} newCls either a unnamed function which can be assigned later or a namespace 527 * @param {function} extendCls the function class to be extended 528 * @param {Object} protoFuncs (Map) an optional map of prototype functions which in case of overwriting a base function get an inherited method 529 * 530 * To explain further 531 * prototype functions: 532 * <pre> 533 * newClass.prototype.<prototypeFunction> 534 * namspace function 535 * newCls.<namespaceFunction> = function() {...} 536 * </pre> 537 */ 538 539 this.extendClass = function(newCls, extendCls, protoFuncs, nmsFuncs) { 540 541 if (!_T.isString(newCls)) { 542 throw Error("new class namespace must be of type String"); 543 } 544 var className = newCls; 545 546 if (_T._reservedNMS[newCls]) { 547 return _T.fetchNamespace(newCls); 548 } 549 var constr = "constructor_"; 550 var parClassRef = "_mfClazz"; 551 if(!protoFuncs[constr]) { 552 protoFuncs[constr] = (extendCls[parClassRef] || (extendCls.prototype && extendCls.prototype[parClassRef])) ? 553 function() {this._callSuper("constructor_");}: function() {}; 554 var assigned = true; 555 } 556 557 if ('function' != typeof newCls) { 558 newCls = _reserveClsNms(newCls, protoFuncs); 559 if (!newCls) return null; 560 } 561 //if the type information is known we use that one 562 //with this info we can inherit from objects also 563 //instead of only from classes 564 //sort of like this.extendClass(newCls, extendObj._mfClazz... 565 566 if (extendCls[parClassRef]) { 567 extendCls = extendCls[parClassRef]; 568 } 569 570 if ('undefined' != typeof extendCls && null != extendCls) { 571 //first we have to get rid of the constructor calling problem 572 //problem 573 var tmpFunc = function() { 574 }; 575 tmpFunc.prototype = extendCls.prototype; 576 577 var newClazz = newCls; 578 newClazz.prototype = new tmpFunc(); 579 tmpFunc = null; 580 var clzProto = newClazz.prototype; 581 clzProto.constructor = newCls; 582 clzProto._parentCls = extendCls.prototype; 583 //in case of overrides the namespace is altered with mfclazz 584 //we want the final namespace 585 clzProto._nameSpace = className.replace(/(\._mfClazz)+$/,""); 586 /** 587 * @ignore 588 */ 589 clzProto._callSuper = function(methodName) { 590 var passThrough = (arguments.length == 1) ? [] : Array.prototype.slice.call(arguments, 1); 591 var accDescLevel = "_mfClsDescLvl"; 592 //we store the descension level of each method under a mapped 593 //name to avoid name clashes 594 //to avoid name clashes with internal methods of array 595 //if we don't do this we trap the callSuper in an endless 596 //loop after descending one level 597 var _mappedName = ["_",methodName,"_mf_r"].join(""); 598 this[accDescLevel] = this[accDescLevel] || new Array(); 599 var descLevel = this[accDescLevel]; 600 //we have to detect the descension level 601 //we now check if we are in a super descension for the current method already 602 //if not we are on this level 603 var _oldDescLevel = this[accDescLevel][_mappedName] || this; 604 //we now step one level down 605 var _parentCls = _oldDescLevel._parentCls; 606 var ret = null; 607 try { 608 //we now store the level position as new descension level for callSuper 609 descLevel[_mappedName] = _parentCls; 610 //and call the code on this 611 if(!_parentCls[methodName]) { 612 throw Error("Method _callSuper('"+ methodName+"') called from "+className+" Method does not exist "); 613 } 614 ret = _parentCls[methodName].apply(this, passThrough); 615 } finally { 616 descLevel[_mappedName] = _oldDescLevel; 617 } 618 if('undefined' != typeof ret) { 619 return ret; 620 } 621 }; 622 //reference to its own type 623 clzProto[parClassRef] = newCls; 624 _T._registeredClasses.push(clzProto); 625 } 626 627 //we now map the function map in 628 _T._applyFuncs(newCls, protoFuncs, true); 629 //we could add inherited but that would make debugging harder 630 //see http://www.ruzee.com/blog/2008/12/javascript-inheritance-via-prototypes-and-closures on how to do it 631 632 _T._applyFuncs(newCls, nmsFuncs, false); 633 634 return newCls; 635 }; 636 637 638 639 /** 640 * Extends a class and puts a singleton instance at the reserved namespace instead 641 * of its original class 642 * 643 * @param {function|String} newCls either a unnamed function which can be assigned later or a namespace 644 * @param {function} extendsCls the function class to be extended 645 * @param {Object} protoFuncs (Map) an optional map of prototype functions which in case of overwriting a base function get an inherited method 646 */ 647 this.singletonExtendClass = function(newCls, extendsCls, protoFuncs, nmsFuncs) { 648 _T._registeredSingletons[newCls] = true; 649 return _T._makeSingleton(_T.extendClass, newCls, extendsCls, protoFuncs, nmsFuncs); 650 }; 651 652 653 654 //since the object is self contained and only 655 //can be delegated we can work with real private 656 //functions here, the other parts of the 657 //system have to emulate them via _ prefixes 658 this._makeSingleton = function(ooFunc, newCls, delegateObj, protoFuncs, nmsFuncs) { 659 if (_T._reservedNMS[newCls]) { 660 return _T._reservedNMS[newCls]; 661 } 662 663 var clazz = ooFunc(newCls + "._mfClazz", delegateObj, protoFuncs, nmsFuncs); 664 if (clazz != null) { 665 _T.applyToGlobalNamespace(newCls, new clazz()); 666 } 667 return _T.fetchNamespace(newCls)["_mfClazz"] = clazz; 668 }; 669 670 //internal class namespace reservation depending on the type (string or function) 671 var _reserveClsNms = function(newCls, protoFuncs) { 672 var constr = null; 673 var UDEF = "undefined"; 674 if (UDEF != typeof protoFuncs && null != protoFuncs) { 675 constr = (UDEF != typeof null != protoFuncs['constructor_'] && null != protoFuncs['constructor_']) ? protoFuncs['constructor_'] : function() { 676 }; 677 } else { 678 constr = function() { 679 }; 680 } 681 682 if (!_T.reserveNamespace(newCls, constr)) { 683 return null; 684 } 685 newCls = _T.fetchNamespace(newCls); 686 return newCls; 687 }; 688 689 this._applyFuncs = function (newCls, funcs, proto) { 690 if (funcs) { 691 for (var key in funcs) { 692 //constructor already passed, callSuper already assigned 693 if ('undefined' == typeof key || null == key || key == "_callSuper") { 694 continue; 695 } 696 if (!proto) 697 newCls[key] = funcs[key]; 698 else 699 newCls.prototype[key] = funcs[key]; 700 } 701 } 702 }; 703 704 /** 705 * general type assertion routine 706 * 707 * @param probe the probe to be checked for the correct type 708 * @param theType the type to be checked for 709 */ 710 this.assertType = function(probe, theType) { 711 return _T.isString(theType) ? probe == typeof theType : probe instanceof theType; 712 }; 713 714 /** 715 * onload wrapper for chaining the onload cleanly 716 * @param func the function which should be added to the load 717 * chain (note we cannot rely on return values here, hence jsf.util.chain will fail) 718 */ 719 this.addOnLoad = function(target, func) { 720 var oldonload = (target) ? target.onload : null; 721 target.onload = (!oldonload) ? func : function() { 722 try { 723 oldonload(); 724 } catch (e) { 725 throw e; 726 } finally { 727 func(); 728 } 729 }; 730 }; 731 732 /** 733 * returns the internationalisation setting 734 * for the given browser so that 735 * we can i18n our messages 736 * 737 * @returns a map with following entires: 738 * <ul> 739 * <li>language: the lowercase language iso code</li> 740 * <li>variant: the uppercase variant iso code</li> 741 * </ul> 742 * null is returned if the browser fails to determine the language settings 743 */ 744 this.getLanguage = function(lOverride) { 745 var deflt = {language: "en", variant: "UK"}; //default language and variant 746 try { 747 var lang = lOverride || navigator.language || navigator.browserLanguage; 748 if (!lang || lang.length < 2) return deflt; 749 return { 750 language: lang.substr(0, 2), 751 variant: (lang.length >= 5) ? lang.substr(3, 5) : null 752 }; 753 } catch(e) { 754 return deflt; 755 } 756 }; 757 758 //implemented in extruntime 759 this.singletonDelegateObj = function() {}; 760 761 //for modern browsers we do not need it anymore 762 //there we can rely on browser capabilities checks 763 //we only have one xml special case so webkit and opera is the 764 //only detection we still need for the minimal case 765 //TODO determine how to eliminate this check 766 //minimal modern should be able to cope without any browser checks 767 //at all 768 //with a direct capabilities check 769 _T.browser = {}; 770 var d = _T.browser; 771 d.isWebKit = parseFloat(navigator.userAgent.split("WebKit/")[1]) || undefined; 772 }; 773 } 774 775