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 theoretically we could save some code 20 by 21 defining the parent object as 22 var parent = new Object(); 23 parent.prototype = new myfaces._impl.core._Runtime(); 24 extendClass(function () { 25 }, parent , { 26 But for now we are not doing it the little bit of saved 27 space is not worth the loss of readability 28 */ 29 30 31 /** 32 * @memberOf myfaces._impl 33 * @namespace 34 * @name _util 35 */ 36 (myfaces._impl._util) ? myfaces._impl._util: {}; 37 38 39 40 /** 41 * @class 42 * @name _Lang 43 * @memberOf myfaces._impl._util 44 * @extends myfaces._impl.core._Runtime 45 * @description Object singleton for Language related methods, this object singleton 46 * decorates the namespace myfaces._impl.core._Runtime and adds a bunch of new methods to 47 * what _Runtime provided 48 * */ 49 var _Lang = myfaces._impl.core._Runtime.singletonDelegateObj("myfaces._impl._util._Lang", myfaces._impl.core._Runtime, 50 /** 51 * @lends myfaces._impl._util._Lang.prototype 52 */ 53 { 54 55 _processedExceptions: {}, 56 57 _installedLocale: null, 58 59 /** 60 * returns a given localized message upon a given key 61 * basic java log like templating functionality is included 62 * 63 * @param {String} key the key for the message 64 * @param {String} optional default message if none was found 65 * 66 * Additionally you can pass additional arguments, which are used 67 * in the same way java log templates use the params 68 * 69 * @param key 70 */ 71 getMessage: function(key, defaultMessage /*,vararg templateParams*/) { 72 if(!this._installedLocale) { 73 //we first try to install language and variant, if that one fails 74 //we try to install the language only, and if that one fails 75 //we install the base messages 76 this.initLocale(); 77 } 78 79 var msg = this._installedLocale[key] || defaultMessage || key + " - undefined message"; 80 for(var cnt = 2; cnt < arguments.length; cnt++) { 81 msg = msg.replace(new RegExp(["\\{",cnt-2,"\\}"].join(""),"g"),new String(arguments[cnt])); 82 } 83 return msg; 84 }, 85 86 /** 87 * (re)inits the currently installed 88 * messages so that after loading the main scripts 89 * a new locale can be installed optionally 90 * to our i18n subsystem 91 * 92 * @param newLocale locale override 93 */ 94 initLocale: function(newLocale) { 95 if(newLocale) { 96 this._installedLocale = new newLocale(); 97 return; 98 } 99 var language_Variant = this._callDelegate("getLanguage", this._callDelegate("getGlobalConfig","locale")); 100 var langStr = language_Variant ? language_Variant.language:""; 101 var variantStr = language_Variant ? [language_Variant.language,"_",language_Variant.variant||""].join(""):""; 102 103 var i18nRoot = myfaces._impl.i18n; 104 var i18nHolder = i18nRoot["Messages_"+variantStr] || 105 i18nRoot["Messages_"+langStr] || 106 i18nRoot.Messages; 107 108 this._installedLocale = new i18nHolder(); 109 }, 110 111 112 isExceptionProcessed: function(e) { 113 return !! this._processedExceptions[e.toString()]; 114 }, 115 116 setExceptionProcessed: function(e) { 117 this._processedExceptions[e.toString()] = true; 118 }, 119 120 clearExceptionProcessed: function() { 121 //ie again 122 for (var key in this._processedExceptions) { 123 this._processedExceptions[key] = null; 124 } 125 this._processedExceptions = {}; 126 }, 127 128 fetchNamespace : function(namespace) { 129 if (!namespace || !this.isString(namespace)) { 130 throw Error(this.getMessage("ERR_MUST_STRING",null,"_Lang.fetchNamespace","namespace")); 131 } 132 return this._callDelegate("fetchNamespace", namespace); 133 }, 134 135 reserveNamespace : function(namespace) { 136 if (!this.isString(namespace)) { 137 throw Error(this.getMessage("ERR_MUST_STRING",null,"_Lang.reserveNamespace", "namespace")); 138 } 139 return this._callDelegate("reserveNamespace", namespace); 140 }, 141 142 globalEval : function(code) { 143 if (!this.isString(code)) { 144 throw Error(this.getMessage("ERR_MUST_STRING",null,"_Lang.globalEval", "code")); 145 } 146 return this._callDelegate("globalEval", code); 147 }, 148 149 150 /** 151 * determines the correct event depending 152 * on the browsers state 153 * 154 * @param evt incoming event object (note not all browsers 155 * have this) 156 * 157 * @return an event object no matter what is incoming 158 */ 159 getEvent: function(evt) { 160 evt = (!evt) ? window.event || {} : evt; 161 return evt; 162 }, 163 164 /** 165 * cross port from the dojo lib 166 * browser save event resolution 167 * @param evt the event object 168 * (with a fallback for ie events if none is present) 169 */ 170 getEventTarget: function(evt) { 171 //ie6 and 7 fallback 172 evt = this.getEvent(evt); 173 /** 174 * evt source is defined in the jsf events 175 * seems like some component authors use our code 176 * so we add it here see also 177 * https://issues.apache.org/jira/browse/MYFACES-2458 178 * not entirely a bug but makes sense to add this 179 * behavior. I dont use it that way but nevertheless it 180 * does not break anything so why not 181 * */ 182 var t = evt.srcElement || evt.target || evt.source || null; 183 while ((t) && (t.nodeType != 1)) { 184 t = t.parentNode; 185 } 186 return t; 187 }, 188 189 /** 190 * consume event in a browser independend manner 191 * @param event the event which should not be propagated anymore 192 */ 193 consumeEvent: function(event) { 194 //w3c model vs ie model again 195 event = event || window.event; 196 (event.stopPropagation) ? event.stopPropagation() : event.cancelBubble = true; 197 }, 198 199 /** 200 * equalsIgnoreCase, case insensitive comparison of two strings 201 * 202 * @param source 203 * @param destination 204 */ 205 equalsIgnoreCase: function(source, destination) { 206 //either both are not set or null 207 if (!source && !destination) { 208 return true; 209 } 210 //source or dest is set while the other is not 211 if (!source || !destination) return false; 212 213 //in any other case we do a strong string comparison 214 return source.toLowerCase() === destination.toLowerCase(); 215 }, 216 217 /** 218 * escapes a strings special chars (crossported from dojo 1.3+) 219 * 220 * @param str the string 221 * 222 * @param except a set of exceptions 223 */ 224 escapeString: function(/*String*/str, /*String?*/except) { 225 // summary: 226 // Adds escape sequences for special characters in regular expressions 227 // except: 228 // a String with special characters to be left unescaped 229 230 return str.replace(/([\.$?*|:{}\(\)\[\]\\\/\+^])/g, function(ch) { 231 if (except && except.indexOf(ch) != -1) { 232 return ch; 233 } 234 return "\\" + ch; 235 }); // String 236 }, 237 238 /** 239 @see this._RT.extendClass 240 */ 241 /*extendClass : function(newClass, extendsClass, functionMap, inherited) { 242 return this._RT.extendClass(newClass, extendsClass, functionMap, inherited); 243 },*/ 244 245 //core namespacing and inheritance done, now to the language extensions 246 247 /** 248 * Save document.getElementById (this code was ported over from dojo) 249 * the idea is that either a string or domNode can be passed 250 * @param {Object} reference the reference which has to be byIded 251 */ 252 byId : function(/*object*/ reference) { 253 if (!reference) { 254 throw Error(this.getMessage("ERR_REF_OR_ID",null,"_Lang.byId","reference")); 255 } 256 return (this.isString(reference)) ? document.getElementById(reference) : reference; 257 }, 258 259 /** 260 * backported from dojo 261 * Converts an array-like object (i.e. arguments, DOMCollection) to an 262 array. Returns a new Array with the elements of obj. 263 * @param {Object} obj the object to "arrayify". We expect the object to have, at a 264 minimum, a length property which corresponds to integer-indexed 265 properties. 266 * @param {int} offset the location in obj to start iterating from. Defaults to 0. 267 Optional. 268 * @param {Array} packArr An array to pack with the properties of obj. If provided, 269 properties in obj are appended at the end of startWith and 270 startWith is the returned array. 271 */ 272 /*_toArray : function(obj, offset, packArr) { 273 // summary: 274 // Converts an array-like object (i.e. arguments, DOMCollection) to an 275 // array. Returns a new Array with the elements of obj. 276 // obj: 277 // the object to "arrayify". We expect the object to have, at a 278 // minimum, a length property which corresponds to integer-indexed 279 // properties. 280 // offset: 281 // the location in obj to start iterating from. Defaults to 0. 282 // Optional. 283 // startWith: 284 // An array to pack with the properties of obj. If provided, 285 // properties in obj are appended at the end of startWith and 286 // startWith is the returned array. 287 var arr = packArr || []; 288 //TODO add splicing here 289 290 for (var x = offset || 0; x < obj.length; x++) { 291 arr.push(obj[x]); 292 } 293 return arr; // Array 294 }, */ 295 296 /** 297 * Helper function to provide a trim with a given splitter regular expression 298 * @param {|String|} it the string to be trimmed 299 * @param {|RegExp|} splitter the splitter regular expressiion 300 * 301 * FIXME is this still used? 302 */ 303 trimStringInternal : function(it, splitter) { 304 return this.strToArray(it, splitter).join(splitter); 305 }, 306 307 /** 308 * String to array function performs a string to array transformation 309 * @param {String} it the string which has to be changed into an array 310 * @param {RegExp} splitter our splitter reglar expression 311 * @return an array of the splitted string 312 */ 313 strToArray : function(/*string*/ it, /*regexp*/ splitter) { 314 // summary: 315 // Return true if it is a String 316 317 if (!this.isString(it)) { 318 throw Error(this.getMessage("ERR_PARAM_STR",null, "myfaces._impl._util._Lang.strToArray", "it")); 319 } 320 if (!splitter) { 321 throw Error(this.getMessage("ERR_PARAM_STR_RE",null, "myfaces._impl._util._Lang.strToArray", "splitter")); 322 } 323 var retArr = it.split(splitter); 324 var len = retArr.length; 325 for (var cnt = 0; cnt < len; cnt++) { 326 retArr[cnt] = this.trim(retArr[cnt]); 327 } 328 return retArr; 329 }, 330 331 /** 332 * hyperfast trim 333 * http://blog.stevenlevithan.com/archives/faster-trim-javascript 334 * crossported from dojo 335 */ 336 trim : function(/*string*/ str) { 337 if (!this.isString(str)) { 338 throw Error(this.getMessage("ERR_PARAM_STR",null,"_Lang.trim", "str")); 339 } 340 str = str.replace(/^\s\s*/, ''); 341 var ws = /\s/; 342 var i = str.length; 343 while (ws.test(str.charAt(--i))); 344 return str.slice(0, i + 1); 345 }, 346 347 /** 348 * Backported from dojo 349 * a failsafe string determination method 350 * (since in javascript String != "" typeof alone fails!) 351 * @param it {|Object|} the object to be checked for being a string 352 * @return true in case of being a string false otherwise 353 */ 354 isString: function(/*anything*/ it) { 355 // summary: 356 // Return true if it is a String 357 return !!arguments.length && it != null && (typeof it == "string" || it instanceof String); // Boolean 358 }, 359 /** 360 * hitch backported from dojo 361 * hitch allows to assign a function to a dedicated scope 362 * this is helpful in situations when function reassignments 363 * can happen 364 * (notably happens often in lazy xhr code) 365 * 366 * @param {Function} scope of the function to be executed in 367 * @param {Function} method to be executed 368 * 369 * @return whatevery the executed method returns 370 */ 371 hitch : function(/*Object*/scope, /*Function|String*/method /*,...*/) { 372 // summary: 373 // Returns a function that will only ever execute in the a given scope. 374 // This allows for easy use of object member functions 375 // in callbacks and other places in which the "this" keyword may 376 // otherwise not reference the expected scope. 377 // Any number of default positional arguments may be passed as parameters 378 // beyond "method". 379 // Each of these values will be used to "placehold" (similar to curry) 380 // for the hitched function. 381 // scope: 382 // The scope to use when method executes. If method is a string, 383 // scope is also the object containing method. 384 // method: 385 // A function to be hitched to scope, or the name of the method in 386 // scope to be hitched. 387 // example: 388 // | myfaces._impl._util._Lang.hitch(foo, "bar")(); 389 // runs foo.bar() in the scope of foo 390 // example: 391 // | myfaces._impl._util._Lang.hitch(foo, myFunction); 392 // returns a function that runs myFunction in the scope of foo 393 if (arguments.length > 2) { 394 return this._hitchArgs._hitchArgs.apply(this._hitchArgs, arguments); // Function 395 } 396 if (!method) { 397 method = scope; 398 scope = null; 399 } 400 if (this.isString(method)) { 401 scope = scope || window || function() { 402 }; 403 /*since we do not have dojo global*/ 404 if (!scope[method]) { 405 throw(['myfaces._impl._util._Lang: scope["', method, '"] is null (scope="', scope, '")'].join('')); 406 } 407 return function() { 408 return scope[method].apply(scope, arguments || []); 409 }; // Function 410 } 411 return !scope ? method : function() { 412 return method.apply(scope, arguments || []); 413 }; // Function 414 } 415 , 416 417 _hitchArgs : function(scope, method /*,...*/) { 418 var pre = this.objToArray(arguments, 2); 419 var named = this.isString(method); 420 return function() { 421 // array-fy arguments 422 var args = this.objToArray(arguments); 423 // locate our method 424 var f = named ? (scope || this.global)[method] : method; 425 // invoke with collected args 426 return f && f.apply(scope || this, pre.concat(args)); // mixed 427 }; // Function 428 } 429 , 430 431 /** 432 * Helper function to merge two maps 433 * into one 434 * @param {|Object|} dest the destination map 435 * @param {|Object|} src the source map 436 * @param {|boolean|} overwrite if set to true the destination is overwritten if the keys exist in both maps 437 **/ 438 mixMaps : function(dest, src, overwrite, blockFilter) { 439 if (!dest || !src) { 440 throw Error(this.getMessage("ERR_PARAM_MIXMAPS",null,"_Lang.mixMaps")); 441 } 442 443 /** 444 * mixing code depending on the state of dest and the overwrite param 445 */ 446 var ret = {}; 447 var keyIdx = {}; 448 var key = null; 449 var _undef = "undefined"; 450 for (key in src) { 451 if(blockFilter && blockFilter[key]) { 452 continue; 453 } 454 /** 455 *we always overwrite dest with source 456 *unless overWrite is not set or source does not exist 457 *but also only if dest exists otherwise source still is taken 458 */ 459 if (!overwrite) { 460 /** 461 *we use exists instead of booleans because we cannot rely 462 *on all values being non boolean, we would need an elvis 463 *operator in javascript to shorten this :-( 464 */ 465 ret[key] = (_undef != typeof dest[key]) ? dest[key] : src[key]; 466 } else { 467 ret[key] = (_undef != typeof src[key]) ? src[key] : dest[key]; 468 } 469 keyIdx[key] = true; 470 } 471 for (key in dest) { 472 /*if result.key does not exist we push in dest.key*/ 473 ret[key] = (_undef != typeof ret[key]) ? ret[key] : dest[key]; 474 } 475 return ret; 476 } 477 , 478 479 /** 480 * checks if an array contains an element 481 * @param {Array} arr array 482 * @param {String} str string to check for 483 */ 484 contains : function(arr, str) { 485 if (!arr || !str) { 486 throw Error(this.getMessage("ERR_MUST_BE_PROVIDED",null,"_Lang.contains", "arr {array}", "str {string}")); 487 } 488 489 for (var cnt = 0; cnt < arr.length; cnt++) { 490 if (arr[cnt] == str) { 491 return true; 492 } 493 } 494 return false; 495 } 496 , 497 498 499 arrToMap: function(arr, offset) { 500 var ret = new Array(arr.length); 501 var len = arr.length; 502 offset = (offset) ? offset : 0; 503 504 for (var cnt = 0; cnt < len; cnt++) { 505 ret[arr[cnt]] = cnt + offset; 506 } 507 508 return ret; 509 }, 510 511 /** 512 * Concatenates an array to a string 513 * @param {Array} arr the array to be concatenated 514 * @param {String} delimiter the concatenation delimiter if none is set \n is used 515 * 516 * @return the concatenated array, one special behavior to enable j4fry compatibility has been added 517 * if no delimiter is used the [entryNumber]+entry is generated for a single entry 518 * TODO check if this is still needed it is somewhat outside of the scope of the function 519 * and functionality wise dirty 520 */ 521 arrToString : function(/*String or array*/ arr, /*string*/ delimiter) { 522 if (!arr) { 523 throw Error(this.getMessage("ERR_MUST_BE_PROVIDED1",null, "arr {array}")); 524 } 525 if (this.isString(arr)) { 526 return arr; 527 } 528 529 delimiter = delimiter || "\n"; 530 return arr.join(delimiter); 531 } 532 , 533 534 535 objToArray: function(obj, offset, pack) { 536 if (!obj) { 537 return null; 538 } 539 //since offset is numeric we cannot use the shortcut due to 0 being false 540 var finalOffset = ('undefined' != typeof offset || null != offset) ? offset : 0; 541 var finalPack = pack || []; 542 try { 543 return finalPack.concat(Array.prototype.slice.call(obj, finalOffset)); 544 } catch (e) { 545 //ie8 (again as only browser) delivers for css 3 selectors a non convertible object 546 //we have to do it the hard way 547 //ie8 seems generally a little bit strange in its behavior some 548 //objects break the function is everything methodology of javascript 549 //and do not implement apply call, or are pseudo arrays which cannot 550 //be sliced 551 for (var cnt = finalOffset; cnt < obj.length; cnt++) { 552 finalPack.push(obj[cnt]); 553 } 554 return finalPack; 555 } 556 557 } 558 , 559 560 /** 561 * foreach implementation utilizing the 562 * ECMAScript wherever possible 563 * with added functionality 564 * 565 * @param arr the array to filter 566 * @param func the closure to apply the function to, with the syntax defined by the ecmascript functionality 567 * function (element<,key, array>) 568 * @param startPos (optional) the starting position 569 * @param scope (optional) the scope to apply the closure to 570 */ 571 arrForEach: function(arr, func /*startPos, scope*/) { 572 if(!arr || !arr.length ) return; 573 try { 574 var startPos = Number(arguments[2]) || 0; 575 var thisObj = arguments[3]; 576 577 //check for an existing foreach mapping on array prototypes 578 //IE9 still does not pass array objects as result for dom ops 579 if (Array.prototype.forEach && arr.forEach) { 580 (startPos) ? arr.slice(startPos).forEach(func, thisObj) : arr.forEach(func, thisObj); 581 } else { 582 startPos = (startPos < 0) ? Math.ceil(startPos) : Math.floor(startPos); 583 if (typeof func != "function") { 584 throw new TypeError(); 585 } 586 for (var cnt = 0; cnt < arr.length; cnt++) { 587 if (thisObj) { 588 func.call(thisObj, arr[cnt], cnt, arr); 589 } else { 590 func(arr[cnt], cnt, arr); 591 } 592 } 593 } 594 } finally { 595 func = null; 596 } 597 } 598 , 599 600 601 /** 602 * foreach implementation utilizing the 603 * ECMAScript wherever possible 604 * with added functionality 605 * 606 * @param arr the array to filter 607 * @param func the closure to apply the function to, with the syntax defined by the ecmascript functionality 608 * function (element<,key, array>) 609 * @param startPos (optional) the starting position 610 * @param scope (optional) the scope to apply the closure to 611 * 612 */ 613 arrFilter: function(arr, func /*startPos, scope*/) { 614 if(!arr || !arr.length ) return []; 615 try { 616 var startPos = Number(arguments[2]) || 0; 617 var thisObj = arguments[3]; 618 619 //check for an existing foreach mapping on array prototypes 620 if (Array.prototype.filter) { 621 return ((startPos) ? arr.slice(startPos).filter(func, thisObj) : arr.filter(func, thisObj)); 622 } else { 623 if (typeof func != "function") { 624 throw new TypeError(); 625 } 626 var ret = []; 627 startPos = (startPos < 0) ? Math.ceil(startPos) : Math.floor(startPos); 628 629 for (var cnt = startPos; cnt < arr.length; cnt++) { 630 if (thisObj) { 631 var elem = arr[cnt]; 632 if (func.call(thisObj, elem, cnt, arr)) ret.push(elem); 633 } else { 634 var elem = arr[cnt]; 635 if (func(arr[cnt], cnt, arr)) ret.push(elem); 636 } 637 } 638 } 639 } finally { 640 func = null; 641 } 642 } 643 , 644 645 /** 646 * adds a EcmaScript optimized indexOf to our mix, 647 * checks for the presence of an indexOf functionality 648 * and applies it, otherwise uses a fallback to the hold 649 * loop method to determine the index 650 * 651 * @param arr the array 652 * @param element the index to search for 653 */ 654 arrIndexOf: function(arr, element /*fromIndex*/) { 655 if (!arr || !arr.length) return -1; 656 var pos = Number(arguments[2]) || 0; 657 658 if (Array.prototype.indexOf) { 659 return arr.indexOf(element, pos); 660 } 661 //var cnt = this._space; 662 var len = arr.length; 663 pos = (pos < 0) ? Math.ceil(pos) : Math.floor(pos); 664 665 //if negative then it is taken from as offset from the length of the array 666 if (pos < 0) { 667 pos += len; 668 } 669 while (pos < len && arr[pos] !== element) { 670 pos++; 671 } 672 return (pos < len) ? pos : -1; 673 } 674 , 675 676 677 /** 678 * helper to automatically apply a delivered arguments map or array 679 * to its destination which has a field "_"<key> and a full field 680 * 681 * @param dest the destination object 682 * @param args the arguments array or map 683 * @param argNames the argument names to be transferred 684 */ 685 applyArgs: function(dest, args, argNames) { 686 var _undef = 'undefined'; 687 if (argNames) { 688 for (var cnt = 0; cnt < args.length; cnt++) { 689 //dest can be null or 0 hence no shortcut 690 if (_undef != typeof dest["_" + argNames[cnt]]) { 691 dest["_" + argNames[cnt]] = args[cnt]; 692 } 693 if (_undef != typeof dest[ argNames[cnt]]) { 694 dest[argNames[cnt]] = args[cnt]; 695 } 696 } 697 } else { 698 for (var key in args) { 699 if (_undef != typeof dest["_" + key]) { 700 dest["_" + key] = args[key]; 701 } 702 if (_undef != typeof dest[key]) { 703 dest[key] = args[key]; 704 } 705 } 706 } 707 } 708 , 709 /** 710 * creates a standardized error message which can be reused by the system 711 * 712 * @param sourceClass the source class issuing the exception 713 * @param func the function issuing the exception 714 * @param error the error object itself (optional) 715 */ 716 createErrorMsg: function(sourceClass, func, error) { 717 var ret = []; 718 719 var keyValToStr = this.keyValToStr; 720 ret.push(keyValToStr(this.getMessage("MSG_AFFECTED_CLASS"), sourceClass)); 721 ret.push(keyValToStr(this.getMessage("MSG_AFFECTED_METHOD"), func)); 722 723 if (error) { 724 var _UDEF = "undefined"; 725 726 ret.push(keyValToStr(this.getMessage("MSG_ERROR_NAME"), error.name ? error.name : _UDEF)); 727 ret.push(keyValToStr(this.getMessage("MSG_ERROR_MESSAGE"), error.message ? error.message : _UDEF)); 728 ret.push(keyValToStr(this.getMessage("MSG_ERROR_DESC"), error.description ? error.description : _UDEF)); 729 ret.push(keyValToStr(this.getMessage("MSG_ERROR_NO"), _UDEF != typeof error.number ? error.number : _UDEF)); 730 ret.push(keyValToStr(this.getMessage("MSG_ERROR_LINENO"), _UDEF != typeof error.lineNumber ? error.lineNumber : _UDEF)); 731 } 732 return ret.join(""); 733 } 734 , 735 736 /** 737 * transforms a key value pair into a string 738 * @param key the key 739 * @param val the value 740 * @param delimiter the delimiter 741 */ 742 keyValToStr: function(key, val, delimiter) { 743 var ret = []; 744 ret.push(key); 745 ret.push(val); 746 if ('undefined' == typeof delimiter) { 747 delimiter = "\n"; 748 } 749 ret.push(delimiter); 750 return ret.join(""); 751 } 752 , 753 754 755 parseXML: function(txt) { 756 try { 757 var parser = null, xmlDoc = null; 758 if (window.DOMParser) { 759 parser = new DOMParser(); 760 xmlDoc = parser.parseFromString(txt, "text/xml"); 761 } 762 else // Internet Explorer 763 { 764 xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); 765 xmlDoc.async = "false"; 766 xmlDoc.loadXML(txt); 767 } 768 return xmlDoc; 769 } catch (e) { 770 //undefined internal parser error 771 return null; 772 } 773 } 774 , 775 776 serializeXML: function(xmlNode) { 777 if (xmlNode.xml) return xmlNode.xml; //IE 778 //rest of the world 779 return (new XMLSerializer()).serializeToString(xmlNode); 780 } 781 , 782 783 serializeChilds: function(xmlNode) { 784 var buffer = []; 785 if (!xmlNode.childNodes) return ""; 786 for (var cnt = 0; cnt < xmlNode.childNodes.length; cnt++) { 787 buffer.push(this.serializeXML(xmlNode.childNodes[cnt])); 788 } 789 return buffer.join(""); 790 } 791 , 792 isXMLParseError: function(xmlContent) { 793 794 //no xml content 795 if (xmlContent == null) return true; 796 797 var findParseError = function(node) { 798 if (!node || !node.childNodes) return false; 799 for (var cnt = 0; cnt < node.childNodes.length; cnt++) { 800 var childNode = node.childNodes[cnt]; 801 if (childNode.tagName && childNode.tagName == "parsererror") return true; 802 } 803 return false; 804 }; 805 return !xmlContent || 806 (this.exists(xmlContent, "parseError.errorCode") && xmlContent.parseError.errorCode != 0) || 807 findParseError(xmlContent); 808 809 810 } 811 , 812 /** 813 * creates a neutral form data wrapper over an existing form Data element 814 * the wrapper delegates following methods, append 815 * and adds makeFinal as finalizing method which returns the final 816 * send representation of the element 817 * 818 * @param formData an array 819 */ 820 createFormDataDecorator: function(formData) { 821 //we simulate the dom level 2 form element here 822 var _newCls = null; 823 var bufInstance = null; 824 825 if (!this.FormDataDecoratorArray) { 826 this.FormDataDecoratorArray = function (theFormData) { 827 this._valBuf = theFormData; 828 this._idx = {}; 829 }; 830 _newCls = this.FormDataDecoratorArray; 831 _newCls.prototype.append = function(key, val) { 832 this._valBuf.push([encodeURIComponent(key), encodeURIComponent(val)].join("=")); 833 this._idx[key] = true; 834 }; 835 _newCls.prototype.hasKey = function(key) { 836 return !!this._idx[key]; 837 }; 838 _newCls.prototype.makeFinal = function() { 839 return this._valBuf.join("&"); 840 }; 841 842 } 843 if (!this.FormDataDecoratorOther) { 844 this.FormDataDecoratorOther = function (theFormData) { 845 this._valBuf = theFormData; 846 this._idx = {}; 847 }; 848 _newCls = this.FormDataDecoratorOther; 849 _newCls.prototype.append = function(key, val) { 850 this._valBuf.append(key, val); 851 this._idx[key] = true; 852 }; 853 _newCls.prototype.hasKey = function(key) { 854 return !!this._idx[key]; 855 }; 856 _newCls.prototype.makeFinal = function() { 857 return this._valBuf; 858 }; 859 } 860 861 if (formData instanceof Array) { 862 bufInstance = new this.FormDataDecoratorArray(formData); 863 } else { 864 bufInstance = new this.FormDataDecoratorOther(formData); 865 } 866 867 return bufInstance; 868 } 869 }); 870