1 | "use strict";
|
2 |
|
3 | var Finder = require("../lib/dependencies-finder");
|
4 |
|
5 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 3600000;
|
6 |
|
7 |
|
8 | describe('Module "dependencies-finder"', function() {
|
9 | var check = function(file, requires) {
|
10 | it('should find requires in ' + file, function() {
|
11 | expect( Finder(FILES[file]).requires ).toEqual( requires );
|
12 | });
|
13 | };
|
14 | describe('should find requires', function() {
|
15 | check( "x-widget", ['dom', 'tfw.data-binding'] );
|
16 |
|
17 | check( "comments", ['YoMan'] );
|
18 | check( "tp4.wdg-event", [
|
19 | 'dom', 'tfw.data-binding', 'wdg.text', 'wdg.flex',
|
20 | 'tp4.button.edit-intl'
|
21 | ]);
|
22 | check( "tfw.binding", ['tfw.binding.converters', 'tfw.binding.property-manager'] );
|
23 | check( "dom", ['$', 'polyfill.classList', 'tfw.pointer-events'] );
|
24 | });
|
25 | });
|
26 |
|
27 |
|
28 |
|
29 | var FILES = {
|
30 | "strings": `// Prout
|
31 | require("A");
|
32 | require('C\'est');
|
33 | require("E\"F")`,
|
34 | "comments": `
|
35 | /**
|
36 | * @example
|
37 | * var Toto = require("Alright");
|
38 | */
|
39 | var Titi = require("YoMan");
|
40 | `,
|
41 | "tp4.wdg-event": `"use strict";
|
42 |
|
43 | var $ = require("dom");
|
44 | var DB = require("tfw.data-binding");
|
45 | var Text = require( "wdg.text" );
|
46 | var Flex = require( "wdg.flex" );
|
47 | var Description = require( "tp4.button.edit-intl" );
|
48 |
|
49 |
|
50 | /**
|
51 | * @class Event
|
52 | *
|
53 | * @param {boolean} opts.visible - Set the visiblity of the component.
|
54 | *
|
55 | * @example
|
56 | * var Tp.Wdg.Event = require("tp.wdg.event");
|
57 | * var instance = new Tp.Wdg.Event({visible: false});
|
58 | */
|
59 | var Event = function(opts) {
|
60 | var inpName = new Text({
|
61 | label: _("name"),
|
62 | width: "320px"
|
63 | });
|
64 | var inpCode = new Text({
|
65 | label: _("code"),
|
66 | width: "120px"
|
67 | });
|
68 | var inpDesc = new Description({
|
69 | text: _("desc"), html: true
|
70 | });
|
71 | var elem = $.elem( this, 'div', [
|
72 | new Flex({
|
73 | justify: "between",
|
74 | content: [inpName, inpCode]
|
75 | }),
|
76 | inpDesc
|
77 | ]);
|
78 |
|
79 | DB.propRemoveClass( this, 'visible', 'hide' );
|
80 | DB.propWidget( this, "focus", inpName );
|
81 | DB.propWidget( this, "name", inpName, "value" );
|
82 | DB.propWidget( this, "code", inpCode, "value" );
|
83 | DB.propWidget( this, "desc", inpDesc, "value" );
|
84 |
|
85 | opts = DB.extend({
|
86 | visible: true,
|
87 | name: "",
|
88 | code: "",
|
89 | desc: ""
|
90 | }, opts, this);
|
91 | };
|
92 |
|
93 |
|
94 | module.exports = Event;
|
95 | `,
|
96 | "tfw.binding": `/** @module tfw.binding */require( 'tfw.binding', function(require, module, exports) { var _=function(){return ''}; "use strict";
|
97 |
|
98 | var Converters = require("tfw.binding.converters");
|
99 | var PropertyManager = require('tfw.binding.property-manager');
|
100 |
|
101 |
|
102 | exports.defProps = function( obj, props ) {
|
103 | var propertyName;
|
104 | for( propertyName in props ) {
|
105 | exports.defProp( obj, propertyName, props[propertyName] );
|
106 | }
|
107 | };
|
108 |
|
109 |
|
110 | /**
|
111 | * @param {function(v)} opts.cast
|
112 | */
|
113 | exports.defProp = function( obj, name, opts ) {
|
114 | if( typeof opts === 'undefined' ) opts = {};
|
115 | var pm = PropertyManager( obj );
|
116 | pm.converter( name, exports.createConverter( opts.cast ) );
|
117 |
|
118 | Object.defineProperty( obj, name, {
|
119 | set: pm.change.bind( pm, name ),
|
120 | get: pm.get.bind( pm, name ),
|
121 | configurable: false,
|
122 | enumerable: true
|
123 | });
|
124 | };
|
125 |
|
126 |
|
127 | exports.createConverter = function( arg ) {
|
128 | var type = typeof arg;
|
129 | switch( type ) {
|
130 | case 'function':
|
131 | return arg;
|
132 | case 'string':
|
133 | return Converters.get( arg );
|
134 | default:
|
135 | return undefined;
|
136 | }
|
137 | };
|
138 |
|
139 |
|
140 |
|
141 | module.exports._ = _;
|
142 | });
|
143 | `,
|
144 | "dom": `/** @module dom */require( 'dom', function(require, module, exports) { var _=function(){var D={"en":{}},X=require("$").intl;function _(){return X(D,arguments);}_.all=D;return _}();
|
145 | /**
|
146 | * @module dom
|
147 | *
|
148 | * @description
|
149 | * Functions which facilitate DOm manipulations.
|
150 | * Included __interact.js__. You can find documentation for it here:
|
151 | * [http://interactjs.io/docs/]
|
152 | *
|
153 | * @example
|
154 | * var mod = require('dom');
|
155 | */
|
156 | require("polyfill.classList");
|
157 | var PointerEvents = require("tfw.pointer-events");
|
158 |
|
159 | var $ = function(dom) {
|
160 | if (typeof dom === 'string') {
|
161 | var elem = document.getElementById( dom );
|
162 | if (!elem) {
|
163 | console.error( "[dom] There is no DOM element with this ID: \`" + dom + "\`" );
|
164 | }
|
165 | return elem;
|
166 | }
|
167 | if (!dom) {
|
168 | debugger;
|
169 | throw Error("\`dom\` is not a valid element!", dom);
|
170 | }
|
171 | if (typeof dom.element === 'function') return dom.element();
|
172 | if (dom.element) return dom.element;
|
173 | return dom;
|
174 | };
|
175 |
|
176 | module.exports = $;
|
177 |
|
178 |
|
179 | // Used to store data on the DOM element without colliding with existing attributes.
|
180 | var SYMBOL = '@dom' + Date.now();
|
181 |
|
182 | var RX_ENTITY = /^&(#[0-9]+|[a-zA-Z0-9]+);$/;
|
183 |
|
184 | $.tagNS = tagNS;
|
185 | $.svgRoot = tagNS.bind( undefined, "http://www.w3.org/2000/svg", "svg", {
|
186 | version: '1.1',
|
187 | 'xmlns:svg': 'http://www.w3.org/2000/svg',
|
188 | xmlns: 'http://www.w3.org/2000/svg',
|
189 | 'xmlns:xlink': 'http://www.w3.org/1999/xlink'
|
190 | });
|
191 | $.svg = tagNS.bind( undefined, "http://www.w3.org/2000/svg" );
|
192 | $.tag = tagNS.bind( undefined, "http://www.w3.org/1999/xhtml" );
|
193 | $.div = tagNS.bind( undefined, "http://www.w3.org/1999/xhtml", "div" );
|
194 | $.txt = window.document.createTextNode.bind( window.document );
|
195 | $.textOrHtml = textOrHtml;
|
196 | $.get = get;
|
197 | /**
|
198 | * Add a readonly \`element\` property to \`obj\` and return it.
|
199 | */
|
200 | $.elem = elem;
|
201 | /**
|
202 | * Apply css rules on \`element\`.
|
203 | *
|
204 | * @return \`element\`.
|
205 | *
|
206 | * @example
|
207 | * var $ = require('dom');
|
208 | * $.css( element, { width: '800px'. height: '600px' });
|
209 | */
|
210 | $.css = css;
|
211 | $.att = att;
|
212 | $.removeAtt = removeAtt;
|
213 | $.addClass = addClass;
|
214 | $.hasClass = hasClass;
|
215 | $.removeClass = removeClass;
|
216 | $.toggleClass = toggleClass;
|
217 | $.saveStyle = saveStyle;
|
218 | $.restoreStyle = restoreStyle;
|
219 | /**
|
220 | * @param newElem {Element} - Replacement element.
|
221 | * @param oldElem {Element} - Element to replace.
|
222 | */
|
223 | $.replace = replace;
|
224 | /**
|
225 | * Remove element from its parent.
|
226 | * @param element {Element} - Element to detach from its parent.
|
227 | * @return The parent element.
|
228 | */
|
229 | $.detach = detach;
|
230 | /**
|
231 | * Add event handlers to one or many elements.
|
232 | *
|
233 | * @param {object|array} element - list of elements on which apply
|
234 | * events handlers.
|
235 | *
|
236 | * @param {object|function} slots - If a function is given, it is
|
237 | * considered as a slot for the event \`tap\`. Otherwise, the object is
|
238 | * a map between events' names (the key) and function to handle the
|
239 | * event (the value).
|
240 | * Events' names are:
|
241 | * * __tap__: When the element is pressed and released in less than
|
242 | 900 ms and without too much sliding.
|
243 | * * __doubletap__
|
244 | * * __dragmove__
|
245 | *
|
246 | * @param {boolean} capture - If \`true\` events are captured before they reach the children.
|
247 | *
|
248 | * @example
|
249 | * DOM.on( [screen, button], function() {...} );
|
250 | * DOM.on( body, null ); // Do nothing, but stop propagation.
|
251 | * DOM.on( element, { tap: function() {...} } );
|
252 | */
|
253 | $.on = on;
|
254 | $.off = off;
|
255 | /**
|
256 | * Append all the \`children\` to \`element\`.
|
257 | * @param element
|
258 | * @param ...children
|
259 | */
|
260 | $.add = add;
|
261 | /**
|
262 | * Add the attribute \`element\` and the following functions to \`obj\`:
|
263 | * * __css__
|
264 | * * __addClass__
|
265 | * * __removeClass__
|
266 | * * __toggleClass__
|
267 | */
|
268 | $.wrap = wrap;
|
269 | /**
|
270 | * Remove all children of the \`element\`.
|
271 | * @param element {Element} - Element from which remove all the children.
|
272 | */
|
273 | $.clear = clear;
|
274 |
|
275 | function wrap( obj, element, nomethods ) {
|
276 | Object.defineProperty( obj, 'element', {
|
277 | value: element, writable: false, configurable: false, enumerable: true
|
278 | });
|
279 | if( nomethods ) return obj;
|
280 |
|
281 | obj.on = on.bind( obj, element );
|
282 | obj.css = css.bind( obj, element );
|
283 | obj.add = add.bind( obj, element );
|
284 | obj.att = att.bind( obj, element );
|
285 | obj.addClass = addClass.bind( obj, element );
|
286 | obj.hasClass = hasClass.bind( obj, element );
|
287 | obj.removeClass = removeClass.bind( obj, element );
|
288 | obj.toggleClass = toggleClass.bind( obj, element );
|
289 | return obj;
|
290 | }
|
291 |
|
292 | function replace( newElem, oldElem ) {
|
293 | newElem = $(newElem);
|
294 | oldElem = $(oldElem);
|
295 | oldElem.parentNode.replaceChild( newElem, oldElem );
|
296 | return newElem;
|
297 | }
|
298 |
|
299 | function css( element, styles ) {
|
300 | element = $(element);
|
301 | var key, val;
|
302 | for( key in styles ) {
|
303 | val = styles[key];
|
304 | element.style[key] = val;
|
305 | }
|
306 | return element;
|
307 | }
|
308 |
|
309 | function att( element, attribs, value ) {
|
310 | element = $(element);
|
311 | var key, val;
|
312 | if (typeof attribs === 'string') {
|
313 | key = attribs;
|
314 | attribs = {};
|
315 | attribs[key] = value;
|
316 | }
|
317 | for( key in attribs ) {
|
318 | val = attribs[key];
|
319 | element.setAttribute( key, val );
|
320 | }
|
321 | return element;
|
322 | }
|
323 |
|
324 | function removeAtt( element, attrib ) {
|
325 | element = $(element);
|
326 | element.removeAttribute( attrib );
|
327 | return element;
|
328 | }
|
329 |
|
330 | function add( element ) {
|
331 | element = $(element);
|
332 | try {
|
333 | var i, child;
|
334 | for (i = 1 ; i < arguments.length ; i++) {
|
335 | child = arguments[i];
|
336 | if( typeof child === 'string' || typeof child === 'number' ) {
|
337 | child = '' + child;
|
338 | if( child.substr( 0, 6 ) == '<html>' ) {
|
339 | var html = child.substr( 6 );
|
340 | child = $.tag('span');
|
341 | child.innerHTML = html;
|
342 | }
|
343 | else if( RX_ENTITY.test( child ) ) {
|
344 | var text = child;
|
345 | child = $.tag('span');
|
346 | child.innerHTML = text;
|
347 | }
|
348 | else {
|
349 | child = document.createTextNode( child );
|
350 | }
|
351 | }
|
352 | else if( typeof child.element === 'function' ) {
|
353 | // Backward compatibility with Widgets.
|
354 | child = child.element();
|
355 | }
|
356 | else if( typeof child.element !== 'undefined' ) {
|
357 | child = child.element;
|
358 | }
|
359 | element.appendChild( child );
|
360 | }
|
361 | return element;
|
362 | }
|
363 | catch( ex ) {
|
364 | console.error( "[DOM.add] arguments=", [].slice.call( arguments ) );
|
365 | throw Error( "[DOM.add] " + ex );
|
366 | }
|
367 | }
|
368 |
|
369 | function off( element ) {
|
370 | if( Array.isArray( element ) ) {
|
371 | element.forEach(function ( elem ) {
|
372 | off( elem );
|
373 | });
|
374 | return element;
|
375 | }
|
376 |
|
377 | if( typeof element[SYMBOL] === 'undefined' ) return element;
|
378 | var pe = element[SYMBOL].events;
|
379 | if( typeof pe === 'undefined' ) return element;
|
380 | pe.off();
|
381 | delete element[SYMBOL].events;
|
382 | }
|
383 |
|
384 | function on( element, slots ) {
|
385 | // If only a function is passed, we consider this is a Tap event.
|
386 | if( typeof slots === 'function' || slots === null ) slots = { tap: slots };
|
387 |
|
388 | if( Array.isArray( element ) ) {
|
389 | element.forEach(function ( elem ) {
|
390 | on( elem, slots );
|
391 | });
|
392 | return element;
|
393 | }
|
394 |
|
395 | element = $(element);
|
396 | if( typeof element[SYMBOL] === 'undefined' ) element[SYMBOL] = {};
|
397 | if( typeof element[SYMBOL].events === 'undefined' ) {
|
398 | element[SYMBOL].events = new PointerEvents( element );
|
399 | }
|
400 |
|
401 | var key, val, preview;
|
402 | for( key in slots ) {
|
403 | val = slots[key];
|
404 | if (key.charAt(0) == '!') {
|
405 | key = key.substr(1);
|
406 | preview = true;
|
407 | } else {
|
408 | preview = false;
|
409 | }
|
410 | if (key == 'keydown' || key == 'keyup') {
|
411 | element.addEventListener( key, val, preview );
|
412 | } else {
|
413 | element[SYMBOL].events.on( key, val, preview );
|
414 | }
|
415 | }
|
416 |
|
417 | return element;
|
418 | }
|
419 |
|
420 | function tagNS( ns, name ) {
|
421 | try {
|
422 | var e = document.createElementNS( ns, name );
|
423 | var i, arg, key, val;
|
424 | for (i = 2 ; i < arguments.length ; i++) {
|
425 | arg = arguments[i];
|
426 | if( Array.isArray(arg) ) {
|
427 | // Arrays are for children.
|
428 | arg.forEach(function (child) {
|
429 | switch( typeof child ) {
|
430 | case 'string':
|
431 | case 'number':
|
432 | case 'boolean':
|
433 | child = '' + child;
|
434 | if( child.substr( 0, 6 ) == '<html>' ) {
|
435 | var html = child.substr( 6 );
|
436 | child = $.tag('span');
|
437 | child.innerHTML = html;
|
438 | } else {
|
439 | child = document.createTextNode( child );
|
440 | }
|
441 | break;
|
442 | }
|
443 | add( e, child );
|
444 | });
|
445 | } else {
|
446 | switch( typeof arg ) {
|
447 | case "string":
|
448 | arg.split( ' ' ).forEach(function ( item ) {
|
449 | if( item.length > 0 ) {
|
450 | addClass(e, item);
|
451 | }
|
452 | });
|
453 | break;
|
454 | case "object":
|
455 | for( key in arg ) {
|
456 | val = arg[key];
|
457 | e.setAttribute( key, val );
|
458 | }
|
459 | break;
|
460 | default:
|
461 | throw Error("[dom.tag] Error creating <" + name + ">: Invalid argument #" + i + "!");
|
462 | }
|
463 | }
|
464 | }
|
465 | return e;
|
466 | }
|
467 | catch (ex) {
|
468 | console.error("[dom.tagNS] Error with \`ns\` = ", ns, " and \`name\` = ", name);
|
469 | console.error(ex);
|
470 | }
|
471 | };
|
472 |
|
473 |
|
474 | function addClass(elem) {
|
475 | var args = [].slice.call( arguments, 1 );
|
476 | if( Array.isArray( elem ) ) {
|
477 | // Loop on each element.
|
478 | args.unshift( null );
|
479 | elem.forEach(function ( child ) {
|
480 | args[0] = child;
|
481 | addClass.apply( undefined, args );
|
482 | });
|
483 | return elem;
|
484 | }
|
485 | elem = $( elem );
|
486 | args.forEach(function (className) {
|
487 | if (typeof className !== 'string') return;
|
488 | className = className.trim();
|
489 | if( className.length == 0 ) return;
|
490 | try {
|
491 | if( elem.classList )
|
492 | elem.classList.add( className );
|
493 | }
|
494 | catch( ex ) {
|
495 | console.error( "[dom.addClass] Invalid class name: ", className );
|
496 | console.error( ex );
|
497 | }
|
498 | });
|
499 | return elem;
|
500 | }
|
501 |
|
502 |
|
503 | function hasClass( elem, className ) {
|
504 | elem = $( elem );
|
505 | if( !elem.classList ) return false;
|
506 | return elem.classList.contains( className );
|
507 | }
|
508 |
|
509 |
|
510 | function removeClass(elem) {
|
511 | var args = [].slice.call( arguments, 1 );
|
512 | if( Array.isArray( elem ) ) {
|
513 | // Loop on each element.
|
514 | args.unshift( null );
|
515 | elem.forEach(function ( child ) {
|
516 | args[0] = child;
|
517 | removeClass.apply( undefined, args );
|
518 | });
|
519 | return elem;
|
520 | }
|
521 | elem = $( elem );
|
522 | args.forEach(function (className) {
|
523 | if (typeof className !== 'string') return;
|
524 | try {
|
525 | if( elem.classList )
|
526 | elem.classList.remove( className );
|
527 | }
|
528 | catch( ex ) {
|
529 | console.error( "[dom.removeClass] Invalid class name: ", className );
|
530 | console.error( ex );
|
531 | }
|
532 | });
|
533 | return elem;
|
534 | }
|
535 |
|
536 |
|
537 | function toggleClass(elem) {
|
538 | var args = [].slice.call( arguments, 1 );
|
539 | args.forEach(function( className ) {
|
540 | if( hasClass( elem, className ) ) {
|
541 | removeClass( elem, className );
|
542 | } else {
|
543 | addClass( elem, className );
|
544 | }
|
545 | });
|
546 | return elem;
|
547 | }
|
548 |
|
549 |
|
550 | function clear( element ) {
|
551 | // (!) On préfère retirer les éléments un par un du DOM plutôt que d'utiliser simplement
|
552 | // this.html("").
|
553 | // En effet, le code simplifié a des conséquences inattendues dans IE9 et IE10 au moins.
|
554 | // Le bug des markers qui disparaissaients sur les cartes de Trail-Passion 4 a été corrigé
|
555 | // avec cette modification.
|
556 | element = $(element);
|
557 | var e = element;
|
558 | while(e.firstChild){
|
559 | e.removeChild(e.firstChild);
|
560 | }
|
561 | var args = [].slice.call( arguments );
|
562 | if( args.length > 1 ) {
|
563 | add.apply( this, args );
|
564 | }
|
565 | return element;
|
566 | }
|
567 |
|
568 | function get( element, query ) {
|
569 | element = $(element);
|
570 | if( typeof query === 'undefined' ) {
|
571 | query = element;
|
572 | element = window.document;
|
573 | }
|
574 | return element.querySelector( query );
|
575 | }
|
576 |
|
577 | function detach( element ) {
|
578 | element = $(element);
|
579 | var parent = element.parentElement;
|
580 | if( !parent ) return parent;
|
581 | parent.removeChild( element );
|
582 | return parent;
|
583 | }
|
584 |
|
585 | function elem( target ) {
|
586 | var args = [].slice.call( arguments );
|
587 | args.shift();
|
588 | if (args.length == 0) args = ['div'];
|
589 | args.push('dom', 'custom');
|
590 | var e;
|
591 | if (typeof args[0].element !== 'undefined') {
|
592 | e = args[0].element;
|
593 | addClass( e, 'dom', 'custom' );
|
594 | } else if (typeof args[0].appendChild === 'function') {
|
595 | e = args[0];
|
596 | addClass( e, 'dom', 'custom' );
|
597 | } else {
|
598 | e = $.tag.apply( $, args );
|
599 | }
|
600 | Object.defineProperty( target, 'element', {
|
601 | value: e, writable: false, configurable: false, enumerable: true
|
602 | });
|
603 | return e;
|
604 | }
|
605 |
|
606 | function textOrHtml( element, content ) {
|
607 | if( typeof content === 'undefined' ) content = '';
|
608 | if (content === null) content = '';
|
609 | if (typeof content !== 'string') content = JSON.stringify( content );
|
610 | if (content.substr(0, 6) == '<html>') {
|
611 | element.innerHTML = content.substr(6);
|
612 | } else {
|
613 | element.textContent = content;
|
614 | }
|
615 | return element;
|
616 | }
|
617 |
|
618 | function saveStyle( elements ) {
|
619 | if( !Array.isArray( elements ) ) return saveStyle( Array.prototype.slice.call( arguments ) );
|
620 | elements.forEach(function (elem) {
|
621 | elem = $( elem );
|
622 | if( typeof elem[SYMBOL] === 'undefined' ) elem[SYMBOL] = {};
|
623 | if( !Array.isArray( elem[SYMBOL].style ) ) elem[SYMBOL].style = [];
|
624 | elem[SYMBOL].style.push( JSON.stringify( elem.style ) );
|
625 | });
|
626 | }
|
627 |
|
628 | function restoreStyle( elements ) {
|
629 | if( !Array.isArray( elements ) ) return restoreStyle( Array.prototype.slice.call( arguments ) );
|
630 | elements.forEach(function (elem) {
|
631 | elem = $( elem );
|
632 | if( typeof elem[SYMBOL] === 'undefined' || !Array.isArray( elem[SYMBOL].style ) ) throw Error(
|
633 | "[dom.restoreStyle] \`saveStyle()\` has never been used on this element!");
|
634 | if( elem[SYMBOL].style.length == 0 ) throw Error(
|
635 | "[dom.restoreStyle] more \`restore\` than \`save\`!");
|
636 | var styles = JSON.parse( elem[SYMBOL].style.pop() );
|
637 | var k, v;
|
638 | for( k in styles ) {
|
639 | v = styles[k];
|
640 | if( typeof v !== 'undefined' ) {
|
641 | elem.style[k] = v;
|
642 | }
|
643 | }
|
644 | });
|
645 | }
|
646 |
|
647 |
|
648 |
|
649 | module.exports._ = _;
|
650 | /**
|
651 | * @module dom
|
652 | * @see module:$
|
653 | * @see module:tfw.pointer-events
|
654 |
|
655 | */
|
656 | });`,
|
657 | "x-widget": `/**
|
658 | * @example
|
659 | *
|
660 | * var W = require("x-widget");
|
661 | * W({
|
662 | * elem: "div",
|
663 | * attr: {"class": "black"},
|
664 | * prop: {"$key": "menu"},
|
665 | * children: [
|
666 | * "This is the ",
|
667 | * W({
|
668 | * elem: "b",
|
669 | * children: ["menu"]
|
670 | * }),
|
671 | * "..."
|
672 | * ]
|
673 | * });
|
674 | */
|
675 | "use strict";
|
676 |
|
677 | var $ = require("dom");
|
678 | var DB = require("tfw.data-binding");
|
679 |
|
680 | var widgets = {};
|
681 | // Used for \`onWidgetCreation()\`.
|
682 | var slots = {};
|
683 |
|
684 |
|
685 | var Widget = function(id, modName, args, attribs) {
|
686 | if (typeof id === 'string') return Widget1.call( this, id, modName, args, attribs );
|
687 | else return Widget2.call( this, id );
|
688 | };
|
689 |
|
690 | function Widget1(id, modName, args, attribs ) {
|
691 | if( typeof attribs === 'undefined' ) attribs = {};
|
692 |
|
693 | try {
|
694 | var module = require( modName );
|
695 | var wdg = new module( args );
|
696 | var elem = typeof wdg.element === 'function' ? wdg.element() : wdg.element;
|
697 | var dst = document.getElementById( id );
|
698 | if (dst) {
|
699 | // This widget does exist in the current DOM.
|
700 | // We have to replace it.
|
701 | dst.parentNode.replaceChild( elem, dst );
|
702 | }
|
703 | elem.setAttribute( 'id', id );
|
704 | // Add classes defined in the containing element (\`dst\`).
|
705 | $.addClass( elem, attribs.class || "" );
|
706 | register( id, wdg );
|
707 | return wdg;
|
708 | }
|
709 | catch (ex) {
|
710 | console.error("[x-widget] Unable to create widget \`" + modName + "\`!");
|
711 | console.error("[x-widget] id = ", id, ", args = ", args);
|
712 | throw Error(ex);
|
713 | }
|
714 | };
|
715 |
|
716 | function Widget2(args) {
|
717 | var id;
|
718 | var elem = $.tag( args.elem );
|
719 | if (args.attr) {
|
720 | // Adding DOM element attributes.
|
721 | $.att( elem, args.attr );
|
722 | id = args.attr.id;
|
723 | }
|
724 |
|
725 | if (Array.isArray( args.children )) {
|
726 | // Adding DOM element children.
|
727 | args.children.forEach(function (child) {
|
728 | $.add( elem, child );
|
729 | });
|
730 | }
|
731 | // Converting into a widget.
|
732 | var key, val;
|
733 | var wdg = {};
|
734 |
|
735 | if (args.prop) {
|
736 | // Adding READ-ONLY properties to the widget.
|
737 | for( key in args.prop ) {
|
738 | val = args.prop[key];
|
739 | Object.defineProperty( wdg, key, {
|
740 | value: val, writable: false, configurable: false, enumerable: true
|
741 | });
|
742 | }
|
743 | }
|
744 | // Assigning the element to the widget.
|
745 | Object.defineProperty( wdg, 'element', {
|
746 | value: elem, writable: false, configurable: false, enumerable: true
|
747 | });
|
748 |
|
749 | if( typeof id !== 'undefined' ) {
|
750 | // Registering the widget only if it as got an id.
|
751 | register( id, wdg );
|
752 | }
|
753 | return wdg;
|
754 | }
|
755 |
|
756 | Widget.template = function( attribs ) {
|
757 | var key, val, id, name = '', args = {};
|
758 | for( key in attribs ) {
|
759 | val = attribs[key];
|
760 | if( key == 'name' ) {
|
761 | name = val;
|
762 | }
|
763 | else if( key == 'id' ) {
|
764 | id = val;
|
765 | }
|
766 | else if( key.charAt(0)=='$' ) {
|
767 | args[key.substr( 1 )] = val;
|
768 | }
|
769 | }
|
770 | var module = require( name );
|
771 | var wdg = new module( args );
|
772 | if( id ) {
|
773 | register( id, wdg );
|
774 | }
|
775 |
|
776 | return typeof wdg.element === 'function' ? wdg.element() : (wdg.element || wdg);
|
777 | };
|
778 |
|
779 | function register( id, wdg ) {
|
780 | widgets[id] = wdg;
|
781 | var mySlots = slots[id];
|
782 | if( typeof mySlots !== 'undefined' ) {
|
783 | window.setTimeout(function() {
|
784 | mySlots.forEach(function (slot) {
|
785 | slot( wdg );
|
786 | });
|
787 | delete slots[id];
|
788 | });
|
789 | }
|
790 | return typeof wdg.element === 'function' ? wdg.element : (wdg.element || wdg);
|
791 | };
|
792 |
|
793 | Widget.getById = function( id ) {
|
794 | if( !widgets[id] ) throw Error( "[x-widget.getById()] ID not found: " + id + "!" );
|
795 | return widgets[id];
|
796 | };
|
797 |
|
798 | Widget.onWidgetCreation = function( id, slot ) {
|
799 | if( typeof widgets[id] === 'undefined' ) {
|
800 | if( typeof slots[id] === 'undefined' ) slots[id] = [slot];
|
801 | else slots[id].push( slot );
|
802 | } else {
|
803 | // Asynchronous call to the slot
|
804 | window.setTimeout(
|
805 | function() {
|
806 | slot( widgets[id] );
|
807 | }
|
808 | );
|
809 | }
|
810 | };
|
811 |
|
812 | /**
|
813 | * @example
|
814 | * var W = require("x-widget");
|
815 | * W.bind('wdg.layout-stack0',{"value":{"B":[["btnNewTask","action"],["btnCancel","action"]]}});
|
816 | */
|
817 | Widget.bind = function( id, attribs ) {
|
818 | // Destination object: the one on the attributes of which we want to bind.
|
819 | var dstObj = widgets[id];
|
820 | // Destination attribute name.
|
821 | var dstAtt;
|
822 | // Temporary variables to hold source object and attributes.
|
823 | var srcObj, srcAtt;
|
824 | // @example
|
825 | // ["btnNewTask","action","btnCancel","action"]
|
826 | var bindings;
|
827 | var slots;
|
828 | // Index used to parse multiple bindings.
|
829 | var idx;
|
830 | for( dstAtt in attribs ) {
|
831 | bindings = attribs[dstAtt].B;
|
832 | if (Array.isArray( bindings )) {
|
833 | // \`binding\` is an array of arrays.
|
834 | // Subarrays have 2 or 3 elements.
|
835 | // * ID if the source object
|
836 | // * attribute to bind on
|
837 | // * [optional] value to use as a constant. This is the
|
838 | // * case where we just want to set a constant value as
|
839 | // * soon as the source's attribute has changed.
|
840 | bindings.forEach(function (binding) {
|
841 | srcObj = widgets[binding[0]];
|
842 | if( typeof srcObj === 'undefined' ) {
|
843 | console.error( "[x-widget:bind(" + id + ")] Trying to bind attribute \""
|
844 | + dstAtt
|
845 | + "\" of widget \"" + id + "\" to the unexisting widget \""
|
846 | + binding[0] + "\"!");
|
847 | return;
|
848 | }
|
849 | srcAtt = binding[1];
|
850 | try {
|
851 | if (binding.length == 2) {
|
852 | DB.bind( srcObj, srcAtt, dstObj, dstAtt );
|
853 | } else {
|
854 | var value = binding[2];
|
855 | DB.bind( srcObj, srcAtt, function() {
|
856 | dstObj[dstAtt] = value;
|
857 | });
|
858 | }
|
859 | } catch( ex ) {
|
860 | console.error("Binding error for widget \`" + id + "\`!", {
|
861 | ex: ex,
|
862 | binding: binding
|
863 | });
|
864 | }
|
865 | });
|
866 | }
|
867 |
|
868 | slots = attribs[dstAtt].S;
|
869 | if (Array.isArray( slots )) {
|
870 | // Each item is the name of a function to call when the value of this attribute changes.
|
871 | // If the item is a \`string\`, the function is from the global \`APP\` object.
|
872 | // Otherwise, the item must be an array with two children:
|
873 | // the first one is the module's name and the second one
|
874 | // id the name of the function.
|
875 | // The slots are called with two arguments:
|
876 | // * the value and
|
877 | // * the object the attribute belongs.
|
878 | slots.forEach(function (slot) {
|
879 | var mod = APP;
|
880 | var fct = slot;
|
881 | if (Array.isArray( slot )) {
|
882 | try {
|
883 | mod = require(slot[0]);
|
884 | } catch( ex ) {
|
885 | console.error("[x-widget:bind] Widget \`" + id + "\` can't require unexistent \`"
|
886 | + slot[0] + "\`: ", ex);
|
887 | throw( ex );
|
888 | }
|
889 | fct = slot[1];
|
890 | }
|
891 | fct = mod[fct];
|
892 | if (typeof fct !== 'function') {
|
893 | if( Array.isArray(slot) ) {
|
894 | throw Error("[x-widget:bind] Widget \`" + id + "\` use unexisting slot \`"
|
895 | + slot[1] + "\` of module \`" + slot[0] + "\`!");
|
896 | } else {
|
897 | throw Error("[x-widget:bind] Widget \`" + id + "\` use unexisting slot \`"
|
898 | + slot + "\` of main module \`APP\`!");
|
899 | }
|
900 | } else {
|
901 | try {
|
902 | DB.bind( dstObj, dstAtt, fct );
|
903 | } catch( ex ) {
|
904 | console.error("Binding error for widget \`" + id + "\`!", {
|
905 | ex: ex,
|
906 | dstObj: dstObj,
|
907 | dstAtt: dstAtt,
|
908 | fct: fct,
|
909 | slot: slot
|
910 | });
|
911 | }
|
912 | }
|
913 | });
|
914 |
|
915 | }
|
916 | }
|
917 | };
|
918 |
|
919 | module.exports = Widget;
|
920 | `};
|