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