1 /* 2 JWL - The JavaScript Widget Library version 0.8.7 3 Copyright (c) 2016 - 2020 The Zonebuilder <zone.builder@gmx.com> 4 http://sourceforge.net/projects/jwl-library/ 5 Licenses: GNU GPL2 or later; GNU LGPLv3 or later (http://sourceforge.net/p/jwl-library/wiki/License/) 6 */ 7 /** 8 @fileOverview The JavaScript Widget Library (JWL) is a tool that allows you to build modern web applications by providing a simple way to create HTML components. 9 JWL is based on JUL - The JavaScript UI Language module and it easily integrates with JUL Designer. 10 */ 11 /* jshint browser: true, curly: true, eqeqeq: true, evil: true, expr: true, funcscope: true, immed: true, latedef: true, loopfunc: 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, module) { 16 'use strict'; 17 if (module && module.exports && typeof require === 'function') { require('jul'); } 18 var jul = new JUL.Instance({nsRoot: module && module.exports ? {JWL: module.exports} : global || null}); 19 var JWL = jul.ns('JWL'); 20 21 /* generated by JCS version 1.5.8 */ 22 23 /** 24 JWL global namespace 25 @namespace It holds properties and methods used by JWL 26 @name JWL 27 */ 28 jul.ns('JWL'); 29 30 jul.apply(jul.get('JWL'), /** @lends JWL */ { 31 /** 32 JWL parser configuration 33 @type Object 34 */ 35 parserConfig: { 36 /** 37 Default class of the parser - HTML elements 38 @type String 39 */ 40 defaultClass: 'html', 41 /** 42 Order of component creation - parent before children 43 @type Boolean 44 */ 45 topDown: true, 46 /** 47 The parser will use tha tag property to differentiate between components 48 @type Boolean 49 */ 50 useTags: true, 51 /** 52 Special component factory that will do further processing compared to JUL.UI.createDom(): 53 <ul> 54 <li>if the config object is not a JWL component, return JUL.UI.createDom(oConfig)</li> 55 <li>call config/component pre-create callback with parent's scope</li> 56 <li>distribute dotted attributes 'objectID.attribute' among descendants</li> 57 <li>create element and recursively process its children</li> 58 <li>call config/component post-create callback with element's scope</li> 59 </ul> 60 @param {Object} oConfig Configuration object 61 @returns {Object} Element instance 62 */ 63 customFactory: function(oConfig) { 64 var sTag = oConfig[this.tagProperty]; 65 if (oConfig[this.classProperty] !== this.defaultClass) { sTag = oConfig[this.classProperty] + ':' + sTag; } 66 var oComponent = JWL.components[sTag]; 67 if (!oComponent) { 68 var fCreator = this._creator ? jul.get(this._creator) : null; 69 return typeof fCreator === 'function' ? fCreator.call(this, oConfig) : this.createDom(oConfig); 70 } 71 oConfig._componentIndex = sTag; 72 var fPreCreate = oConfig.preCreate || oComponent.preCreate; 73 if (fPreCreate) { fPreCreate = jul.get(fPreCreate); } 74 delete oConfig.preCreate; 75 var fPostCreate = oConfig.postCreate || oComponent.postCreate; 76 if (fPostCreate) { fPostCreate = jul.get(fPostCreate); } 77 if (fPostCreate) { oConfig.postCreate = fPostCreate; } 78 var oHost = oConfig[this.parentProperty] ? oConfig[this.parentProperty].host || oConfig[this.parentProperty] : null; 79 if (oHost && oHost._componentName && oHost._derived) { 80 oConfig[this.classProperty] = this.defaultClass; 81 oConfig[this.tagProperty] = 'div'; 82 } 83 else if (oComponent.ui) { 84 oConfig[this.classProperty] = oComponent.ui[this.classProperty] || this.defaultClass; 85 oConfig[this.tagProperty] = oComponent.ui[this.tagProperty]; 86 } 87 if (typeof fPreCreate === 'function') { 88 var bReturn = fPreCreate.call(oConfig[this.parentProperty], oConfig, this); 89 if (bReturn === false) { return null; } 90 } 91 fPostCreate = oConfig.postCreate; 92 delete oConfig.postCreate; 93 var oInit = oConfig; 94 oConfig = JUL.apply({}, oConfig); 95 delete oConfig._componentIndex; 96 var oLogic = {}; 97 var sBinding = oComponent.ui ? oComponent.ui[this.bindingProperty] : '.component'; 98 if (!oComponent.ui) { oConfig[this.bindingProperty] = sBinding; } 99 var oRootLogic = {}; 100 oLogic[sBinding] = oRootLogic; 101 var sItem, nDot; 102 for (sItem in oConfig) { 103 if (oConfig.hasOwnProperty(sItem)) { 104 nDot = sItem.indexOf('.', 1); 105 if (nDot > 0 && nDot < sItem.length - 1) { 106 sBinding = sItem.slice(0, nDot); 107 oLogic[sBinding] = oLogic[sBinding] || {}; 108 oLogic[sBinding][sItem.substr(nDot + 1)] = oConfig[sItem]; 109 delete oConfig[sItem]; 110 } 111 } 112 } 113 var oListeners = oConfig.listeners || {}; 114 delete oConfig.listeners; 115 oRootLogic.listeners = oListeners; 116 for (sItem in oListeners) { 117 if (oListeners.hasOwnProperty(sItem)) { 118 nDot = sItem.indexOf('.', 1); 119 if (nDot > 0 && nDot < sItem.length - 1) { 120 sBinding = sItem.slice(0, nDot); 121 oLogic[sBinding] = oLogic[sBinding] || {}; 122 oLogic[sBinding].listeners = oLogic[sBinding].listeners || {}; 123 oLogic[sBinding].listeners[sItem.substr(nDot + 1)] = oListeners[sItem]; 124 delete oListeners[sItem]; 125 } 126 } 127 } 128 for (sItem in oConfig) { 129 if (oConfig.hasOwnProperty(sItem) && 130 [this.classProperty, this.tagProperty, this.idProperty, this.bindingProperty, this.parentProperty, this.cssProperty].indexOf(sItem) < 0) { 131 oRootLogic[sItem] = oConfig[sItem]; 132 delete oConfig[sItem]; 133 } 134 } 135 if (oComponent.ui) { oConfig[this.includeProperty] = oComponent.ui; } 136 if (oComponent.logic) { oLogic[this.includeProperty] = oComponent.logic; } 137 var oWidget = this.create(oConfig, oLogic, oConfig.parent); 138 if (typeof fPostCreate === 'function') { fPostCreate.call(oWidget, oInit, this); } 139 return oWidget; 140 }, 141 /** 142 Use JSON prefixes when serializing objects 143 @type Boolean 144 @private 145 */ 146 _usePrefixes: true, 147 /** 148 Special merging callback for parser's include() method that will do further processing compared to JUL.apply(): 149 <ul> 150 <li>if the object is not a component (binding ID doesn't start with a dot), call JUL.apply(oData, oApply)</li> 151 <li>attach object's listeners in cascade</li> 152 <li>append object's CSS class</li> 153 <li>call JUL.apply() on the other members</li> 154 </ul> 155 @param {Object} oData Target object 156 @param {Object} oApply Appended object 157 @param {Boolean} [bSkip] For internal use, to process component's logic 158 @returns {Object} Target object with the resulting members 159 @private 160 */ 161 _includeMerger: function(oData, oApply, bSkip) { 162 var oListeners = oData.listeners; 163 var sItem; 164 if (bSkip) { 165 JUL.apply(oData, oApply); 166 } 167 else { 168 for (sItem in oApply) { 169 if (oApply.hasOwnProperty(sItem)) { 170 if (sItem.substr(0, 1) === '.' && sItem.length > 1 && JUL.typeOf(oApply[sItem]) === 'Object' && 171 oData[sItem] && JUL.typeOf(oData[sItem]) === 'Object') { this._includeMerger(oData[sItem], oApply[sItem], true); } 172 else { 173 if (sItem === this.cssProperty) { oData[sItem] = oApply[sItem] + (oData[sItem] ? ' ' + oData[sItem] : ''); } 174 else { oData[sItem] = oApply[sItem]; } 175 } 176 } 177 } 178 } 179 if (!oListeners || !oApply.listeners) { return oData; } 180 for (sItem in oApply.listeners) { 181 if (oApply.listeners.hasOwnProperty(sItem)) { 182 if (sItem !== 'scope' && oListeners[sItem]) { 183 oListeners[sItem] = [].concat(oListeners[sItem], oApply.listeners[sItem]); 184 } 185 else { 186 oListeners[sItem] = oApply.listeners[sItem]; 187 } 188 } 189 } 190 oData.listeners = oListeners; 191 return oData; 192 }, 193 /** 194 Callback for un-serializing objects. Use it in a safe data environment because of eval(). 195 @param {String} sKey Current member key 196 @param {Mixed} oValue Current member value 197 @returns {Mixed} Member's processed value 198 @private 199 */ 200 _jsonReviver: function(sKey, oValue) { 201 if (sKey && typeof oValue === 'string') { 202 var bPrefix = false; 203 for (var sItem in this._jsonPrefixes) { 204 if (this._jsonPrefixes.hasOwnProperty(sItem) && 205 oValue.substr(0, this._jsonPrefixes[sItem].length) === this._jsonPrefixes[sItem]) { 206 bPrefix = true; 207 oValue = oValue.substr(this._jsonPrefixes[sItem].length).replace(/^\s+/, ''); 208 break; 209 } 210 } 211 if (bPrefix) { 212 try { 213 return eval('(function(){return ' + oValue + '})()'); 214 } 215 catch(e) { 216 return oValue; 217 } 218 } 219 } 220 return oValue; 221 } 222 }, 223 /** 224 Default prefix for custom element tags 225 @type String 226 */ 227 registerPrefix: 'jwl', 228 /** 229 JWL version 230 @type String 231 */ 232 version: '0.8.5', 233 /** 234 Creates a new parser derived from JWL.parser 235 @class 236 @extends JWL.parser 237 @param {Object} oConfig Configuration object 238 */ 239 Parser: function(oConfig) { 240 if (!(this instanceof JWL.Parser)) { 241 return new JWL.Parser(oConfig); 242 } 243 jul.ui.Parser.call(this, oConfig); 244 }, 245 /** 246 Callback to be used as the JWL.Parser custom factory to self-register and to create custom HTML elements. 247 e.g. var oParser = new JWL.Parser({customFactory: 'JWL.custom'}); 248 @param {Object} oConfig Configuration object 249 @returns {Object} New element 250 */ 251 custom: function(oConfig) { 252 var sName = oConfig[this.tagProperty]; 253 if (oConfig[this.classProperty] !== this.defaultClass) { sName = oConfig[this.classProperty] + ':' + sName; } 254 var oComponent = JWL.components[sName]; 255 if (oComponent) { 256 JWL._customCache = JWL._customCache || {}; 257 var sKey = oConfig[this.classProperty] + ':' + oConfig[this.tagProperty]; 258 if (typeof JWL._customCache[sKey] !== 'function') { 259 try { 260 JWL._customCache[sKey] = JWL.register(sName, false, '', this); 261 } 262 catch (e) {} 263 } 264 oConfig[this.tagProperty] = JWL.registerPrefix + '-' + 265 (oConfig[this.classProperty] === this.defaultClass ? '' : oConfig[this.classProperty] + '-') + 266 oConfig[this.tagProperty]; 267 oConfig[this.classProperty] = this.defaultClass; 268 } 269 return this.createDom(oConfig); 270 }, 271 /** 272 Callback to be used as the JWL.Parser custom factory to create element wrappers. 273 e.g. var oParser = new JWL.Parser({customFactory: 'JWL.factory'}); 274 @param {Object} oConfig Configuration object 275 @returns {Object} Element wrapper instance 276 */ 277 factory: function(oConfig) { 278 var sName = oConfig[this.tagProperty]; 279 var sNS = oConfig[this.classProperty]; 280 var CNew = jul.get('JWL.' + sNS.toUpperCase() + '.' + sName[0].toUpperCase() + sName.substr(1)); 281 if (typeof CNew !== 'function') { CNew = JWL.makeClass(sNS + ':' + sName, false, this); } 282 return new CNew(oConfig); 283 }, 284 /** 285 Gets a wrapper object associated with an element 286 @param {Object} oEl DOM element or element ID 287 @returns {Object} Element wrapper, component root element, of null if not found 288 */ 289 get: function(oEl) { 290 if (typeof oEl === 'string') { oEl = document.getElementById(oEl); } 291 if (typeof JWL._Base_ === 'function'&& oEl instanceof JWL._Base_) { return oEl; } 292 if (oEl && typeof oEl.el === 'function') { oEl = oEl.el(); } 293 return oEl && typeof JWL._Base_ === 'function' ? JWL._Base_.get(oEl) || oEl : oEl || null; 294 }, 295 /** 296 Loads component(s) from a string serialization 297 @param {String} sData Parameter description 298 @param {String} [sWhat] Name of a specific component to load 299 @param {String} [sRoot] Dotted path to read the component from the un-serialized object 300 @returns {Object} Component configuration 301 */ 302 load: function(sData, sWhat, sRoot) { 303 var oData = null; 304 try { 305 oData = JSON.parse(sData, JUL.makeCaller(this.parser, '_jsonReviver')); 306 } 307 catch (e) {} 308 if (oData && sRoot) { oData = JUL.get(sRoot, oData); } 309 if (!oData || typeof oData !== 'object') { return null; } 310 return jul.ns('JWL.components' + (sWhat ? '.' + sWhat : ''), oData); 311 }, 312 /** 313 Loads external CSS for a component using XMLHttpRequest 314 @param {String} sComponent Component name 315 @param {Function} [fCall] If supplied, the CSS will be loaded asynchronously 316 @param {Object} [oScope] Optional scope to run the callback in 317 @returns {Array} Array of CSS descriptors 318 */ 319 loadCss: function(sComponent, fCall, oScope) { 320 var oComponent = this.components[sComponent] || {}; 321 if (!oComponent.css) { return null; } 322 var bCall = typeof fCall === 'function'; 323 this._cssCache = this._cssCache || {}; 324 var aCss; 325 if (this._cssCache[sComponent]) { 326 aCss = this._cssCache[sComponent]; 327 if (bCall) { 328 (oScope ? fCall.call(oScope, aCss) : fCall(aCss)); 329 } 330 return aCss; 331 } 332 var oWatch = { 333 values: [], 334 total: 0, 335 count: 0, 336 ready: null, 337 scope: this, 338 set: function(nKey, oValue) { 339 var sUrl = ''; 340 if (nKey >= 0) { 341 sUrl = this.values[nKey].style; 342 this.values[nKey].style = oValue; 343 } 344 this.count++; 345 if (this.count >= this.total && typeof this.ready === 'function') { 346 this.ready.call(this.scope); 347 } 348 if (sUrl && this.scope.__pending[sUrl]) { 349 var aCalls = this.scope.__pending[sUrl]; 350 for (var i = 0; i < aCalls.length; i++) { 351 aCalls[i].watch.set(aCalls[i].key, oValue); 352 } 353 delete this.scope.__pending[sUrl]; 354 } 355 } 356 }; 357 this.__pending = this.__pending || {}; 358 var fLoad = function(sUrl, nKey) { 359 if (this.__pending[sUrl]) { 360 this.__pending[sUrl].push({watch: oWatch, key: nKey}); 361 return; 362 } 363 else { 364 this.__pending[sUrl] = []; 365 } 366 var oHttp; 367 try { 368 if (typeof window.XMLHttpRequest === 'function') { 369 oHttp = new XMLHttpRequest(); 370 if (bCall) { 371 oHttp.addEventListener('load', function() { 372 oWatch.set(nKey, this.responseText || ''); 373 }); 374 oHttp.addEventListener('error', function() { 375 oWatch.set(nKey, ''); 376 }); 377 } 378 oHttp.open('GET', sUrl, bCall); 379 oHttp.setRequestHeader('Content-Type', 'text/css; charset=UTF-8'); 380 oHttp.send(null); 381 if (!bCall) { oWatch.set(nKey, oHttp.responseText || ''); } 382 } 383 else { 384 oHttp = new ActiveXObject('WinHttp.WinHttpRequest.5.1'); 385 oHttp.Open('GET', sUrl, false); 386 oHttp.SetRequestHeader('Content-Type', 'text/css; charset=UTF-8'); 387 oHttp.Send(); 388 oWatch.ser(nKey, oHttp.ResponseText || ''); 389 } 390 } 391 catch (e) { 392 oWatch.set(nKey, ''); 393 } 394 }; 395 aCss = [].concat(oComponent.css); 396 oWatch.ready = function() { 397 for (var i = 0; i < aCss.length; i++) { 398 if (!aCss[i].style) { 399 aCss.splice(i, 1); 400 i--; 401 } 402 } 403 this._cssCache[sComponent] = aCss; 404 if (bCall) { 405 (oScope ? fCall.call(oScope, aCss) : fCall(aCss)); 406 } 407 }; 408 oWatch.total = aCss.length; 409 oWatch.values = aCss; 410 for (var i = 0; i < aCss.length; i++) { 411 if (typeof aCss[i] !== 'object') { aCss[i] = {style: aCss[i], media: ''}; } 412 var sUrl = encodeURI(JUL.trim(aCss[i].style.substr(0, 1024).split('\n'))); 413 if (aCss[i].style && aCss[i].style.indexOf(';') < 0 && sUrl) { 414 fLoad.call(this, sUrl, i); 415 } 416 else { 417 oWatch.set(-1, '' ); 418 } 419 } 420 return aCss; 421 }, 422 /** 423 Creates an element wrapper class for a specific component 424 @param {String} sName Component name or existing HTML tag 425 @param {String} [sNewNS] Optional namespace under to put the class constructor, it defaults to 'JWL.<Name> 426 @param {Object} [oParser] Custom instance of JUL.UI.Parser to use instead of the default JWL.parser 427 @returns {Function} Class constructor 428 */ 429 makeClass: function(sName, sNewNS, oParser) { 430 oParser = oParser || this.parser; 431 if (sName.indexOf(oParser.defaultClass + ':') === 0) { sName = sName.substr(oParser.defaultClass.length + 1); } 432 var oComponent = this.components[sName] || {}; 433 var sNS = oParser.defaultClass; 434 if (sName.indexOf(':') > -1) { 435 sNS = sName.split(':')[0]; 436 sName = sName.substr(sNS.length + 1); 437 } 438 if (typeof JWL._Base_ !== 'function') { 439 JWL._Base_ = function() {}; 440 JUL.apply(JWL._Base_.prototype, { 441 get: function(sItem) { return sItem in this ? this[sItem] : JWL._Base_.wrapUp(this._el[sItem]); }, 442 set: function(sItem, oValue, bHost) { (bHost || sItem in this ? this : this._el)[sItem] = 443 !bHost && !(sItem in this) && JWL._Base_.onRe.test(sItem) && typeof oValue === 'function' ? 444 JUL.makeCaller(this, oValue) : oValue; }, 445 el: function() { return this._el; }, 446 config: function() { return this._config; } 447 }); 448 JUL.apply(JWL._Base_, { 449 id: 1, 450 items: {}, 451 attributeName: 'data-component-id', 452 onRe: /^on[a-z]+$/, 453 map: function(oComponent, sItem) { 454 var oEl = oComponent.el(); 455 if (!oEl) { return; } 456 if (sItem && typeof sItem !== 'string') { sItem = sItem.getAttribute(this.attributeName); } 457 if (!sItem) { sItem = 'c-' + this.id++; } 458 this.items[sItem] = oComponent; 459 oEl.setAttribute(this.attributeName, sItem); 460 }, 461 get: function(sId) { 462 if (sId && typeof sId !== 'string') { sId = sId.getAttribute(this.attributeName); } 463 return sId ? this.items[sId] || null : null; 464 }, 465 cleanUp: function() { 466 if ((this.id - 1) % 4096) { return; } 467 var oItems = {}; 468 var fClean = function(oNode) { 469 for (var i = 0; i < oNode.childNodes.length; i++) { 470 var oChild = oNode.childNodes[i]; 471 if (oChild.nodeType !== 1) { continue; } 472 var sItem = oChild.getAttribute(this.attributeName); 473 if (sItem) { oItems[sItem] = this.items[sItem]; } 474 fClean.call(this, oChild); 475 } 476 }; 477 fClean.call(this, document.body); 478 this.items = oItems; 479 }, 480 wrapUp: function(oData) { 481 try { 482 if (!oData) { return oData; } 483 if (oData instanceof Element && oData.nodeType === 1) { 484 return oData.getAttribute(this.attributeName) ? this.get(oData) : oData; 485 } 486 if (typeof oData === 'object' && typeof oData.length && oData[0] instanceof Element) { 487 var aReturn = []; 488 for (var i = 0; i < oData.length; i++) { 489 aReturn[i] = oData[i].nodeType === 1 && oData[i].getAttribute(this.attributeName) ? this.get(oData[i]) : oData[i]; 490 } 491 return aReturn; 492 } 493 } 494 catch (e) {} 495 return oData; 496 } 497 }); 498 } 499 var fClass = function(oConfig, oEl) { 500 oConfig = oConfig || {}; 501 this._config = JUL.apply({}, oConfig); 502 delete this._config[this._parser.parentProperty]; 503 oConfig = JUL.apply({}, oConfig); 504 oConfig[this._parser.classProperty] = sNS; 505 oConfig[this._parser.tagProperty] = sName; 506 oConfig.listeners = JUL.apply({scope: this}, oConfig.listeners || {}); 507 if (oEl) { 508 this._parser.createDom(oConfig, oEl); 509 this._el = oEl; 510 } 511 else { 512 this._el = JWL.parser.customFactory.call(this._parser, oConfig); 513 if (this._el instanceof JWL._Base_) { 514 oEl = this._el.el(); 515 oEl.setAttribute('class', (oEl.getAttribute('class') || '') + ' ' + JWL.registerPrefix + '-' + 516 (sNS === this._parser.defaultClass ? '' : sNS + '-') + 517 ((oComponent.ui || {})[this._parser.cssProperty] || sName).split(' ').shift()); 518 this._el = oEl; 519 JWL._Base_.map(this, oEl); 520 return; 521 } 522 } 523 JWL._Base_.map(this); 524 JWL._Base_.cleanUp(); 525 }; 526 fClass.prototype = new JWL._Base_(); 527 fClass.prototype.constructor = fClass; 528 fClass.prototype._parser = oParser; 529 this._protoCache = this._protoCache || {}; 530 var sItem, oProto; 531 var sCompNS = oComponent.ui ? oComponent.ui[oParser.classPrpperty] || oParser.defaultClass : sNS; 532 var sCompTag = oComponent.ui ? oComponent.ui[oParser.tagProperty] : sName; 533 var sTag = sCompNS + ':' + sCompTag; 534 if (!this._protoCache[sTag]) { 535 this._protoCache[sTag] = {attributes: false, baseURI: false, childNodes: false, className: false, 536 dataset: false, firstChild: false, innerHTML: false, lastChild: false, localName: false, 537 namespaceURI: false, nextSibling: false, nodeName: false, nodeType: false, nodeValue: false, 538 ownerDocument: false, parentNode: false, prefix: false, previousSibling : false, style: false, 539 tabIndex: false, tagName: false, 540 addEventListener: true, appendChild: true, blur: true, cloneNode: true, 541 compareDocumentPosition: true, dispatchEvent: true, focus: true, getAttribute: true, 542 getAttributeNode: true, getAttributeNodeNS: true, getAttributeNS: true, getBoundingClientRect: true, 543 getElementsByTagName: true, getElementsByTagNameNS: true, hasAttribute: true, hasAttributeNS: true, 544 hasAttributes: true, hasChildNodes: true, insertBefore: true, isDefaultNamespace: true, 545 lookupNamespaceURI: true, lookupPrefix: true, normalize: true, querySelector: true, querySelectorAll: true, 546 releaseCapture: true, removeAttribute: true, removeAttributeNode: true, removeAttributeNS: true, 547 removeChild: true, removeEventListener: true, replaceChild: true, scrollIntoView: true, setAttribute: true, 548 setAttributeNode: true, setAttributeNodeNS: true, setAttributeNS: true, setCapture: true}; 549 var oFilter = /^[a-z]/; 550 var oProtoConfig = {}; 551 oProtoConfig[oParser.classProperty] = sCompNS; 552 oProtoConfig[oParser.tagProperty] = sCompTag; 553 oProto = oParser.createDom(oProtoConfig); 554 for (sItem in oProto) { 555 if (!oFilter.test(sItem)) { continue; } 556 try { 557 this._protoCache[sTag][sItem] = typeof oProto[sItem] === 'function'; 558 } 559 catch (e) {} 560 } 561 } 562 oProto = this._protoCache[sTag]; 563 this._methodCache = this._methodCache || {getter: {}, setter: {}, other: {}}; 564 for (sItem in oProto) { 565 if (oProto.hasOwnProperty(sItem)) { 566 if (oProto[sItem]) { 567 this._methodCache.other[sItem] = this._methodCache.other[sItem] || (function(sItem) { 568 return function() { 569 if (!this._el) { return undefined; } 570 var bEvent = sItem === 'addEventListener' || sItem === 'attachEvent'; 571 var aArgs = [].slice.call(arguments); 572 for (var i = 0; i < aArgs.length; i++) { 573 // re-scope event listeners at explicit assignment 574 if (bEvent && typeof aArgs[i] === 'function') { aArgs[i] = JUL.makeCaller(this, aArgs[i]); } 575 if (aArgs[i] instanceof JWL._Base_) { aArgs[i] = aArgs[i].el(); } 576 } 577 return JWL._Base_.wrapUp(this._el[sItem].apply(this._el, aArgs)); 578 }; 579 })(sItem); 580 fClass.prototype[sItem] = this._methodCache.other[sItem]; 581 } 582 else { 583 this._methodCache.getter[sItem] = this._methodCache.getter[sItem] || (function(sItem) { 584 return function() { 585 return this._el ? JWL._Base_.wrapUp(this._el[sItem]) : undefined; 586 }; 587 })(sItem); 588 this._methodCache.setter[sItem] = this._methodCache.setter[sItem] || (function(sItem) { 589 return function(oValue) { 590 if (oValue instanceof JWL._Base_) { oValue = oValue.el(); } 591 if (this._el) { 592 // re-scope 'on' handler at direct assignment; careful not using a function expression with an 'on' attribute 593 if (JWL._Base_.onRe.test(sItem) && typeof oValue === 'function') { 594 oValue = JUL.makeCaller(this, oValue); 595 } 596 this._el[sItem] = oValue; 597 } 598 }; 599 })(sItem); 600 try { 601 Object.defineProperty(fClass.prototype, sItem, { 602 configurable: true, 603 enumerable: true, 604 get: this._methodCache.getter[sItem], 605 set: this._methodCache.setter[sItem] 606 }); 607 } 608 catch (e1) {} 609 } 610 } 611 } 612 JUL.apply(fClass.prototype, oComponent.prototype || {}); 613 var sWhat = 'JWL.' + sNS.toUpperCase() + '.' + sName[0].toUpperCase() + sName.substr(1); 614 return jul.ns(sNewNS || sWhat, fClass); 615 }, 616 /** 617 Registers a JWL component as a custom HTML element (W3C web components) 618 @param {String} sName Component name 619 @param {Boolean} [bDerived] Set it to true to register derived a custom HTML element 620 @param {String} [sNewName] Optional new tag name instead of prepending 'jwl-' to the component name 621 @param {Object} [oParser] Custom instance of JUL.UI.Parser to use instead of the default JWL.parser 622 @returns {Function} Registered element constructor 623 */ 624 register: function(sName, bDerived, sNewName, oParser) { 625 oParser = oParser || this.parser; 626 if (sName.indexOf(oParser.defaultClass + ':') === 0) { sName = sName.substr(oParser.defaultClass.length + 1); } 627 var oComponent = this.components[sName] || {}; 628 var oPrototype = JUL.apply({ 629 _componentName: sName, 630 _derived: bDerived && true, 631 _parser: oParser, 632 el: function() { 633 return (this.shadowRoot || this).lastChild; 634 }, 635 createdCallback: function() { 636 var oRoot = this; 637 if (this.shadowRoot) { oRoot = this.shadowRoot; } 638 else if (typeof this.createShadowRoot === 'function') { oRoot = this.createShadowRoot(); } 639 var oComponent = JWL.components[this._componentName] || {}; 640 if (oComponent.css) { 641 var n = [].concat(oComponent.css).length; 642 for (var i = 0; i < n; i++) { 643 var oStyle = document.createElement('style'); 644 oStyle.setAttribute('type', 'text/css' ); 645 oStyle.appendChild(document.createTextNode('noscript {}')); 646 oRoot.appendChild(oStyle); 647 } 648 JWL.loadCss(this._componentName, function(aCss) { 649 for (var i = 0; i < aCss.length; i++) { 650 var oStyle = document.createElement('style'); 651 oStyle.setAttribute('type', 'text/css' ); 652 if (aCss[i].media) { oStyle.setAttribute('media', aCss[i].media); } 653 oStyle.appendChild(document.createTextNode(aCss[i].style)); 654 oRoot.replaceChild(oStyle, oRoot.childNodes[i]); 655 } 656 }, this); 657 } 658 }, 659 connectedCallback: function() { 660 this.attachedCallback(); 661 }, 662 attachedCallback: function() { 663 var oRoot = this.shadowRoot || this; 664 if (oRoot.childNodes.length && oRoot.lastChild.nodeName.toLowerCase() !== 'style') { return; } 665 var oComponent = JWL.components[this._componentName] || {}; 666 var sNS = this._parser.defaultClass; 667 var sName = this._componentName; 668 if (sName.indexOf(':') > -1) { 669 sNS = sName.split(':')[0]; 670 sName = sName.substr(sNS.length + 1); 671 } 672 this.setAttribute('class', (this.getAttribute('class') || '') + ' ' + JWL.registerPrefix + '-' + 673 (sNS === this._parser.defaultClass ? '' : sNS + '-') + 674 ((oComponent.ui || {})[this._parser.cssProperty] || sName).split(' ').shift()); 675 if (!oComponent.ui) { return; } 676 var oConfig = {}; 677 oConfig[this._parser.classProperty] = sNS; 678 oConfig[this._parser.tagProperty] = sName; 679 oConfig[this._parser.parentProperty] = oRoot; 680 oConfig.listeners = JUL.apply({scope: this}, oConfig.listeners || {}); 681 JWL.parser.customFactory.call(this._parser, oConfig); 682 } 683 }, oComponent.prototype || {}); 684 var sNS = oParser.defaultClass; 685 if (sName.indexOf(':') > -1) { 686 sNS = sName.split(':')[0]; 687 sName = sName.substr(sNS.length + 1); 688 } 689 var sTag = ''; 690 if (bDerived) { 691 var sCompNS = oComponent.ui ? oComponent.ui[oParser.classPrpperty] || oParser.defaultClass : sNS; 692 var sCompTag = oComponent.ui ? oComponent.ui[oParser.tagProperty] : sName; 693 sTag = (sCompNS === oParser.defaultClass ? '' : sCompNS + ':') + sCompTag; 694 var oElConfig = {}; 695 oElConfig[oParser.classProperty] = sCompNS; 696 oElConfig[oParser.tagProperty] = sCompTag; 697 var oEl = oParser.createDom(oElConfig); 698 oPrototype = JUL.apply(Object.create(oEl.constructor.prototype), oPrototype); 699 } 700 else { 701 oPrototype = JUL.apply(Object.create(HTMLElement.prototype), oPrototype); 702 } 703 sName = this.registerPrefix + '-' + (sNS === oParser.defaultClass ? '' : sNS + '-') + sName; 704 if (window.customElements && typeof window.customElements.define === 'function') { 705 var CC = function() { 706 var oEl = window.Reflect.construct(oPrototype.constructor, [].slice.call(arguments) , CC); 707 oEl.attachShadow({mode: 'open'}); 708 oEl.createdCallback(); 709 return oEl; 710 }; 711 CC.prototype = oPrototype; 712 var oCE = window.customElements; 713 oCE.define.apply(oCE, [sName, CC].concat(bDerived ? {'extends': sTag} : [])); 714 return CC; 715 } 716 return document.registerElement(sNewName || sName, bDerived ? 717 {'extends': sTag, prototype: oPrototype} : {prototype: oPrototype}); 718 }, 719 /** 720 Saves component(s) into a JSON serialization 721 @param {String} [sWhat] Component to serialize, if omitted all components are saved 722 @returns {String} JSON string containing serialized component(s) 723 */ 724 save: function(sWhat) { 725 return this.parser.obj2str(sWhat ? this.components[sWhat] : this.components, true); 726 }, 727 /** 728 Creates and triggers a custom DOM event 729 @param {Object} oTarget Target element or element wrapper to trigger the event on 730 @param {String} sEvent Event name 731 @param {Object} [oInit] Hash of additional event properties 732 @returns {Boolean} The result of dispatching the event to its cascaded listeners 733 */ 734 trigger: function(oTarget, sEvent, oInit) { 735 if (typeof JWL._Base_ === 'function' && oTarget instanceof JWL._Base_) { oTarget = oTarget.el(); } 736 oInit = JUL.apply({bubbles: true, cancelable: true, composed: true}, oInit || {}); 737 var nSep = sEvent.indexOf(':'); 738 var sClass = nSep < 0 ? 'Event' : sEvent.slice (0, nSep); 739 if (nSep > -1) { sEvent = sEvent.substr(nSep + 1); } 740 var oEvent = null; 741 if (typeof window[sClass] === 'function') { 742 oEvent = new window[sClass](sEvent, oInit); 743 } 744 else if (typeof document.createEvent === 'function') { 745 oEvent = document.createEvent(sClass ); 746 var aArgs = [sEvent]; 747 for (var sItem in oInit) { 748 if (oInit.hasOwnProperty(sItem)) { aArgs.push(oInit[sItem]); } 749 } 750 oEvent['init' + sClass].apply(oEvent, aArgs); 751 } 752 else { 753 oEvent = document.createEventObject(); 754 JUL.apply(oEvent, oInit); 755 try { 756 return oTarget.fireEvent('on' + sEvent, oEvent); 757 } 758 catch (e1) { return false; } 759 } 760 return oTarget.dispatchEvent(oEvent); 761 } 762 }); 763 764 jul.ns('JWL.components'); 765 JWL.parser = new jul.ui.Parser(JWL.parserConfig); 766 JWL.Parser.prototype = JWL.parser; 767 768 })(typeof global !== 'undefined' ? global : window, typeof module !== 'undefined' ? module : null); 769 770 /* end JWL.js */ 771