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