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