1 /*
  2   JUL - The JavaScript UI Language version 1.6.8
  3   Copyright (c) 2012 - 2020 The Zonebuilder <zone.builder@gmx.com>
  4   http://sourceforge.net/projects/jul-javascript/
  5   Licenses: GNU GPL2 or later; GNU LGPLv3 or later (http://sourceforge.net/p/jul-javascript/wiki/License/)
  6  */
  7 /**
  8   @fileOverview  JUL tools for working with a config tree of component configs<br>
  9   It can be also used as an instantiator (see {@link JUL.UI.Parser} method)
 10 */
 11 /* jshint browser: true, curly: true, eqeqeq: true, expr: true, funcscope: true, immed: true, latedef: true, 
 12   onevar: true, newcap: true, noarg: true, node: true, strict: true, trailing: true, undef: true, unused: vars, wsh: true */
 13 /* globals JUL */
 14 
 15 (function(global) {
 16 'use strict';
 17 // JUL & tools always remain global namespaces
 18 var jul = JUL;
 19 
 20 /* generated by JCS version 1.5.8 */
 21 
 22 /**
 23   JUL.UI namespace
 24   @namespace  JUL tools for working with a config tree of component configs<br />
 25   It can be also used as an instantiator (see {@link JUL.UI.Parser} method)
 26   @name  JUL.UI
 27 */
 28 jul.ns('JUL.UI');
 29 
 30 jul.apply(jul.get('JUL.UI'), /** @lends JUL.UI */ {
 31   /**
 32     The name of the binding property in the config tree
 33     @type  String
 34   */
 35   bindingProperty: 'cid',
 36   /**
 37     If enabled, boolean config props passed to JUL.UI.createDom() will be regarded as HTML5 boolean attributes
 38     @type  Boolean
 39   */
 40   booleanAttrs: false,
 41   /**
 42     The name of the children property in the config tree
 43     @type  String
 44   */
 45   childrenProperty: 'children',
 46   /**
 47     The name of the class property in the config tree
 48     @type  String
 49   */
 50   classProperty: 'xclass',
 51   /**
 52     The name of the CSS class property in the config tree
 53     @type  String
 54   */
 55   cssProperty: 'css',
 56   /**
 57     An optional custom function to instantiate a component
 58     @type  Function
 59   */
 60   customFactory: null,
 61   /**
 62     The name of the default class in the config tree
 63     @type  String
 64   */
 65   defaultClass: 'Object',
 66   /**
 67     The name of the inner HTML property in the config tree
 68     @type  String
 69   */
 70   htmlProperty: 'html',
 71   /**
 72     The name of the ID property in the config tree
 73     @type  String
 74   */
 75   idProperty: 'id',
 76   /**
 77     The name of the include property in the config tree
 78     @type  String
 79   */
 80   includeProperty: 'include',
 81   /**
 82     A property that may contain a per-config list of members to instantiate
 83     @type  String
 84   */
 85   instantiateProperty: 'instantiate',
 86   /**
 87     Optional mappings between the component class name and a list of members to instantiate
 88     @type  Object
 89   */
 90   membersMappings: {},
 91   /**
 92     An array of names of the other 'members' properties in the config tree
 93     @type  Array
 94   */
 95   membersProperties: [],
 96   /**
 97     The name of the parent property in the config tree. Used if topDown is true
 98     @type  String
 99   */
100   parentProperty: 'parent',
101   /**
102     The name pf the parser config property used as meta information for a branch of the config tree
103     @type  String
104   */
105   parserProperty: 'parserConfig',
106   /**
107     It allows JUL.UI.obj2str() to output the enclosed expression unquoted.
108     It also applies to JUL.UI.creatDom() when setting the element attributes.
109     When using create() method, it will resolve to an object using get() method of the instance.
110     @type  String
111   */
112   referencePrefix: '=ref:',
113   /**
114     The name of the tag property in the config tree
115     @type  String
116   */
117   tagProperty: 'tag',
118   textProperty: 'text',
119   /**
120     Set this to true to have a top-down instantiation instead of the default bottom-up one
121     @type  Boolean
122   */
123   topDown: false,
124   /**
125     Whether to use tag property in the config tree
126     @type  Boolean
127   */
128   useTags: false,
129   /**
130     Hash containing XML namespaces for several DOM languages
131     @type  Object
132   */
133   xmlNS: {
134     aml: 'http://www.amplesdk.com/ns/aml', aui: 'http://www.amplesdk.com/ns/aui', chart: 'http://www.amplesdk.com/ns/chart',
135      html: 'http://www.w3.org/1999/xhtml', svg: 'http://www.w3.org/2000/svg', xform: 'http://www.w3.org/2002/xforms',
136      xul: 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'
137   },
138   /**
139     Creates a new JUL.UI parser that inherits all JUL.UI members
140     @class  Used to build parser objects for config trees
141     @extends  JUL.UI
142     @param  {Object}  [oConfig]  Parser config object to override inherited members
143   */
144   Parser: function (oConfig) {
145     if (!(this instanceof JUL.UI.Parser) || this.hasOwnProperty('Parser')) {
146       return this && typeof this.Parser === 'function' && this.Parser.prototype instanceof JUL.UI.Parser ?
147         new this.Parser(oConfig) :
148         (this && this.ui && this.ui instanceof JUL.UI.Parser ?
149           new this.ui.Parser(oConfig) :  new JUL.UI.Parser(oConfig));
150     }
151     oConfig = oConfig || {};
152     if (oConfig.nsRoot) {
153       this._getJulInstance = JUL._getAutoInstance(oConfig.nsRoot);
154     }
155     JUL.apply(this, oConfig, false, 'nsRoot');
156     /* carry on the cyclic direct inheritance */
157     this.Parser = function(oConfig) {
158       var oReturn = JUL.UI.Parser.call(this, oConfig);
159       if (typeof oReturn === 'object') { return oReturn; }
160     };
161     this.Parser.prototype = this;
162   },
163   /**
164     Compacts a config tree converting suitable 'childrenProperty' items into 'membersProperties' properties
165     @param  {Object}  oData  Config tree
166     @param  {Boolean}  bAuto  Autodetect compactable items and add their names to the 'membersProperties' array of the parser
167     @param  {Number}  [_nLength]  For internal use
168     @returns  {Object}  Compacted config tree
169   */
170   compact: function (oData, bAuto, _nLength) {
171     if (!oData || typeof oData !== 'object') { return oData; }
172     if (JUL.typeOf(oData) === 'Array') {
173       var aResult = [];
174       for (var u = 0; u < oData.length; u++) { aResult.push(this.compact(oData[u], bAuto, _nLength)); }
175       return aResult;
176     }
177     oData = this.include(oData);
178     if (JUL.typeOf(oData[this.childrenProperty]) !== 'Array') { return oData; }
179     var aItems = [];
180     var oRepeat = {};
181     for (var i = 0; i < oData[this.childrenProperty].length; i++) {
182       var oChild = oData[this.childrenProperty][i];
183       var sClass = oChild[this.classProperty] || this.defaultClass;
184       var sName = this.useTags ? sClass + ':' + oChild[this.tagProperty] : sClass;
185       if (aItems.indexOf(sName) < 0) {
186         aItems.push(sName);
187       }
188       else {
189         oRepeat[sName] = true;
190       }
191     }
192     var oNew = {};
193     for (var sItem in oData) {
194       if (oData.hasOwnProperty(sItem) && sItem !== this.childrenProperty) {
195         oNew[sItem] = oData[sItem];
196       }
197     }
198     var oAdd = {};
199     if (typeof _nLength === 'undefined') { _nLength = (this.membersProperties || []).length; }
200     for (i = 0; i < oData[this.childrenProperty].length; i++) {
201       oChild = oData[this.childrenProperty][i];
202       sClass = oChild[this.classProperty] || this.defaultClass;
203       sName = this.useTags ? sClass + ':' + oChild[this.tagProperty] : sClass;
204       var sTag = this.useTags && sClass === this.defaultClass ? oChild[this.tagProperty] : sName;
205       var bCompact = typeof oChild[this.childrenProperty] === 'object' && oChild[this.childrenProperty].length;
206       var iPos = (this.membersProperties || []).indexOf(sTag);
207       if (bCompact && (iPos < 0 || iPos >= _nLength)) {
208         if (bAuto) {
209           for (sItem in oChild) {
210             if (oChild.hasOwnProperty(sItem) && sItem !== this.classProperty &&
211               sItem !== this.childrenProperty && !(this.useTags && sItem === this.tagProperty)) {
212               bCompact = false;
213               break;
214             }
215           }
216         }
217         else {
218           bCompact = false;
219         }
220       }
221       if (bCompact && !oRepeat[sName]) {
222         oAdd[sTag] = oChild[this.childrenProperty];
223       }
224       else {
225         oAdd[this.childrenProperty] = oAdd[this.childrenProperty] || [];
226         oAdd[this.childrenProperty].push(oChild);
227       }
228     }
229     for (sItem in oAdd) {
230       if (oAdd.hasOwnProperty(sItem)) {
231         oNew[sItem] = oAdd[sItem];
232         if (sItem !== this.childrenProperty && (this.membersProperties || []).indexOf(sItem) < 0) {
233           this.membersProperties = this.membersProperties || [];
234           this.membersProperties.push(sItem);
235         }
236         for (i = 0; i < oAdd[sItem].length; i++) {
237           oAdd[sItem][i] = this.compact(oAdd[sItem][i], bAuto, _nLength);
238         }
239       }
240     }
241     return oNew;
242   },
243   /**
244     Creates a tree of runtime objects specified by a config tree<br>
245     Object instances are created bottom-up by default with children instances put in the parent config, 
246     or top-down with parent instance put in the children configs
247     @param  {Object}  oTree  Config tree root or array of root configs
248     @param  {Object|Array}  [oBindings]  Config tree logic that will apply to the corresponding nodes
249     @param  {Object}  [oParent]  Optional parent object of the root instance when instantiating top-down
250     @param  {Boolean}  [bSparse]  This allows parsing a ‘sparse’ tree, i.e. a tree where some component nodes are not direct children of other components.
251     Every node which has a class or a tag set will be instantiated, regardless of its membership.
252     The ‘defaultClass’ property of the parser does not apply in this mode.
253     @returns  {Object}  Tree root instance or array of root instances
254   */
255   create: function (oTree, oBindings, oParent, bSparse) {
256       var sType = JUL.typeOf(oTree);
257       if (['Array', 'Object'].indexOf(sType) < 0) { return null; }
258       if (['Array', 'Object'].indexOf(JUL.typeOf(oBindings)) < 0) { oBindings = null; }
259       if (oBindings) {
260         var aBindings = [].concat(oBindings);
261         oBindings = {};
262         for (var m = 0; m < aBindings.length; m++) {
263           var oBinding = aBindings[m];
264           if (oBinding[this.includeProperty]) {
265             if (!oBindings[this.includeProperty]) { oBindings[this.includeProperty] = []; }
266             var aItems = [].concat(oBinding[this.includeProperty]);
267             for (var p = 0; p < aItems.length; p++) {
268               if (oBindings[this.includeProperty].indexOf(aItems[p]) < 0) { oBindings[this.includeProperty].push(aItems[p]); }
269             }
270           }
271           JUL.apply(oBindings, oBinding, false, this.includeProperty);
272         }
273         if (oBindings[this.includeProperty]) { oBindings = this.include(oBindings); }
274       }
275       if (sType === 'Array') {
276         return oTree.map(function(oItem) {
277           return this.create(oItem, oBindings, oParent);
278         }, this);
279       }
280       var oJul = JUL.getInstance(this);
281       var oRoot = {root: oTree};
282       var oRootParent = {root: oParent};
283       var aNodes = [new JUL.Ref({ref: oRoot, key: 'root', parent: oParent ? new JUL.Ref(oRootParent, 'root') : null})];
284       var aStack = aNodes;
285       while (aNodes.length) {
286         var aNextNodes = [];
287       for (var i = 0; i < aNodes.length; i++) {
288         var oCurrent = aNodes[i];
289         oCurrent.config = oCurrent.val();
290         sType = JUL.typeOf(oCurrent.config);
291         if (sType !== 'Object' && (!bSparse || sType !== 'Array')) { continue; }
292         if (sType === 'Array') {
293           var aCopy = [].concat(oCurrent.config);
294           for (var j = 0; j < aCopy.length; j++) {
295             if (['Array', 'Object'].indexOf(JUL.typeOf(aCopy[j])) > -1) {
296               aNextNodes.push(new JUL.Ref(aCopy, j));
297             }
298           }
299           oCurrent.val(aCopy);
300           oCurrent.sparse = true;
301           delete oCurrent.config;
302           continue;
303         }
304         if (oCurrent.config[this.parserProperty]) { continue; }
305         var bConfig = !bSparse || oCurrent.config[this.classProperty] || (this.useTags && oCurrent.config[this.tagProperty]);
306         delete oCurrent.config[this._instanceProperty];
307         var oNew = bConfig ? this.include(oCurrent.config) : JUL.apply({}, oCurrent.config);
308         if (bConfig) {
309           if (oNew[this.bindingProperty]) {
310             if (oBindings) {
311               var aCid = [].concat(oNew[this.bindingProperty]);
312               for (var k = 0; k < aCid.length; k++) {
313                 if (oBindings[aCid[k]]) { JUL.apply(oNew, oBindings[aCid[k]]); }
314               }
315             }
316             delete oNew[this.bindingProperty];
317           }
318           if (oNew[this.idProperty] && oBindings && oBindings[oNew[this.idProperty]]) {
319             JUL.apply(oNew, oBindings[oNew[this.idProperty]]);
320           }
321         }
322         oCurrent.val(oNew);
323         oCurrent.sparse = !bConfig;
324         var aInstantiate = bConfig ? this.getMembers(oNew) : [];
325         if (bConfig) { delete oNew[this.instantiateProperty]; }
326         for (var sItem in oNew) {
327           if (oNew.hasOwnProperty(sItem)) {
328             var bInstantiate = bConfig && aInstantiate.indexOf(sItem) > -1;
329             sType = JUL.typeOf(oNew[sItem]);
330             if (['Array', 'Object'].indexOf(sType) > -1 && (bInstantiate || bSparse)) {
331               var aMembers = [].concat(oNew[sItem]);
332               if (sType === 'Array' || (bInstantiate && this.topDown)) {
333                 for (var n = 0; n < aMembers.length; n++) {
334                     if (['Array', 'Object'].indexOf(JUL.typeOf(aMembers[n])) > -1) {
335                       aNextNodes.push(new JUL.Ref({ref: aMembers, key: n, parent: oCurrent}));
336                     }
337                 }
338               }
339               else {
340                   aNextNodes.push(new JUL.Ref({ref: oNew, key: sItem, parent: oCurrent}));
341               }
342               if (bInstantiate && this.topDown) {
343                 delete oNew[sItem];
344               }
345               else {
346                 if (sType === 'Array') { oNew[sItem] = aMembers; }
347               }
348             }
349           }
350           if (sType === 'String' && this.referencePrefix && !bInstantiate && sItem !== this.idProperty) {
351             var sPrefix = oNew[sItem].substr(0, this.referencePrefix.length + 1);
352             if (sPrefix === '\\' + this.referencePrefix) {
353               oNew[sItem] = oNew[sItem].substr(1);
354             }
355             else if (sPrefix.substr(0, this.referencePrefix.length) === this.referencePrefix && JUL.trim(oNew[sItem].substr(this.referencePrefix.length))) {
356               oNew[sItem] = oJul.get(JUL.trim(oNew[sItem].substr(this.referencePrefix.length)));
357             }
358           }
359         }
360       }
361       aStack = this.topDown ? aStack.concat(aNextNodes) : aNextNodes.concat(aStack);
362       aNodes = aNextNodes;
363     }
364     for (i = 0; i < aStack.length; i++) {
365       oCurrent = aStack[i];
366       if (!oCurrent.val()) { continue; }
367       if (oCurrent.val()[this.parserProperty]) {
368         var oBranchConfig = JUL.apply({}, oCurrent.val());
369         var oBranchParser = new this.Parser(oBranchConfig[this.parserProperty]);
370         delete oBranchConfig[this.parserProperty];
371         oCurrent.val(oBranchParser.create(oBranchConfig, oBindings, this.topDown && oCurrent.parent ? oCurrent.parent.val() : null));
372         continue;
373       }
374       if (!oCurrent.sparse) {
375         if (this.topDown) {
376           oCurrent.val()[this.parentProperty] = oCurrent.parent ? oCurrent.parent.val() : null;
377         }
378         oCurrent.val(this.createComponent(oCurrent.val()));
379       }
380       if (this._keepInstance && oCurrent.config) {
381         oCurrent.config[this._instanceProperty] = oCurrent.val();
382       }
383     }
384     return oRoot.root;
385   },
386   /**
387     Creates a single component given its config<br>
388     If the ID property is a dotted path, the component is published (made available) under that path
389     @param  {Object}  oConfig  Component config
390     @returns  {Object}  Component instance
391   */
392   createComponent: function (oConfig) {
393     if (!oConfig[this.classProperty]) { oConfig[this.classProperty] = this.defaultClass; }
394     var sNamespace = '';
395     if (oConfig[this.idProperty]) {
396       sNamespace = oConfig[this.idProperty].replace(/\\\./g, ':::::');
397       oConfig[this.idProperty] = oConfig[this.idProperty].replace(/\\\./g, '--').replace(/\./g, '-');
398       if (['window.', 'global.'].indexOf(sNamespace.substr(0, 7)) > -1) { oConfig[this.idProperty] = oConfig[this.idProperty].substr(7); }
399       if (sNamespace.substr(0, 1) === '.') { oConfig[this.idProperty] = oConfig[this.idProperty].substr(1); }
400     }
401     var oJul = JUL.getInstance(this);
402     var sClass = oConfig[this.classProperty];
403     if (!this.customFactory) { delete oConfig[this.classProperty]; }
404     var oNew = this.customFactory ? oJul.get(this.customFactory).call(this, oConfig) : this.factory(sClass, oConfig);
405     if (sNamespace.indexOf('.') > -1) {
406       return oJul.ns(sNamespace.replace(/:{5}/g, '\\.').replace(/-/g, '_'), oNew);
407     }
408     else {
409       return oNew;
410     }
411   },
412   /**
413     Custom factory for DOM languages<br>
414     To use it, set the 'customFactory' property of the parser to JUL.UI.createDom
415     @param  {Object}  oConfig  Config object
416     @param  {Object}  [oWidget]  Optional element instance. If present, the element will not be created, but it will be applied the passed config.
417     @returns  {Object}  Element instance
418   */
419   createDom: function (oConfig, oWidget) {
420     if (!oConfig) { return null; }
421     var oJul = JUL.getInstance(this);
422     var nNS = this.useTags ? -1 : oConfig[this.classProperty].indexOf(':');
423     var sNS = nNS > -1 ? oConfig[this.classProperty].substr(0, nNS) : (this.useTags ? oConfig[this.classProperty] : 'html');
424     var oDocument = this._domDocument ? oJul.get(this._domDocument) : global.document || (global.window ? global.window.document : null);
425     var bAmple = typeof global.ample === 'object';
426     if (bAmple) { oDocument = global.ample; }
427     if ((this.useTags ? oConfig[this.tagProperty] : oConfig[this.classProperty].substr(nNS + 1)) === '#text') {
428       if (oWidget) { oWidget.nodeValue = oConfig.value; }
429       else { oWidget = oDocument.createTextNode(oConfig.value); }
430       return oWidget;
431     }
432     oWidget = oWidget || (sNS === 'html' || typeof oDocument.createElementNS !== 'function' && this.xmlNS[sNS] ?
433       oDocument.createElement.apply(oDocument, [nNS > -1 ? oConfig[this.classProperty].substr(nNS + 1) : (this.useTags ? oConfig[this.tagProperty] : oConfig[this.classProperty])].concat(oConfig.is || [])) :
434       oDocument.createElementNS.apply(oDocument, [this.xmlNS[sNS] || null, nNS > -1 ? oConfig[this.classProperty] : sNS + ':' + (this.useTags ? oConfig[this.tagProperty] : oConfig[this.classProperty])].concat(oConfig.is || [] )));
435     if (!oWidget) { return null; }
436     if (oConfig.listeners && typeof oConfig.listeners === 'object') {
437       var oListeners = oConfig.listeners;
438       var oScope = oListeners.scope ? oJul.get(oListeners.scope) : null;
439       for (var sItem in oListeners) {
440         if (oListeners.hasOwnProperty(sItem) && sItem !== 'scope') {
441           var aAll = [].concat(oListeners[sItem]);
442           for (var j = 0; j < aAll.length; j++) {
443             var fListener = oJul.get(aAll[j]);
444             if (fListener) {
445               var oWhat = {
446                 fn: fListener,
447                 scope: oScope,
448                 useCapture: false
449               };
450               if (typeof fListener === 'object') {
451                 JUL.apply(oWhat, fListener);
452                 if (fListener.scope) { oWhat.scope = oJul.get(fListener.scope); }
453               }
454               if (bAmple || oWidget.addEventListener) { oWidget.addEventListener(sItem, oWhat.scope ? oJul.makeCaller(oWhat.scope, oWhat.fn, true) : oWhat.fn, oWhat.useCapture); }
455               else { oWidget.attachEvent('on' + sItem, oJul.makeCaller(oWhat.scope || oWidget, oWhat.fn, true)); }
456             }
457           }
458         }
459       }
460     }
461     var aInstantiate = this.getMembers(oConfig);
462     for (sItem in oConfig) {
463       if (oConfig.hasOwnProperty(sItem) && aInstantiate.indexOf(sItem) < 0 &&
464         ['listeners', this.cssProperty, 'style', this.textProperty, this.htmlProperty, this.tagProperty, this.classProperty, this.parentProperty].indexOf(sItem) < 0)
465       {
466         nNS = sItem.indexOf(':');
467         var sAttr = ['Array', 'Date', 'Function', 'Object', 'Null', 'RegExp'].indexOf(JUL.typeOf(oConfig[sItem])) > -1 ? this.obj2str(oConfig[sItem]) : oConfig[sItem];
468         if (this.booleanAttrs && typeof oConfig[sItem] === 'boolean') {
469           if (oConfig[sItem]) { sAttr = ''; }
470           else { continue; }
471         }
472         if (nNS > -1 && typeof oWidget.setAttributeNS === 'function' && this.xmlNS[sItem.substr(0, nNS)]) {
473           oWidget.setAttributeNS(this.xmlNS[sItem.substr(0, nNS)] || null, sItem, sAttr);
474         }
475         else {
476           oWidget.setAttribute(sItem, sAttr);
477         }
478       }
479     }
480     if (oConfig[this.cssProperty]) {
481       oWidget.setAttribute('class', oConfig[this.cssProperty]);
482     }
483     if (oConfig.style) {
484       if (bAmple) {
485         oWidget.setAttribute('style', oConfig.style);
486       }
487       else {
488         oWidget.style.cssText = oWidget.style.cssText +';' + oConfig.style;
489       }
490     }
491     if (oConfig[this.textProperty]) {
492       if (bAmple) { global.ample.query(oWidget).text(oConfig[this.textProperty]); }
493       else { oWidget['textContent' in oWidget ? 'textContent' : 'innerText'] = oConfig[this.textProperty]; }
494     }
495     if (oConfig[this.htmlProperty]) {
496       if (bAmple) {
497         global.ample.query(oWidget).html(oConfig[this.htmlProperty].substr(0, 1) === '<' && oConfig[this.htmlProperty].substr(-1) === '>' ?
498           oConfig[this.htmlProperty] : '<span>' + oConfig[this.htmlProperty] + '</span>');
499       }
500       else {
501         oWidget.innerHTML = oConfig[this.htmlProperty].replace(this._regExps.xss, '');
502       }
503     }
504     for (sItem in oConfig) {
505       if (oConfig.hasOwnProperty(sItem) && oConfig[sItem] && typeof oConfig[sItem] === 'object' &&
506         aInstantiate.indexOf(sItem) > -1) {
507         var aMembers = [].concat(oConfig[sItem]);
508         var oMembersWidget =  oWidget;
509         if (sItem !== this.childrenProperty) {
510           nNS = sItem.indexOf(':');
511           sNS = nNS > -1 ? sItem.substr(0, nNS) : (this.useTags ? this.defaultClass : 'html');
512           oMembersWidget = sNS === 'html' || typeof oDocument.createElementNS !== 'function' ?
513             oDocument.createElement.apply(oDocument, [nNS > -1 ? sItem.substr(nNS + 1) : sItem].concat(oConfig.is || [])) :
514             oDocument.createElementNS.apply(oDocument, [this.xmlNS[sNS] || null, nNS > -1 ? sItem : sNS + ':' + sItem].concat(oConfig.is || []));
515         }
516         for (var k = 0; k < aMembers.length; k++) {
517           oMembersWidget.appendChild(aMembers[k]);
518         }
519         if (sItem !== this.childrenProperty) {
520           oWidget.appendChild(oMembersWidget);
521         }
522       }
523     }
524     if (this.topDown && oConfig[this.parentProperty] &&
525       typeof oConfig[this.parentProperty] === 'object') {
526       oConfig[this.parentProperty].appendChild(oWidget);
527     }
528     return oWidget;
529   },
530   /**
531     Uses a variant of createElement(sClass, oProps, aChildren) like in React or Vue.js to create a V-DOM node.
532     createElement() must be present in the outer environment or the _domDocument parser property must be set 
533     t
534     to an object with the appropriate method.
535     @param  {Object}  oConfig  Config object
536     @returns  {Object}  V-DOM node
537   */
538   createVDom: function (oConfig) {
539     if (!oConfig) { return null; }
540     var oJul = JUL.getInstance(this);
541     var fCreate = this._createElement ? oJul.get(this._createElement) : null;
542     var bCreate = typeof fCreate === 'function';
543     var oDocument = this._domDocument ? oJul.get(this._domDocument) : null;
544     fCreate = fCreate || (oDocument ? oDocument.createElement : null);
545     if (!fCreate) { return null; }
546     oConfig = JUL.apply({}, oConfig);
547     var sTag = this.useTags ? oConfig[this.tagProperty] : oConfig[this.classProperty];
548     if (typeof sTag === 'string') {
549       if (sTag.split(':').pop() === '#text') {
550         return oConfig.value;
551       }
552       if (this.useTags && oConfig[this.classProperty] !== this.defaultClass) {
553         sTag = oConfig[this.classProperty] + ':' + sTag;
554       }
555     }
556     delete oConfig[this.classProperty];
557     delete oConfig[this.tagProperty];
558     var aChildren = [];
559     if (oConfig[this.textProperty]) {
560       aChildren.push(oConfig[this.textProperty]);
561     }
562     delete oConfig[this.textProperty];
563     if (oConfig[this.htmlProperty]) { 
564       if (typeof oConfig[this.htmlProperty] === 'string') {
565         try {
566         this._createDiv = this._createDiv || (global.document || (global.window ? global.window.document : null)).createElement('div');
567         this._createDiv.innerHTML = oConfig[this.htmlProperty].replace(this._regExps.xss, '');
568         oConfig[this.htmlProperty] = this.html2jul(this._createDiv, false, true)[this.childrenProperty] || [];
569         this._createDiv.innerHTML = '';
570         }
571         catch (e0) {
572           aChildren = [oConfig[this.htmlProperty]];
573         }
574       }
575       if (typeof oConfig[this.htmlProperty] === 'object') {
576         aChildren = [].concat(this.create(oConfig[this.htmlProperty]));
577       }
578     }
579     delete oConfig[this.htmlProperty];
580     if (oConfig[this.cssProperty]) {
581       if (typeof oConfig[this.cssProperty] === 'string') { oConfig.className = oConfig[this.cssProperty]; }
582       else { oConfig['class'] = oConfig[this.cssProperty]; }
583     }
584     delete oConfig[this.cssProperty];
585     var bObject = JUL.typeOf(oConfig[this.childrenProperty]) === 'Object';
586     aChildren = aChildren.concat(oConfig[this.childrenProperty] || []);
587     delete oConfig[this.childrenProperty];
588     for (var sItem in oConfig) {
589       if (oConfig.hasOwnProperty(sItem) && typeof oConfig[sItem] === 'string' &&
590         oConfig[sItem].substr(0, 2) === '{<' && oConfig[sItem].substr(-2) === '>}') {
591         oConfig[sItem] = this.xml2jul('<' + 'div>' + oConfig[sItem].slice(1, -1) + '<' + '/div>', false, true)[this.childrenProperty];
592         if (oConfig[sItem].length < 2) { oConfig[sItem] = oConfig[sItem][0]; }
593         oConfig[sItem] = this.create(oConfig[sItem]);
594       }
595     }
596     if (!aChildren.length) { aChildren = null; }
597     else if (aChildren.length < 2 && (bObject || typeof aChildren[0] === 'string')) { aChildren = aChildren[0]; }
598     return bCreate ? fCreate(sTag, oConfig, aChildren) : fCreate.call(oDocument, sTag, oConfig, aChildren);
599   },
600   /**
601     Expands a compacted config tree converting all 'membersProperties' properties into 'childrenProperty' items
602     @param  {Object}  oData  Compacted config tree
603     @returns  {Object}  Expanded config tree
604   */
605   expand: function (oData) {
606     if (!oData || typeof oData !== 'object') { return oData; }
607     if (JUL.typeOf(oData)  === 'Array') { return oData.map(this.expand, this); }
608     oData = this.include(oData);
609     var aChildren = [];
610     var aInstantiate = this.getMembers(oData);
611     delete oData[this.instantiateProperty];
612     for (var sItem in oData) {
613       if (oData.hasOwnProperty(sItem) && oData[sItem] && typeof oData[sItem] === 'object' &&
614         aInstantiate.indexOf(sItem) > -1) {
615         if (sItem === this.childrenProperty) {
616           aChildren = aChildren.concat(oData[sItem]);
617         }
618         else {
619           var oNew = {};
620           if (!this.useTags && sItem !== this.defaultClass) { oNew[this.classProperty] = sItem; }
621           if (this.useTags) {
622             var nNS = sItem.indexOf(':');
623             if (nNS > -1 && sItem.substr(0, nNS) !== this.defaultClass) { oNew[this.classProperty] = sItem.substr(0, nNS); }
624             oNew[this.tagProperty] = nNS > -1 ? sItem.substr(nNS + 1) : sItem;
625           }
626           oNew[this.childrenProperty] = [].concat(oData[sItem]);
627           delete oData[sItem];
628           aChildren.push(oNew);
629         }
630       }
631     }
632     if (aChildren.length) {
633       oData[this.childrenProperty] = aChildren;
634       for (var i = 0; i < aChildren.length; i++) {
635         aChildren[i] = this.expand(aChildren[i]);
636       }
637     }
638     return oData;
639   },
640   /**
641     Default factory method that creates an object of a certain class
642     @param  {String}  sClass  Dotted path of the class constructor i.e. class full name
643     @param  {Object}  oArgs  Constructor arguments as a config object (excluding class name)
644     @returns  {Object}  The new created object
645   */
646   factory: function (sClass, oArgs) {
647     var FNew = JUL.getInstance(this).get(sClass);
648     if (typeof FNew !== 'function') { return null; }
649     if (oArgs) {
650       return new (FNew)(oArgs);
651     }
652     else {
653       return new (FNew)();
654     }
655   },
656   /**
657     Gets a list of members to instantiate that may depend on the supplied config node.
658     It takes into account all members settings and mappings.
659     @param  {Object}  [oConfig]  Configuration object
660     @returns  {Array}  List of member names to instantiate (for the given config)
661   */
662   getMembers: function (oConfig) {
663     var aMembers = [].concat(this.childrenProperty, this.membersProperties || []);
664     if (!oConfig) { return aMembers; }
665     var aClass = [];
666     if (!this.useTags || (oConfig[this.classProperty] && oConfig[this.classProperty] !== this.defaultClass)) {
667       aClass.push(oConfig[this.classProperty] || this.defaultClass);
668     }
669     if (this.useTags) { aClass.push(oConfig[this.tagProperty]); }
670     var oMappings = this.membersMappings || {};
671     return aMembers.concat(oMappings[aClass.join(':')] || [], oConfig[this.instantiateProperty] || []);
672   },
673   /**
674     Converts a HTML string/DOM to a JUL.UI config tree or to JavaScript code, using the current document
675     @param  {Object}  oXml  DOM XML object or XML string to convert
676     @param  {Boolean}  [bReturnString]  Whether to serialize converted object to JavaScript code
677     @param  {Boolean}  [bTextNodes]  Whether to export text nodes as standalone JUL objects
678     @returns  {Object|Array}  JUL.UI config tree or its JavaScript code
679   */
680   html2jul: function (oXml, bReturnString, bTextNodes) {
681     this._divEncode = this._divEncode || (global.document || (global.window ? global.window.document : null)).createElement('div');
682     if (typeof oXml !== 'object') {
683       oXml = JUL.trim(oXml);
684       this._divEncode.innerHTML = oXml.replace(this._regExps.xss, '');
685       oXml = [].slice.call(this._divEncode.childNodes || []);
686     }
687     oXml = [].concat(oXml);
688     var aJul = [];
689     for (var i = 0; i < oXml.length; i++) {
690       if (oXml[i].nodeType === 1 || (bTextNodes && oXml[i].nodeType === 3)) {
691         aJul.push(this.xml2jul(oXml[i], false, bTextNodes, true));
692       }
693     }
694     this._divEncode.innerHTML = '';
695     if (!aJul.length) { return this.xml2jul(null, bReturnString); }
696     if (aJul.length < 2) { aJul = aJul[0]; }
697     return bReturnString ? this.obj2str(aJul) : aJul;
698   },
699   /**
700     Does explicit object inheritance based on a predefined object property
701     @param  {Object}  oData  Config object containing an include property i.e. a dotted path to another object
702     @param  {Function}  [fMerger]  Callback function to do a custom object merging. it has two parameters:<ul>
703     <li>oSource - the current object to be affected</li>
704     <li>oAdd - the object to be merged with oSource</li>
705     </ul>If not present, the merging is done using JUL.apply()
706     @returns  {Object}  Object with recursively applied inherited properties where not already present
707   */
708   include: function (oData, fMerger) {
709     var oNew = {};
710     if (!oData[this.includeProperty]) {
711       return JUL.apply(oNew, oData);
712     }
713     var bMerger = fMerger && typeof fMerger === 'function';
714     if (!bMerger && this._includeMerger) {
715       fMerger = this._includeMerger;
716       bMerger = typeof fMerger === 'function';
717     }
718     var oJul = JUL.getInstance(this);
719     var aIncludes = [].concat(oData[this.includeProperty]);
720     for (var i = 0; i < aIncludes.length; i++) {
721       var oInclude = oJul.get(aIncludes[i]);
722       if (oInclude) {
723         if (bMerger) {  fMerger.call(this, oNew, this.include(oInclude, fMerger)); }
724         else { JUL.apply(oNew, this.include(oInclude)); }
725       }
726     }
727     var aCid = oNew[this.bindingProperty] ? [].concat(oNew[this.bindingProperty]) : [];
728     if (oData[this.bindingProperty] && aCid.indexOf(oData[this.bindingProperty]) < 0) {
729       aCid.push(oData[this.bindingProperty]);
730     }
731     if (bMerger) { fMerger.call(this, oNew, oData); }
732     else { JUL.apply(oNew, oData); }
733     if (aCid.length) { oNew[this.bindingProperty] = aCid; }
734     delete oNew[this.includeProperty];
735     return oNew;
736   },
737   /**
738     Recursively converts a config object to JavaScript or to JSON
739     @param  {Object}  oData  Config object
740     @param  {Boolean}  [bQuote]  Set it to true for getting valid JSON instead of JavaScript
741     @param  {Function}  [fDecorator]  Postprocessing the code fragment corresponding to each inner member, up to the next delimiter. 
742     Only available for the JavaScript generated code. Parameters:<ul>
743     <li><b>sContent</b>: the code fragment</li>
744     <li><b>sPath</b>: the dotted path to the current member relative to the root object</li>
745     <li><b>sIndent</b>: the current indentation in the generated code</li>
746     </ul>The call is made in the context of the root object. You should return the unchanged fragment when not processing.
747     @returns  {String}  JavaScript code or JSON string
748   */
749   obj2str: function (oData, bQuote, fDecorator) {
750     if (typeof this._useJsonize === 'undefined') {
751       var fEmpty = function() {};
752       this._useJsonize = JSON.stringify({o: fEmpty}, JUL.makeCaller(JUL.UI, '_jsonReplacer')).indexOf('function') < 0;
753     }
754     var sData = this._useJsonize ? JSON.stringify(this._jsonize(oData)) : JSON.stringify(oData, JUL.getInstance(this).makeCaller(this, '_jsonReplacer'));
755     if (!sData) { return ''; }
756     var ca = '#';
757     var c = '';
758     var sIndent = '';
759     var sResult = '';
760     var sContent = '';
761     var bString = false;
762     var aStack = [];
763     var aPath = [];
764     var nStart = 0;
765     var nLn = 0;
766     var nLastComma = 0;
767     for (var i = 0; i < sData.length; i++) {
768       c = sData.substr(i, 1);
769       if (c === '"' && ca !== '\\') {
770         bString = !bString;
771         if (bString) { nStart = i; }
772       }
773       if (bString) {
774         if (bQuote) { sResult = sResult + c; }
775         ca = c;
776         continue;
777       }
778       if (!bQuote && ('{[,]}').indexOf(c) > -1 && (']}').indexOf(ca) < 0) {
779         sResult = sResult + (fDecorator ? fDecorator.call(oData, sContent,
780           aPath.map(function(sVal) { return sVal.toString().replace(/\./g, '\\.'); }).join('.').replace(/(\[|\])/g, '\\$1'), sIndent) : sContent);
781         sContent = '';
782       }
783       if (c === '"' && !bQuote) {
784         var sItem = JSON.parse(sData.substr(nStart, i - nStart + 1));
785         if (sData.substr(i + 1, 1) === ':') {
786           if (fDecorator) { aPath[aPath.length - 1] = sItem; }
787           sContent = sContent + (!this._regExps.keyword.test(sItem) && (this._regExps.variable.test(sItem) || this._regExps.uint.test(sItem)) ?
788             sItem : (this._useDoubleQuotes ? sData.substr(nStart, i - nStart + 1) :
789             "'" + sData.substr(nStart + 1, i - nStart - 1).replace(/\\"/g, '"').replace(/'/g, "\\'") + "'"));
790         }
791         else {
792           var bPrefix = false;
793           if (this._usePrefixes) {
794             if (sItem.substr(0, this._jsonPrefixes.func.length) === this._jsonPrefixes.func &&
795               JUL.trim(sItem.substr(this._jsonPrefixes.func.length))) {
796               bPrefix = true;
797               sItem = sItem.substr(this._jsonPrefixes.func.length).replace(/^\s+/, '');
798             }
799             else if (sItem.substr(0, this._jsonPrefixes.newop.length) === this._jsonPrefixes.newop &&
800               JUL.trim(sItem.substr(this._jsonPrefixes.newop.length))) {
801               bPrefix = true;
802               sItem = sItem.substr(this._jsonPrefixes.newop.length).replace(/^\s+/, '');
803             }
804           }
805           if (bPrefix || (!this._usePrefixes && (this._regExps.functionStart.test(sItem) || this._regExps.newStart.test(sItem)))) {
806             var oBegin = sItem.match(/\n(\s|\t)+\}$/);
807             if (oBegin) {
808               oBegin = oBegin[0].substr(0, oBegin[0].length - 1);
809               sItem = sItem.replace(new RegExp(oBegin, 'g'), '\n');
810             }
811             sItem = sItem.replace(/\t/g, this._tabString)
812               .replace(/\n\r?/g, this._newlineString + sIndent);
813             sContent = sContent + sItem;
814             ca = '~';
815             continue;
816           }
817           else {
818             bPrefix = false;
819             if (this._usePrefixes &&
820               sItem.substr(0, this._jsonPrefixes.regex.length) === this._jsonPrefixes.regex &&
821                 JUL.trim(sItem.substr(this._jsonPrefixes.regex.length))) {
822               bPrefix = true;
823               sItem = sItem.substr(this._jsonPrefixes.regex.length).replace(/^\s+/, '');
824             }
825             if (this._usePrefixes) {
826               for (var sWhat in this._jsonPrefixes) {
827                 if (this._jsonPrefixes.hasOwnProperty(sWhat) &&
828                   sItem.substr(0, this._jsonPrefixes[sWhat].length + 1) === '\\' + this._jsonPrefixes[sWhat]) {
829                   sItem = sItem.substr(1);
830                 }
831               }
832             }
833             if (this.referencePrefix && sItem.substr(0, this.referencePrefix.length) === this.referencePrefix &&
834               JUL.trim(sItem.substr(this.referencePrefix.length))) {
835               bPrefix = true;
836               sItem = sItem.substr(this.referencePrefix.length).replace(/^\s+/, '');
837             }
838             if (this.referencePrefix && sItem.substr(0, this.referencePrefix.length + 1) === '\\' + this.referencePrefix) {
839               sItem = sItem.substr(1);
840             }
841             sContent = sContent + (bPrefix || (!this._usePrefixes && this._regExps.regexp.test(sItem)) ?
842               sItem : (this._useDoubleQuotes ? sData.substr(nStart, i - nStart + 1) :
843               "'" + sData.substr(nStart + 1, i - nStart - 1).replace(/\\"/g, '"').replace(/'/g, "\\'") + "'"));
844           }
845         }
846       }
847       else if (c === '{' || c === '[') {
848         if (!bQuote && fDecorator) { aPath.push(c === '[' ? 0 : ''); }
849         aStack.push(c);
850         if ((aStack.length === 1 && sData.substr(i + 1, 1) !== '}' && sData.substr(i + 1, 1) !== ']') ||
851           (c === '{' && ca === ':' && sData.substr(i + 1, 1) !== '}') ||
852           (c === '[' && (sData.substr(i + 1, 1) === '{' || sData.substr(i + 1, 1) === '['))) {
853           sIndent = sIndent + this._tabString;
854           sResult = sResult + c + this._newlineString + sIndent;
855           nLn = sResult.length;
856         }
857         else {
858           sResult = sResult + c;
859         }
860       }
861       else if (c === ',') {
862         if (this._phraseLength && nLastComma && sResult.length > nLn + this._phraseLength) {
863           sResult = sResult.slice(0, nLastComma) + this._newlineString + sIndent + sResult.substr(nLastComma);
864           nLn = nLastComma + this._newlineString.length + sIndent.length;
865         }
866         nLastComma = sResult.length + 1;
867         if (!bQuote && fDecorator && aStack.length && aStack[aStack.length - 1] === '[') { aPath[aPath.length - 1]++; }
868         if (aStack.length === 1 || ca === '~' || ((ca === '}' || ca === ']') &&
869           sData.substr(i - 2, 1) !== '{' && sData.substr(i - 2, 1) !== '[')) {
870           sResult = sResult + c + this._newlineString + sIndent;
871           nLn = sResult.length;
872           nLastComma = 0;
873         }
874         else if(!bQuote) {
875           sResult = sResult + c + this._spaceString;
876         }
877         else {
878           sResult = sResult + c;
879         }
880       }
881       else if (c === '}' || c === ']') {
882         if (this._phraseLength &&  nLastComma && sResult.length > nLn + this._phraseLength) {
883           sResult = sResult.slice(0, nLastComma) + this._newlineString + sIndent + sResult.substr(nLastComma);
884           nLn = nLastComma + this._newlineString.length + sIndent.length;
885         }
886         nLastComma = 0;
887         if (!bQuote && fDecorator) { aPath.pop(); }
888         aStack.pop();
889         if ((!aStack.length && ca !== '{' && ca !== '[') ||
890           (c === '}' && aStack.length && aStack[aStack.length - 1] === '{' && ca !== '{') ||
891           (c === ']' && (ca === '}'  || ca === ']'))) {
892           sIndent = sIndent.substr(this._tabString.length);
893           sResult = sResult + this._newlineString + sIndent + c;
894           nLn = sResult.length - 1;
895         }
896         else {
897           sResult = sResult + c;
898         }
899       }
900       else {
901         if (bQuote) { sResult = sResult + c; }
902         else { sContent = sContent + (c === ':' ? c + this._spaceString : c); }
903       }
904       ca = c;
905     }
906     if (!bQuote && sContent) {
907       sResult = sResult + (fDecorator ? fDecorator.call(oData, sContent, '', sIndent) : sContent);
908     }
909     return sResult;
910   },
911   /**
912     Converts a XML string/DOM to a JUL.UI config tree or to JavaScript code
913     @param  {Object}  oXml  DOM XML object or XML string to convert
914     @param  {Boolean}  [bReturnString]  Whether to serialize converted object to JavaScript code
915     @param  {Boolean}  [bTextNodes]  Whether to export text nodes as standalone JUL objects
916     @param  {Boolean}  [bLowerTags]  Force tag names to be converted to lowercase
917     @returns  {Object}  JUL.UI config tree or its JavaScript code
918   */
919   xml2jul: function (oXml, bReturnString, bTextNodes, bLowerTags) {
920     if (typeof oXml !== 'object') {
921       try {
922       var sXml = oXml.replace(/&(lt|gt|amp);/g, ';;;;~;$1;').replace(/&(#?\w+;)/g, ';;;;0;$1').replace(/[<>&]/g, function(sMatch) {
923         switch (sMatch) {
924         case '<': return ';;;;1;';
925         case '>': return ';;;;2;';
926         case '&': return ';;;;3;';
927         }
928         return sMatch;
929       }).replace(/;{4}0;/g, '&');
930       this._divEncode = this._divEncode || (global.document || (global.window ? global.window.document : null)).createElement('div');
931       this._divEncode.innerHTML = sXml.replace(this._regExps.xss, '');
932       oXml = (this._divEncode.textContent || this._divEncode.innerText || '').replace(/;{4}\d;/g, function(sMatch) {
933         switch (parseInt(sMatch.substr(4, 1))) {
934         case 1: return '<';
935         case 2: return '>';
936         case 3: return '&';
937         }
938         return sMatch;
939       }).replace(/;{4}~;/g, '&');
940       this._divEncode.innerHTML = '';
941       }
942       catch (e0) {}
943       oXml = this._createXml(oXml);
944     }
945     if (oXml.error) {
946         return bReturnString ? this.obj2str(oXml) : oXml;
947     }
948     if (oXml.parseError && oXml.parseError.errorCode !== 0) {
949       return bReturnString ? this.obj2str({error: oXml.parseError.reason}) : {error: oXml.parseError.reason};
950     }
951     var oData = {};
952     var _t = function(s) {
953       return bLowerTags ? s.toLowerCase() : s;
954     };
955     var dom2jul = function(oData, oNode, bNoTag) {
956       if (!oNode) { return; }
957       if (oNode.nodeName === 'parsererror') {
958         oData.error = oNode.textContent;
959         return;
960       }
961       var nNS = oNode.nodeName.indexOf(':');
962       if (!bNoTag && nNS > -1) {
963         if (this.defaultClass !== _t(oNode.nodeName).substr(0, nNS)) {
964           oData[this.classProperty] = _t(oNode.nodeName).substr(0, nNS);
965         }
966         oData[this.tagProperty] = _t(oNode.nodeName).substr(nNS + 1);
967       }
968       else {
969         if (!bNoTag || this.defaultClass !== _t(oNode.nodeName)) {
970           oData[bNoTag ? this.classProperty : this.tagProperty] = _t(oNode.nodeName);
971         }
972       }
973       if (oNode.nodeType === 3) {
974         oData.value = oNode.nodeValue;
975         return;
976       }
977       for (var i = 0; i < oNode.attributes.length; i++) {
978         var oAttribute = oNode.attributes[i];
979         if (oAttribute.name.substr(0, 5) !== 'xmlns') { 
980           oData[oAttribute.name === 'class' ? this.cssProperty : oAttribute.name] =
981             this._regExps.number.test(oAttribute.value) || this._regExps.special.test(oAttribute.value) ?
982             JSON.parse(oAttribute.value) : oAttribute.value;
983         }
984       }
985       if (!bTextNodes && oNode.childNodes.length && oNode.firstChild.nodeType === 3 &&
986         JUL.trim(oNode.firstChild.nodeValue)) {
987         oData[this.htmlProperty] = oNode.firstChild.nodeValue.replace(/[<>&]/g, function(sMatch) {
988           switch (sMatch) {
989           case '<': return '<';
990           case '>': return '>';
991           case '&': return '&';
992           }
993           return sMatch;
994         });
995       }
996       var aNames = [];
997       var oRepeat = {};
998       for (i = 0; i < oNode.childNodes.length; i++) {
999         var oChild = oNode.childNodes[i];
1000         if (oChild.nodeType === 1) {
1001           if (aNames.indexOf(oChild.nodeName) > -1) {
1002             oRepeat[oChild.nodeName] = true;
1003           }
1004           else {
1005             aNames.push(oChild.nodeName);
1006           }
1007         }
1008       }
1009       for (i = 0; i < oNode.childNodes.length; i++) {
1010         oChild = oNode.childNodes[i];
1011         if (oChild.nodeType === 1 || (bTextNodes && oChild.nodeType === 3)) {
1012           nNS = oChild.nodeName.indexOf(':');
1013           var sTag = !bNoTag && nNS > -1 && this.defaultClass === _t(oChild.nodeName).substr(0, nNS) ?
1014             _t(oChild.nodeName).substr(nNS + 1) : _t(oChild.nodeName);
1015           if (oChild.nodeType === 1 && !oRepeat[oChild.nodeName] &&
1016             [].concat(this.childrenProperty, this.membersProperties || []).indexOf(sTag) > -1) {
1017             var aMembers = [];
1018             oData[sTag] = aMembers;
1019             for (var k = 0; k < oChild.childNodes.length; k++) {
1020               var oGrandChild = oChild.childNodes[k];
1021               if (oGrandChild.nodeType === 1 || (bTextNodes && oGrandChild.nodeType === 3)) {
1022                 aMembers.push({});
1023                 dom2jul.call(this, aMembers[aMembers.length - 1], oGrandChild, bNoTag);
1024               }
1025             }
1026           }
1027           else {
1028             aMembers = oData[this.childrenProperty] || [];
1029             oData[this.childrenProperty] = aMembers;
1030             aMembers.push({});
1031             dom2jul.call(this, aMembers[aMembers.length - 1], oChild, bNoTag);
1032           }
1033         }
1034       }
1035     };
1036     if (oXml.documentElement) { oXml = oXml.documentElement; }
1037     dom2jul.call(this, oData, oXml, !this.useTags);
1038     return bReturnString ? this.obj2str(oData) : oData;
1039   },
1040   /**
1041     Reference to a createElement() function used in Virtual DOM
1042     @type  Function
1043     @private
1044   */
1045   _createElement: null,
1046   /**
1047     May be set to a custom document object to use createDom() for various DOM implementations
1048     @type  Object
1049     @private
1050   */
1051   _domDocument: null,
1052   /**
1053     It can be set to a user defined DOM Parser instance
1054     @type  Object
1055     @private
1056   */
1057   _domParser: null,
1058   /**
1059     May be set to a 'include' merging function used globally by the parser.
1060     See JUL.UI.include() parameters.
1061     @type  Function
1062     @private
1063   */
1064   _includeMerger: null,
1065   /**
1066     Used for debugging purposes
1067     @type  String
1068     @private
1069   */
1070   _instanceProperty: '_instance',
1071   /**
1072     Prefixes used when serializing certain objects into JSON.
1073     @see JUL.UI._usePrefixes property
1074     @type  Object
1075     @private
1076   */
1077   _jsonPrefixes: {
1078     func: '=func:', regex: '=regex:', newop: '=newop:'
1079   },
1080   /**
1081     Used for debugging purposes
1082     @type  Boolean
1083     @private
1084   */
1085   _keepInstance: false,
1086   /**
1087     New line delimiter for serialized strings
1088     @type  String
1089     @private
1090   */
1091   _newlineString: '\n',
1092   /**
1093     Desired line length of an Array/Object member list in the generated code
1094     @type  Number
1095     @private
1096   */
1097   _phraseLength: 120,
1098   /**
1099     Several RegExp patterns used across JUL.UI
1100     @type  Object
1101     @private
1102   */
1103   _regExps: {
1104     variable: /^[a-z$_][\w$]*$/i, number: /^[\-+]?[0-9]*\.?[0-9]+([eE][\-+]?[0-9]+)?$/, uint: /^(\d|[1-9]\d+)$/,
1105      functionStart: /^function\s*\(/, newStart: /^new\s+[A-Z$_][\w$]*\s*\(/,
1106      isoDateStart: /^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d/, regexp: /^\/(\s|\S)+\/[gim]{0,3}$/,
1107      special: /^(true|false|null)$/, autoUseStrict: /(\{)\r?\n?"use strict";\r?\n?/, xss: /<script(\s|\S)+?\/script\s*>/g,
1108      keyword: /^(break|case|catch|continue|debugger|default|delete|do|else||finally|for|function|if|in|instanceof|new|return|switch|this|throw|try|typeof|var|void|while|with|class|enum|export|extends|import|super|implements|interface|let|package|private|protected|public|static|yield)$/
1109   },
1110   /**
1111     Space delimiter for serialized strings
1112     @type  String
1113     @private
1114   */
1115   _spaceString: ' ',
1116   /**
1117     Tab delimiter for serialized strings
1118     @type  String
1119     @private
1120   */
1121   _tabString: '\t',
1122   /**
1123     Set to true to generate double quoted JavaScript strings
1124     @type  Boolean
1125     @private
1126   */
1127   _useDoubleQuotes: false,
1128   /**
1129     If true, the objects/functions will serialize into JSON using type dependent prefixes.
1130     @see JUL.UI._jsonPrefixes hash. This doesn't affect the generated JavaScript.
1131     @type  Boolean
1132     @private
1133   */
1134   _usePrefixes: false,
1135   /**
1136     Utility wrapper of browser's XML parser
1137     @private
1138   */
1139   _createXml: function (sXml) {
1140     var DomParser = this._domParser || global.DOMParser;
1141     if (typeof DomParser === 'function') {
1142       this._xmlParser = this._xmlParser || new DomParser();
1143       try {
1144         return this._xmlParser.parseFromString(sXml, 'application/xml');
1145       }
1146       catch(e) {
1147         return {error: e.message};
1148       }
1149     }
1150     else {
1151       this._xmlParser = this._xmlParser || new global.ActiveXObject('Msxml2.DOMDocument.3.0');
1152        this._xmlParser.async = false;
1153        this._xmlParser.loadXML(sXml);
1154       return  this._xmlParser; 
1155     }
1156   },
1157   /**
1158     Callback used internally by the serializer
1159     @private
1160   */
1161   _jsonReplacer: function (sKey, oValue) {
1162     if (typeof oValue === 'string' && JUL.UI._regExps.isoDateStart.test(oValue)) {
1163       oValue = new Date(Date.UTC(parseInt(oValue.substr(0, 4)), parseInt(oValue.substr(5, 2)) - 1, parseInt(oValue.substr(8, 2)),
1164         parseInt(oValue.substr(11, 2)), parseInt(oValue.substr(14, 2)), parseInt(oValue.substr(17, 2)), oValue.substr(19, 1) === '.' ? parseInt(oValue.substr(20, 3)) : 0));
1165     }
1166     switch (JUL.typeOf(oValue)) {
1167     case 'Function':
1168       return (this._usePrefixes ? this._jsonPrefixes.func + ' ' : '') +
1169         oValue.toString().replace(JUL.UI._regExps.autoUseStrict, '$1');
1170     case 'RegExp':
1171       return (this._usePrefixes ? this._jsonPrefixes.regex + ' ' : '') +
1172         oValue.toString();
1173     case 'Date':
1174       return (this._usePrefixes ? this._jsonPrefixes.newop + ' ' : '') +
1175         'new Date(/*' + oValue.toUTCString().replace('UTC', 'GMT') + '*/' + oValue.getTime() + ')';
1176     default:
1177       return oValue;
1178     }
1179   },
1180   /**
1181     Copies an object to a format suitable for JSON.stringify(). Used only for old JSON engines
1182     @private
1183   */
1184   _jsonize: function (oData, _sKey) {
1185     var oValue = oData;
1186     if (typeof _sKey === 'undefined') { _sKey = ''; }
1187     else { oValue = oData[_sKey]; }
1188     if (oValue && typeof oValue === 'object' && typeof oValue.toJSON === 'function') {
1189       return this._jsonReplacer(_sKey, oValue.toJSON());
1190     }
1191     switch (JUL.typeOf(oValue)) {
1192     case 'Array':
1193       var aOut = [];
1194       for (var i = 0; i < oValue.length; i++) {
1195         try {
1196           aOut[i] = this._jsonize(oValue, i);
1197         }
1198         catch (e1) {
1199           aOut[i] = null;
1200         }
1201       }
1202       return aOut;
1203     case 'Object':
1204       var oOut = {};
1205       for (var sItem in oValue) {
1206         try {
1207           if (oValue.hasOwnProperty(sItem)) { oOut[sItem] = this._jsonize(oValue, sItem); }
1208         }
1209         catch (e2) {}
1210       }
1211       return oOut;
1212     default:
1213       return this._jsonReplacer(_sKey, oValue);
1214     }
1215   }
1216 });
1217 
1218 /* initiate a cyclic direct inheritance */
1219 JUL.UI.Parser.prototype = JUL.UI;
1220 
1221 })(typeof global !== 'undefined' ? global : window);
1222 
1223 /* end JUL.UI.js */
1224