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 theoretically we could save some code 18 by 19 defining the parent object as 20 var parent = new Object(); 21 parent.prototype = new myfaces._impl.core._Runtime(); 22 extendClass(function () { 23 }, parent , { 24 But for now we are not doing it the little bit of saved 25 space is not worth the loss of readability 26 */ 27 /** 28 * @memberOf myfaces._impl 29 * @namespace 30 * @name _util 31 */ 32 /** 33 * @class 34 * @name _Lang 35 * @memberOf myfaces._impl._util 36 * @extends myfaces._impl.core._Runtime 37 * @namespace 38 * @description Object singleton for Language related methods, this object singleton 39 * decorates the namespace myfaces._impl.core._Runtime and adds a bunch of new methods to 40 * what _Runtime provided 41 * */ 42 _MF_SINGLTN(_PFX_UTIL + "_Lang", Object, /** @lends myfaces._impl._util._Lang.prototype */ { 43 _processedExceptions: {}, 44 _installedLocale: null, 45 _RT: myfaces._impl.core._Runtime, 46 /** 47 * returns a given localized message upon a given key 48 * basic java log like templating functionality is included 49 * 50 * @param {String} key the key for the message 51 * @param {String} optional default message if none was found 52 * 53 * Additionally you can pass additional arguments, which are used 54 * in the same way java log templates use the params 55 * 56 * @param key 57 */ 58 getMessage: function(key, defaultMessage /*,vararg templateParams*/) { 59 if (!this._installedLocale) { 60 //we first try to install language and variant, if that one fails 61 //we try to install the language only, and if that one fails 62 //we install the base messages 63 this.initLocale(); 64 } 65 var msg = this._installedLocale[key] || defaultMessage || key + " - undefined message"; 66 for (var cnt = 2; cnt < arguments.length; cnt++) { 67 msg = msg.replace(new RegExp(["\\{",cnt - 2,"\\}"].join(""), "g"), new String(arguments[cnt])); 68 } 69 return msg; 70 }, 71 /** 72 * (re)inits the currently installed 73 * messages so that after loading the main scripts 74 * a new locale can be installed optionally 75 * to our i18n subsystem 76 * 77 * @param newLocale locale override 78 */ 79 initLocale: function(newLocale) { 80 if (newLocale) { 81 this._installedLocale = new newLocale(); 82 return; 83 } 84 var language_Variant = this._RT.getLanguage(this._RT.getGlobalConfig("locale")); 85 var langStr = language_Variant ? language_Variant.language : ""; 86 var variantStr = language_Variant ? [language_Variant.language,"_",language_Variant.variant || ""].join("") : ""; 87 var i18nRoot = myfaces._impl.i18n; 88 var i18nHolder = i18nRoot["Messages_" + variantStr] || 89 i18nRoot["Messages_" + langStr] || 90 i18nRoot["Messages"]; 91 this._installedLocale = new i18nHolder(); 92 }, 93 assertType: function(probe, theType) { 94 return this._RT.assertType(probe, theType); 95 }, 96 exists: function(nms, theType) { 97 return this._RT.exists(nms, theType); 98 }, 99 isExceptionProcessed: function(e) { 100 return !! this._processedExceptions[e.toString()]; 101 }, 102 setExceptionProcessed: function(e) { 103 this._processedExceptions[e.toString()] = true; 104 }, 105 clearExceptionProcessed: function() { 106 //ie again 107 for (var key in this._processedExceptions) { 108 this._processedExceptions[key] = null; 109 } 110 this._processedExceptions = {}; 111 }, 112 fetchNamespace : function(namespace) { 113 this._assertStr(namespace, "fetchNamespace", "namespace"); 114 return this._RT.fetchNamespace(namespace); 115 }, 116 reserveNamespace : function(namespace) { 117 this._assertStr(namespace, "reserveNamespace", "namespace"); 118 return this._RT.reserveNamespace(namespace); 119 }, 120 globalEval : function(code) { 121 this._assertStr(code, "globalEval", "code"); 122 return this._RT.globalEval(code); 123 }, 124 /** 125 * determines the correct event depending 126 * on the browsers state 127 * 128 * @param evt incoming event object (note not all browsers 129 * have this) 130 * 131 * @return an event object no matter what is incoming 132 */ 133 getEvent: function(evt) { 134 evt = (!evt) ? window.event || {} : evt; 135 return evt; 136 }, 137 /** 138 * cross port from the dojo lib 139 * browser save event resolution 140 * @param evt the event object 141 * (with a fallback for ie events if none is present) 142 */ 143 getEventTarget: function(evt) { 144 //ie6 and 7 fallback 145 evt = this.getEvent(evt); 146 /** 147 * evt source is defined in the jsf events 148 * seems like some component authors use our code 149 * so we add it here see also 150 * https://issues.apache.org/jira/browse/MYFACES-2458 151 * not entirely a bug but makes sense to add this 152 * behavior. I dont use it that way but nevertheless it 153 * does not break anything so why not 154 * */ 155 var t = evt.srcElement || evt.target || evt.source || null; 156 while ((t) && (t.nodeType != 1)) { 157 t = t.parentNode; 158 } 159 return t; 160 }, 161 /** 162 * consume event in a browser independend manner 163 * @param event the event which should not be propagated anymore 164 */ 165 consumeEvent: function(event) { 166 //w3c model vs ie model again 167 event = event || window.event; 168 (event.stopPropagation) ? event.stopPropagation() : event.cancelBubble = true; 169 }, 170 /** 171 * equalsIgnoreCase, case insensitive comparison of two strings 172 * 173 * @param source 174 * @param destination 175 */ 176 equalsIgnoreCase: function(source, destination) { 177 //either both are not set or null 178 if (!source && !destination) { 179 return true; 180 } 181 //source or dest is set while the other is not 182 if (!source || !destination) return false; 183 //in any other case we do a strong string comparison 184 return source.toLowerCase() === destination.toLowerCase(); 185 }, 186 /** 187 * escapes a strings special chars (crossported from dojo 1.3+) 188 * 189 * @param str the string 190 * 191 * @param except a set of exceptions 192 */ 193 escapeString: function(/*String*/str, /*String?*/except) { 194 // summary: 195 // Adds escape sequences for special characters in regular expressions 196 // except: 197 // a String with special characters to be left unescaped 198 return str.replace(/([\.$?*|:{}\(\)\[\]\\\/\+^])/g, function(ch) { 199 if (except && except.indexOf(ch) != -1) { 200 return ch; 201 } 202 return "\\" + ch; 203 }); // String 204 }, 205 /** 206 * Save document.getElementById (this code was ported over from dojo) 207 * the idea is that either a string or domNode can be passed 208 * @param {Object} reference the reference which has to be byIded 209 */ 210 byId : function(/*object*/ reference) { 211 if (!reference) { 212 throw Error(this.getMessage("ERR_REF_OR_ID", null, "_Lang.byId", "reference")); 213 } 214 return (this.isString(reference)) ? document.getElementById(reference) : reference; 215 }, 216 /** 217 * Helper function to provide a trim with a given splitter regular expression 218 * @param {String} it the string to be trimmed 219 * @param {RegExp} splitter the splitter regular expressiion 220 * 221 * FIXME is this still used? 222 */ 223 trimStringInternal : function(it, splitter) { 224 return this.strToArray(it, splitter).join(splitter); 225 }, 226 /** 227 * String to array function performs a string to array transformation 228 * @param {String} it the string which has to be changed into an array 229 * @param {RegExp} splitter our splitter reglar expression 230 * @return an array of the splitted string 231 */ 232 strToArray : function(/*string*/ it, /*regexp*/ splitter) { 233 // summary: 234 // Return true if it is a String 235 this._assertStr(it, "strToArray", "it"); 236 if (!splitter) { 237 throw Error(this.getMessage("ERR_PARAM_STR_RE", null, "myfaces._impl._util._Lang.strToArray", "splitter")); 238 } 239 var retArr = it.split(splitter); 240 var len = retArr.length; 241 for (var cnt = 0; cnt < len; cnt++) { 242 retArr[cnt] = this.trim(retArr[cnt]); 243 } 244 return retArr; 245 }, 246 _assertStr: function(it, functionName, paramName) { 247 if (!this.isString(it)) { 248 throw Error(this.getMessage("ERR_PARAM_STR", null, "myfaces._impl._util._Lang." + functionName, paramName)); 249 } 250 }, 251 /** 252 * hyperfast trim 253 * http://blog.stevenlevithan.com/archives/faster-trim-javascript 254 * crossported from dojo 255 */ 256 trim : function(/*string*/ str) { 257 this._assertStr(str, "trim", "str"); 258 str = str.replace(/^\s\s*/, ''); 259 var ws = /\s/; 260 var i = str.length; 261 while (ws.test(str.charAt(--i))) { 262 //do nothing 263 } 264 return str.slice(0, i + 1); 265 }, 266 /** 267 * Backported from dojo 268 * a failsafe string determination method 269 * (since in javascript String != "" typeof alone fails!) 270 * @param it {|Object|} the object to be checked for being a string 271 * @return true in case of being a string false otherwise 272 */ 273 isString: function(/*anything*/ it) { 274 // summary: 275 // Return true if it is a String 276 return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); // Boolean 277 }, 278 /** 279 * hitch backported from dojo 280 * hitch allows to assign a function to a dedicated scope 281 * this is helpful in situations when function reassignments 282 * can happen 283 * (notably happens often in lazy xhr code) 284 * 285 * @param {Function} scope of the function to be executed in 286 * @param {Function} method to be executed, the method must be of type function 287 * 288 * @return whatever the executed method returns 289 */ 290 hitch : function(scope, method) { 291 return !scope ? method : function() { 292 return method.apply(scope, arguments || []); 293 }; // Function 294 }, 295 /** 296 * Helper function to merge two maps 297 * into one 298 * @param {Object} dest the destination map 299 * @param {Object} src the source map 300 * @param {boolean} overwrite if set to true the destination is overwritten if the keys exist in both maps 301 **/ 302 mixMaps: function(dest, src, overwrite, blockFilter, whitelistFilter) { 303 if (!dest || !src) { 304 throw Error(this.getMessage("ERR_PARAM_MIXMAPS", null, "_Lang.mixMaps")); 305 } 306 var _undef = "undefined"; 307 for (var key in src) { 308 if (blockFilter && blockFilter[key]) { 309 continue; 310 } 311 if (whitelistFilter && !whitelistFilter[key]) { 312 continue; 313 } 314 if (!overwrite) { 315 /** 316 *we use exists instead of booleans because we cannot rely 317 *on all values being non boolean, we would need an elvis 318 *operator in javascript to shorten this :-( 319 */ 320 dest[key] = (_undef != typeof dest[key]) ? dest[key] : src[key]; 321 } else { 322 dest[key] = (_undef != typeof src[key]) ? src[key] : dest[key]; 323 } 324 } 325 return dest; 326 }, 327 /** 328 * checks if an array contains an element 329 * @param {Array} arr array 330 * @param {String} str string to check for 331 */ 332 contains : function(arr, str) { 333 if (!arr || !str) { 334 throw Error(this.getMessage("ERR_MUST_BE_PROVIDED", null, "_Lang.contains", "arr {array}", "str {string}")); 335 } 336 return this.arrIndexOf(arr, str) != -1; 337 }, 338 arrToMap: function(arr, offset) { 339 var ret = new Array(arr.length); 340 var len = arr.length; 341 offset = (offset) ? offset : 0; 342 for (var cnt = 0; cnt < len; cnt++) { 343 ret[arr[cnt]] = cnt + offset; 344 } 345 return ret; 346 }, 347 objToArray: function(obj, offset, pack) { 348 if (!obj) { 349 return null; 350 } 351 //since offset is numeric we cannot use the shortcut due to 0 being false 352 //special condition array delivered no offset no pack 353 if (obj instanceof Array && !offset && !pack) return obj; 354 var finalOffset = ('undefined' != typeof offset || null != offset) ? offset : 0; 355 var finalPack = pack || []; 356 try { 357 return finalPack.concat(Array.prototype.slice.call(obj, finalOffset)); 358 } catch (e) { 359 //ie8 (again as only browser) delivers for css 3 selectors a non convertible object 360 //we have to do it the hard way 361 //ie8 seems generally a little bit strange in its behavior some 362 //objects break the function is everything methodology of javascript 363 //and do not implement apply call, or are pseudo arrays which cannot 364 //be sliced 365 for (var cnt = finalOffset; cnt < obj.length; cnt++) { 366 finalPack.push(obj[cnt]); 367 } 368 return finalPack; 369 } 370 }, 371 /** 372 * foreach implementation utilizing the 373 * ECMAScript wherever possible 374 * with added functionality 375 * 376 * @param arr the array to filter 377 * @param func the closure to apply the function to, with the syntax defined by the ecmascript functionality 378 * function (element<,key, array>) 379 * <p /> 380 * optional params 381 * <p /> 382 * <ul> 383 * <li>param startPos (optional) the starting position </li> 384 * <li>param scope (optional) the scope to apply the closure to </li> 385 * </ul> 386 */ 387 arrForEach: function(arr, func /*startPos, scope*/) { 388 if (!arr || !arr.length) return; 389 var startPos = Number(arguments[2]) || 0; 390 var thisObj = arguments[3]; 391 //check for an existing foreach mapping on array prototypes 392 //IE9 still does not pass array objects as result for dom ops 393 arr = this.objToArray(arr); 394 (startPos) ? arr.slice(startPos).forEach(func, thisObj) : arr.forEach(func, thisObj); 395 }, 396 /** 397 * foreach implementation utilizing the 398 * ECMAScript wherever possible 399 * with added functionality 400 * 401 * @param arr the array to filter 402 * @param func the closure to apply the function to, with the syntax defined by the ecmascript functionality 403 * function (element<,key, array>) 404 * <p /> 405 * additional params 406 * <ul> 407 * <li> startPos (optional) the starting position</li> 408 * <li> scope (optional) the scope to apply the closure to</li> 409 * </ul> 410 */ 411 arrFilter: function(arr, func /*startPos, scope*/) { 412 if (!arr || !arr.length) return []; 413 arr = this.objToArray(arr); 414 return ((startPos) ? arr.slice(startPos).filter(func, thisObj) : arr.filter(func, thisObj)); 415 }, 416 /** 417 * adds a EcmaScript optimized indexOf to our mix, 418 * checks for the presence of an indexOf functionality 419 * and applies it, otherwise uses a fallback to the hold 420 * loop method to determine the index 421 * 422 * @param arr the array 423 * @param element the index to search for 424 */ 425 arrIndexOf: function(arr, element /*fromIndex*/) { 426 if (!arr || !arr.length) return -1; 427 var pos = Number(arguments[2]) || 0; 428 arr = this.objToArray(arr); 429 return arr.indexOf(element, pos); 430 }, 431 /** 432 * helper to automatically apply a delivered arguments map or array 433 * to its destination which has a field "_"<key> and a full field 434 * 435 * @param dest the destination object 436 * @param args the arguments array or map 437 * @param argNames the argument names to be transferred 438 */ 439 applyArgs: function(dest, args, argNames) { 440 var UDEF = 'undefined'; 441 if (argNames) { 442 for (var cnt = 0; cnt < args.length; cnt++) { 443 //dest can be null or 0 hence no shortcut 444 if (UDEF != typeof dest["_" + argNames[cnt]]) { 445 dest["_" + argNames[cnt]] = args[cnt]; 446 } 447 if (UDEF != typeof dest[ argNames[cnt]]) { 448 dest[argNames[cnt]] = args[cnt]; 449 } 450 } 451 } else { 452 for (var key in args) { 453 if (UDEF != typeof dest["_" + key]) { 454 dest["_" + key] = args[key]; 455 } 456 if (UDEF != typeof dest[key]) { 457 dest[key] = args[key]; 458 } 459 } 460 } 461 }, 462 /** 463 * creates a standardized error message which can be reused by the system 464 * 465 * @param sourceClass the source class issuing the exception 466 * @param func the function issuing the exception 467 * @param error the error object itself (optional) 468 */ 469 createErrorMsg: function(sourceClass, func, error) { 470 var ret = []; 471 var keyValToStr = this.hitch(this, this.keyValToStr), 472 getMsg = this.hitch(this, this.getMessage), 473 pushRet = this.hitch(ret, ret.push); 474 pushRet(keyValToStr(getMsg("MSG_AFFECTED_CLASS"), sourceClass)); 475 pushRet(keyValToStr(getMsg("MSG_AFFECTED_METHOD"), func)); 476 /*we push the values into separate vars to improve the compression*/ 477 var errName = error.name; 478 var errMsg = error.message; 479 var errDesc = error.description; 480 var errNum = error.number; 481 var errLineNo = error.lineNumber; 482 if (error) { 483 var _UDEF = "undefined"; 484 pushRet(keyValToStr(getMsg("MSG_ERROR_NAME"), errName ? errName : _UDEF)); 485 pushRet(keyValToStr(getMsg("MSG_ERROR_MESSAGE"), errMsg ? errMsg : _UDEF)); 486 pushRet(keyValToStr(getMsg("MSG_ERROR_DESC"), errDesc ? errDesc : _UDEF)); 487 pushRet(keyValToStr(getMsg("MSG_ERROR_NO"), _UDEF != typeof errNum ? errNum : _UDEF)); 488 pushRet(keyValToStr(getMsg("MSG_ERROR_LINENO"), _UDEF != typeof errLineNo ? errLineNo : _UDEF)); 489 } 490 return ret.join(""); 491 }, 492 /** 493 * transforms a key value pair into a string 494 * @param key the key 495 * @param val the value 496 * @param delimiter the delimiter 497 */ 498 keyValToStr: function(key, val, delimiter) { 499 var ret = []; 500 pushRet = this.hitch(ret, ret.push); 501 pushRet(key); 502 pushRet(val); 503 delimiter = delimiter || "\n"; 504 pushRet(delimiter); 505 return ret.join(""); 506 }, 507 parseXML: function(txt) { 508 try { 509 var parser = new DOMParser(); 510 return parser.parseFromString(txt, "text/xml"); 511 } catch (e) { 512 //undefined internal parser error 513 return null; 514 } 515 }, 516 serializeXML: function(xmlNode, escape) { 517 if (!escape) { 518 if (xmlNode.data) return xmlNode.data; //CDATA block has raw data 519 if (xmlNode.textContent) return xmlNode.textContent; //textNode has textContent 520 } 521 return (new XMLSerializer()).serializeToString(xmlNode); 522 }, 523 serializeChilds: function(xmlNode) { 524 var buffer = []; 525 if (!xmlNode.childNodes) return ""; 526 for (var cnt = 0; cnt < xmlNode.childNodes.length; cnt++) { 527 buffer.push(this.serializeXML(xmlNode.childNodes[cnt])); 528 } 529 return buffer.join(""); 530 }, 531 isXMLParseError: function(xmlContent) { 532 //TODO determine the ie specific part here 533 //no xml content 534 if (xmlContent == null) return true; 535 var findParseError = function(node) { 536 if (!node || !node.childNodes) return false; 537 for (var cnt = 0; cnt < node.childNodes.length; cnt++) { 538 var childNode = node.childNodes[cnt]; 539 if (childNode.tagName && childNode.tagName == "parsererror") return true; 540 } 541 return false; 542 }; 543 return !xmlContent || 544 (this.exists(xmlContent, "parseError.errorCode") && xmlContent.parseError.errorCode != 0) || 545 findParseError(xmlContent); 546 }, 547 /** 548 * creates a neutral form data wrapper over an existing form Data element 549 * the wrapper delegates following methods, append 550 * and adds makeFinal as finalizing method which returns the final 551 * send representation of the element 552 * 553 * @param formData an array 554 */ 555 createFormDataDecorator: function(formData) { 556 //we simulate the dom level 2 form element here 557 var _newCls = null; 558 var bufInstance = null; 559 if (!this.FormDataDecoratorArray) { 560 this.FormDataDecoratorArray = function (theFormData) { 561 this._valBuf = theFormData; 562 this._idx = {}; 563 }; 564 _newCls = this.FormDataDecoratorArray; 565 _newCls.prototype.append = function(key, val) { 566 this._valBuf.push([encodeURIComponent(key), encodeURIComponent(val)].join("=")); 567 this._idx[key] = true; 568 }; 569 _newCls.prototype.hasKey = function(key) { 570 return !!this._idx[key]; 571 }; 572 _newCls.prototype.makeFinal = function() { 573 return this._valBuf.join("&"); 574 }; 575 } 576 if (!this.FormDataDecoratorString) { 577 this.FormDataDecoratorString = function (theFormData) { 578 this._preprocessedData = theFormData; 579 this._valBuf = []; 580 this._idx = {}; 581 }; 582 _newCls = this.FormDataDecoratorString; 583 _newCls.prototype.append = function(key, val) { 584 this._valBuf.push([encodeURIComponent(key), encodeURIComponent(val)].join("=")); 585 this._idx[key] = true; 586 }; 587 //for now we check only for keys which are added subsequently otherwise we do not perform any checks 588 _newCls.prototype.hasKey = function(key) { 589 return !!this._idx[key]; 590 }; 591 _newCls.prototype.makeFinal = function() { 592 if (this._preprocessedData != "") { 593 return this._preprocessedData + "&" + this._valBuf.join("&") 594 } else { 595 return this._valBuf.join("&"); 596 } 597 }; 598 } 599 if (!this.FormDataDecoratorOther) { 600 this.FormDataDecoratorOther = function (theFormData) { 601 this._valBuf = theFormData; 602 this._idx = {}; 603 }; 604 _newCls = this.FormDataDecoratorOther; 605 _newCls.prototype.append = function(key, val) { 606 this._valBuf.append(key, val); 607 this._idx[key] = true; 608 }; 609 _newCls.prototype.hasKey = function(key) { 610 return !!this._idx[key]; 611 }; 612 _newCls.prototype.makeFinal = function() { 613 return this._valBuf; 614 }; 615 } 616 if (formData instanceof Array) { 617 bufInstance = new this.FormDataDecoratorArray(formData); 618 } else if (this.isString(formData)) { 619 bufInstance = new this.FormDataDecoratorString(formData); 620 } else { 621 bufInstance = new this.FormDataDecoratorOther(formData); 622 } 623 return bufInstance; 624 }, 625 /** 626 * define a property mechanism which is browser neutral 627 * we cannot use the existing setter and getter mechanisms 628 * for now because old browsers do not support them 629 * in the long run we probably can switch over 630 * or make a code split between legacy and new 631 * 632 * TODO find a way to map this mechanism to the standard 633 * props mechanism modern browsers have 634 * 635 * @param obj 636 * @param name 637 * @param value 638 */ 639 attr: function(obj, name, value) { 640 var findAccessor = function(theObj, theName) { 641 return (theObj["_" + theName]) ? "_" + theName : ( (theObj[theName]) ? theName : null) 642 }; 643 var applyAttr = function(theObj, theName, value, isFunc) { 644 if (value) { 645 if (isFunc) { 646 theObj[theName](value); 647 } else { 648 theObj[theName] = value; 649 } 650 return null; 651 } 652 return (isFunc) ? theObj[theName]() : theObj[theName]; 653 }; 654 try { 655 var finalAttr = findAccessor(obj, name); 656 //simple attibute no setter and getter overrides 657 if (finalAttr) { 658 return applyAttr(obj, finalAttr, value); 659 } 660 //lets check for setter and getter overrides 661 var found = false; 662 var prefix = (value) ? "set" : "get"; 663 finalAttr = [prefix,name.substr(0, 1).toUpperCase(),name.substr(1)].join(""); 664 finalAttr = findAccessor(obj, finalAttr); 665 if (finalAttr) { 666 return applyAttr(obj, finalAttr, value, true); 667 } 668 throw Error("property " + name + " not found"); 669 } finally { 670 findAccessor = null; 671 applyAttr = null; 672 } 673 } 674 }); 675