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 9 The JavaScript UI Language (JUL) is a configuration and instantiation module for the JavaScript frameworks. 10 It can be used with any framework that accepts a configuration object as a constructor parameter, 11 or with a custom factory for the other frameworks.<br /> 12 JUL parses a tree of configuration objects, and creates the runtime components in the expected order and membership. 13 For the most uses, after this step, you will have your application's user interface up and running.<br> 14 <br>An example of the JUL tree for a generic 'FWK.Widgets' component library: 15 <pre><code>var oUiConfig = { 16 xclass: 'FWK.Widgets.DataView', 17 id: 'APP.mainView', 18 autoDraw: false, 19 overflow: 'hidden', 20 children: [{ 21 xclass: 'FWK.Widgets.VLayout', 22 id:'VLayout0', 23 children: [{ 24 xclass: 'FWK.Widgets.ToolStrip', 25 id: 'ToolStrip0', 26 visibilityMode: 'multiple' 27 }, { 28 xclass: 'FWK.Widgets.HLayout', 29 id: 'HLayout0', 30 children: [{ 31 xclass: 'FWK.Widgets.VLayout', 32 id: 'Navigation', 33 width: 200, 34 showResizeBar: true 35 }, { 36 xclass: 'FWK.Widgets.TabSet', 37 id: 'TabSet1', 38 tabs: [{ 39 xclass: 'FWK.Widgets.Tab', 40 title: 'Tab1', 41 id: 'Tab1' 42 }, { 43 xclass: 'FWK.Widgets.Tab', 44 title: 'Tab2', 45 id: 'Tab2' 46 }], 47 showTabScroller:true, 48 showTabPicker:true, 49 destroyPanes:false, 50 showEdges:false 51 }] 52 }] 53 }], 54 width: '100%', 55 height: '100%' 56 }; 57 var oParser = new JUL.UI.Parser({ 58 classProperty: 'xclass', 59 childrenProperty: 'children', 60 membersProperties: ['tabs'], 61 idProperty: 'id' 62 }); 63 oParser.create(oUiConfig); 64 APP.mainView.render();</code></pre> 65 Another example of the JUL tree for a XUL dialog: 66 <pre><code>var oUiConfig = { 67 tag: 'dialog', 68 id: 'dialog-browse', 69 title: 'Open', 70 width: 500, 71 height: 250, 72 hidden: true, 73 children: [ 74 {tag: 'listbox', id: 'listbox-browse', selType: 'single', 75 flex: 1, children: [ 76 {tag: 'listhead', children: [ 77 {tag: 'listheader', label: 'Name', width: 300} 78 ]}, 79 {tag: 'listbody', children: [ 80 {tag: 'listitem', children: [ 81 {tag: 'listcell', label: 'File 1'} 82 ]}, 83 {tag: 'listitem', children: [ 84 {tag: 'listcell', label: 'File 2'} 85 ]} 86 ]} 87 ]} 88 ], 89 }; 90 var oParser = new JUL.UI.Parser({ 91 defaultClass: 'xul', 92 useTags: true, 93 tagProperty: 'tag', 94 customFactory: 'JUL.UI.createDom', 95 topDown: true 96 }); 97 var oDialog = oParser.create(oUiConfig); 98 oDialog.show();</code></pre> 99 */ 100 /* jshint browser: true, curly: true, eqeqeq: true, expr: true, funcscope: true, immed: true, latedef: true, 101 onevar: true, newcap: true, noarg: true, node: true, strict: true, trailing: true, undef: true, unused: vars, wsh: true */ 102 /* globals JUL: true */ 103 104 (function(global) { 105 'use strict'; 106 /* initialize JUL global namespace */ 107 global.JUL = {}; 108 109 /* generated by JCS version 1.5.8 */ 110 111 /** 112 JUL global namespace 113 @namespace It holds the utility methods used by JUL 114 */ 115 JUL = { 116 /** 117 This allows setting a default root namespace instead of the global one 118 @type Object 119 */ 120 nsRoot: null, 121 /** 122 JUL version 123 @type String 124 */ 125 version: '1.6.8', 126 /** 127 Creates instances of the JUL global, which can be used as local variables. 128 E.g. <code>var oInstance = new JUL.Instance({nsRoot: myLocalVar});</code> 129 Special members of an instance:<ul> 130 <li>ui - instance of JUL.UI.Parser with JUL.UI defaults applied</li> 131 <li>ref - the same as JUL.Ref() factory</li> 132 <li>parser - shortcut to this.ui.Parser() factory</li></ul> 133 @class A class for creating instances of the main JUL object 134 @param {Object} [oConfig] Configuration object or an object to use as 'nsRoot' 135 */ 136 Instance: function (oConfig) { 137 if (!(this instanceof JUL.Instance)) { return new JUL.Instance(oConfig); } 138 JUL.apply(this, oConfig || {}); 139 var oThis = this; 140 var fInstance = function() { return oThis; }; 141 var FRef = function(oRef, sKey) { 142 if (!(this instanceof FRef)) { return new FRef(oRef, sKey); } 143 JUL.Ref.call(this, oRef, sKey); 144 }; 145 FRef. prototype = new JUL.Ref(); 146 FRef.prototype.constructor = FRef; 147 FRef.prototype._getJulInstance = fInstance; 148 this.ref = FRef; 149 this.ui = new JUL.UI.Parser(); 150 this.ui._getJulInstance = fInstance; 151 this.parser = this.ui.Parser; 152 }, 153 /** 154 Applies an object or an array of objects to a given object 155 @param {Object} oSource The source object to apply to 156 @param {Object} oAdd An Object or array of Objects to apply 157 @param {Boolean} bDontReplace Set it to true to don't replace existing members 158 @param {Array} [aFilterOut] If not null, the keys in this array will not be applied over 159 @param {Array} [aFilterIn] If not null, only the keys in this array will be applied over 160 @returns {Object} The source object with the applied members 161 */ 162 apply: function (oSource, oAdd, bDontReplace, aFilterOut, aFilterIn) { 163 if (!oAdd || typeof oAdd !== 'object') { return oSource; } 164 if (aFilterOut) { aFilterOut = [].concat(aFilterOut); } 165 if (aFilterIn) { aFilterIn = [].concat(aFilterIn); } 166 var oNew = bDontReplace ? {} : oSource; 167 var aMembers = [].concat(oAdd); 168 for (var i = 0; i < aMembers.length; i++) { 169 oAdd = aMembers[i]; 170 for (var sItem in oAdd) { 171 if (oAdd.hasOwnProperty(sItem) && 172 (!aFilterOut || aFilterOut.indexOf(sItem) < 0) && (!aFilterIn || aFilterIn.indexOf(sItem) > -1)) { 173 oNew[sItem] = oAdd[sItem]; 174 } 175 } 176 } 177 if (!bDontReplace) { return oSource; } 178 for (sItem in oNew) { 179 if (oNew.hasOwnProperty(sItem) && typeof oSource[sItem] === 'undefined') { oSource[sItem] = oNew[sItem]; } 180 } 181 return oSource; 182 }, 183 /** 184 Retrieves a member specified by a dotted path 185 @param {String} sPath The dotted path or object reference to retrieve 186 @param {Object} [oRoot] An optional object where to start the search from 187 @returns {Object} The requested member or undefined if not found 188 */ 189 get: function (sPath, oRoot) { 190 var oCurrent = oRoot || this.nsRoot || global; 191 if (!sPath) { return oCurrent; } 192 if (typeof sPath !== 'string') { return sPath; } 193 var aNames = this._square2dots(sPath, ':::::').split('.'); 194 if (!oRoot && aNames.length > 1 && ('window' === aNames[0] || 'global' === aNames[0])) { 195 aNames.shift(); 196 oCurrent = global; 197 } 198 var sItem = ''; 199 while (aNames.length) { 200 sItem = aNames.shift().replace(/:{5}/g, '.'); 201 if (!sItem) { continue; } 202 if (typeof oCurrent[sItem] === 'undefined') { return oCurrent[sItem]; } 203 oCurrent = oCurrent[sItem]; 204 } 205 return oCurrent; 206 }, 207 /** 208 Gets the JUL instance associated with a child object (e.g. a parser, a reference) 209 @param {Object} oChild Child object 210 @returns {Object} JUL instance or null if not available 211 */ 212 getInstance: function (oChild) { 213 if (oChild instanceof JUL.Ref || oChild instanceof JUL.UI.Parser) { 214 return oChild._getJulInstance ? oChild._getJulInstance() : JUL; 215 } 216 else { 217 return oChild === JUL.UI ? JUL : null; 218 } 219 }, 220 /** 221 Creates a wrapper that will call a certain function in a specific scope. 222 Useful for ensuring that a callback will get the desired scope. 223 @param {Object} oScope The scope to call the given function in 224 @param {Mixed} fCall The function to be called. If a string or an index, it will be the oScope method with that name. 225 @param {Boolean} [bAppendThis] If true, the actual calling context will be added as the last parameter of the called function 226 @returns {Function} The caller function 227 */ 228 makeCaller: function (oScope, fCall, bAppendThis) { 229 if (!oScope || (!fCall && fCall !== 0)) { return null; } 230 if (typeof fCall !== 'function') { 231 fCall = oScope[fCall]; 232 if (typeof fCall !== 'function') { return null; } 233 } 234 bAppendThis = (bAppendThis || false) && true; 235 /* try checking for duplicates */ 236 this._callers = this._callers || []; 237 for (var i = 0; i < this._callers.length; i++) { 238 if (oScope === this._callers[i][0] && fCall === this._callers[i][1] && bAppendThis === this._callers[i][2]) { return this._callers[i][3]; } 239 } 240 var fCaller = bAppendThis ? function() { return fCall.apply(oScope, [].slice.call(arguments).concat([this])); } : 241 function() { return fCall.apply(oScope, [].slice.call(arguments)); }; 242 if (this._callers.length > 16383) { this._callers = this._callers.slice(1024, 16384); } 243 this._callers.push([oScope, fCall, bAppendThis, fCaller]); 244 return fCaller; 245 }, 246 /** 247 Creates the specified namespace, and optionally initializes it 248 @param {String} sPath The dotted path for the namespace 249 @param {Object} [oInit] An optional initializer 250 @param {Object} [oRoot] The root object where to start from 251 @returns {Object} The created or existing namespace 252 */ 253 ns: function (sPath, oInit, oRoot) { 254 var aNames = sPath ? this._square2dots(sPath, ':::::').split('.') : []; 255 var sItem = ''; 256 var oRe = /^(\d|[1-9]\d+)$/; 257 var oCurrent = oRoot || this.nsRoot || global; 258 if (!oRoot && aNames.length > 1 && ('window' === aNames[0] || 'global' === aNames[0])) { 259 aNames.shift(); 260 oCurrent = global; 261 } 262 while (aNames.length) { 263 sItem = aNames.shift().replace(/:{5}/g, '.'); 264 if (!sItem) { continue; } 265 if (typeof oCurrent[sItem] === 'undefined') { oCurrent[sItem] = aNames.length && oRe.test(aNames[0]) ? [] : {}; } 266 if (!aNames.length && typeof oInit !== 'undefined') { oCurrent[sItem] = oInit; } 267 oCurrent = oCurrent[sItem]; 268 } 269 return oCurrent; 270 }, 271 /** 272 Trims a string at one or both ends 273 @param {String} sText The string to trim 274 @param {String} [sWhat] String pattern to match at the ends, it defaults to whitespace 275 @param {Boolean} [bLeftOrRight] If true trims left, if false trims right, if not specified trims at both ends 276 @returns {String} Trimmed string 277 */ 278 trim: function (sText, sWhat, bLeftOrRight) { 279 if (typeof sText !== 'string') { sText = sText.toString(); } 280 if (!sText) { return sText; } 281 var bUndef = typeof bLeftOrRight === 'undefined'; 282 if (!(bUndef || bLeftOrRight === true || bLeftOrRight === false) || (bUndef && !(sWhat || sWhat === 0))) { 283 if (typeof String.prototype.trim === 'function') { 284 return sText.trim(); 285 } 286 else { 287 return sText.replace(/\s+$/, '').replace(/^\s+/, ''); 288 } 289 } 290 else { 291 if (!(sWhat || sWhat === 0)) { 292 if (bLeftOrRight) { return sText.replace(/^\s+/, ''); } 293 else { return sText.replace(/\s+$/, ''); } 294 } 295 if (typeof sWhat !== 'string') { sWhat = sWhat.toString(); } 296 if (bUndef || bLeftOrRight === false) { 297 var nEnd = sText.length; 298 while (nEnd >= sWhat.length && sText.slice(nEnd - sWhat.length, nEnd) === sWhat) { nEnd = nEnd - sWhat.length; } 299 if (nEnd < sText.length) { sText = sText.slice(0, nEnd); } 300 } 301 if (sText.length < sWhat.length) { return sText; } 302 if (bUndef || bLeftOrRight === true) { 303 var nStart = 0; 304 while (nStart <= sText.length - sWhat.length && sText.slice(nStart, nStart + sWhat.length) === sWhat) { nStart = nStart + sWhat.length; } 305 if (nStart) { sText = sText.substr(nStart); } 306 } 307 return sText; 308 } 309 }, 310 /** 311 Returns the name of the object's native constructor 312 @param {Mixed} oData An object to get the native constructor name from 313 @returns {String} The native constructor name 314 */ 315 typeOf: function (oData) { 316 return ({}).toString.call(oData).match(/\w+/g)[1]; 317 }, 318 /** 319 Gets the JUL instance getter function for a given namespace root, with caching 320 @param {Object} [oNSRoot] Namespace root 321 @returns {Function} Instance getter 322 @private 323 */ 324 _getAutoInstance: function (oNSRoot) { 325 oNSRoot = oNSRoot || null; 326 if (oNSRoot === this.nsRoot) { return this; } 327 this._autoInstances = this._autoInstances || []; 328 for (var i = 0; i < this._autoInstances.length; i++) { 329 if (oNSRoot === this._autoInstances[i].nsRoot) { return this._autoInstances[i].getInstance; } 330 } 331 var oInstance = new JUL.Instance({nsRoot: oNSRoot}); 332 var fInstance = function() { return oInstance; }; 333 if (this._autoInstances.length > 1023) { this._autoInstances = this._autoInstances.slice(64, 1024); } 334 this._autoInstances.push({nsRoot: oNSRoot, getInstance: fInstance}); 335 return fInstance; 336 }, 337 /** 338 Converts a path with square brackets into a dotted path. 339 Use a backslash to escape a square bracket inside segments. 340 @param {String} sNS Input path 341 @param {String} [sDotEsc] If specified, the escaped dots will be replaced with this string 342 @returns {String} Output path 343 @private 344 */ 345 _square2dots: function (sNS, sDotEsc) { 346 sNS = sNS.replace(/\\\./g, sDotEsc || ':::::').replace(/\\\[/g, ';;;;;1').replace(/\\\]/g, ';;;;;2'); 347 sNS = sNS.replace(/(\[|\])+/g, '.').replace(/\.+/g, '.'); 348 sNS = this.trim(sNS, '.', false).replace(/;{5}2/g, ']').replace(/;{5}1/g, '['); 349 return sDotEsc ? sNS : sNS.replace(/:{5}/g, '\\.'); 350 } 351 }; 352 353 /* add 'indexOf' Array method, if not present */ 354 if (typeof Array.prototype.indexOf !== 'function') { 355 Array.prototype.indexOf = function(oSearch, nStart) { 356 for (var i = nStart || 0; i < this.length; i++) { 357 if (this[i] === oSearch) { return i; } 358 } 359 return -1; 360 }; 361 } 362 363 /* add 'map' Array method, if not present */ 364 if (typeof Array.prototype.map !== 'function') { 365 Array.prototype.map = function(fMap, oScope) { 366 if (typeof fMap !== 'function') { return null; } 367 var aResult = []; 368 for (var i = 0; i < this.length; i++) { 369 aResult.push(oScope ? fMap.call(oScope, this[i], i, this) : fMap(this[i], i, this)); 370 } 371 return aResult; 372 }; 373 } 374 375 /* make JUL.Instance to inherit the JUL members */ 376 JUL.apply(JUL.Instance.prototype, JUL, false, ['Instance', 'Ref', 'UI', 'version', '_getAutoInstance']); 377 378 })(typeof global !== 'undefined' ? global : window); 379 380 /* end JUL.js */ 381