mushroom-cli 1.2.0-alpha.12 → 1.2.0-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,1025 +1,1268 @@
1
- "use strict";
2
- (function () {
3
- function MrgiaTemplate() {
4
- var _debugMode = false;
5
- this.debugMode = function (value) {
6
- if (value === undefined)
7
- return _debugMode;
8
- _debugMode = !!value;
9
- };
10
- var _domBinders = {};
11
- this.registerDomFeature = function (name, fnHandler) {
12
- if (typeof name !== "string")
13
- throw new TypeError("Dom binder name must be a string");
14
- if (typeof fnHandler !== "function")
15
- throw new TypeError("fnHandler must be a function");
16
- _domBinders[name] = fnHandler;
17
- };
18
- this.bindDom = function (dom, data, fnPool, session) {
19
- if (!dom)
20
- return;
21
- if (dom.nodeType !== Node.ELEMENT_NODE)
22
- return;
23
- session = session || {};
24
- if (!session.mt)
25
- session.mt = this;
26
- if (typeof session.scope != "string")
27
- session.scope = "";
28
- if (typeof session.specialData != "object" && !session.specialData)
29
- session.specialData = {};
30
- if (dom.dataset) {
31
- var mediator = {};
32
- var skipInner = undefined;
33
- for (var name in _domBinders) {
34
- var config = dom.dataset[name];
35
- if (!config)
36
- continue;
37
- if (typeof config === "string") {
38
- try {
39
- config = JSON.parse(config);
40
- }
41
- catch (e) {
42
- dom.dataset[name + "Error"] = "Parse error";
43
- skipInner = true;
44
- continue;
45
- }
46
- }
47
- if (typeof config !== "object") {
48
- dom.dataset[name + "Error"] = "Invalid configuration";
49
- skipInner = true;
50
- continue;
51
- }
52
- if (!dom.__mt_features)
53
- dom.__mt_features = {};
54
- dom.__mt_features[name] = dom.dataset[name];
55
- _domBinders[name]({
56
- name: name,
57
- dom: dom,
58
- data: data,
59
- fnPool: fnPool,
60
- config: config,
61
- mediator: mediator,
62
- session: session
63
- });
64
- if (mediator.stop)
65
- return;
66
- if (mediator.skipInner)
67
- skipInner = true;
68
- mediator.skipInner = skipInner;
69
- }
70
- if (skipInner)
71
- return;
72
- }
73
- for (var i = 0; i < dom.childNodes.length; i++) {
74
- this.bindDom(dom.childNodes[i], data, fnPool, session);
75
- }
76
- };
77
- this.rebindDom = function (dom, data, fnPool, session) {
78
- if (dom.__mt_features) {
79
- for (var name in dom.__mt_features) {
80
- dom.dataset[name] = dom.__mt_features[name];
81
- }
82
- }
83
- this.bindDom(dom, data, fnPool, session);
84
- };
85
- var _languages = [];
86
- var _currentLanguage = null;
87
- this.addLanguage = function (language) {
88
- if (!language || !language.code || !language.data || typeof language.data !== "object")
89
- return;
90
- if (_languages.some(function (lang) { return lang.code === language.code; }))
91
- return;
92
- var lang = { code: language.code, data: language.data };
93
- _languages.push(lang);
94
- if (_languages.length === 1)
95
- _currentLanguage = lang;
96
- };
97
- this.setCurrentLanguage = function (code) {
98
- return _languages.some(function (lang) {
99
- if (lang.code === code) {
100
- _currentLanguage = lang;
101
- return true;
102
- }
103
- return false;
104
- });
105
- };
106
- this.getCurrentLanguage = function () {
107
- return _currentLanguage;
108
- };
109
- }
110
- window.mt = new MrgiaTemplate();
111
- window.MrgiaTemplate = MrgiaTemplate;
112
- })();
113
- (function () {
114
- var _dataQueryCharacter = "$";
115
- var _specialDataQueryCharacter = "~";
116
- var _functionQueryCharacter = "@";
117
- var _conditionOperatorCharacter = "$";
118
- var _languageQueryCharacter = "#";
119
- function encodeHtml(data) {
120
- if (data === undefined || data === null)
121
- return "";
122
- var html = typeof (data) === "string" ? data : data.toString();
123
- html = html.replace(/&/g, "&amp;");
124
- html = html.replace(/</g, "&lt;");
125
- html = html.replace(/>/g, "&gt;");
126
- html = html.replace(/"/g, "&quot;");
127
- html = html.replace(/'/g, "&apos;");
128
- return html;
129
- }
130
- function fieldComparer(fieldPath, session) {
131
- return function (a, b) {
132
- var x = findValueForDomStyle({ field: fieldPath, data: a, session: session });
133
- var y = findValueForDomStyle({ field: fieldPath, data: b, session: session });
134
- return x > y ? 1 : x < y ? -1 : 0;
135
- };
136
- }
137
- function multiFieldComparer(fields, session) {
138
- return function (a, b) {
139
- for (var m in fields) {
140
- if (!m || m.charAt(0) !== "$" || fields[m] !== 1 && fields[m] !== -1)
141
- continue;
142
- var x = findValueForDomStyle({ field: m, data: a, session: session });
143
- var y = findValueForDomStyle({ field: m, data: b, session: session });
144
- if (x > y)
145
- return fields[m];
146
- else if (x < y)
147
- return -fields[m];
148
- }
149
- return 0;
150
- };
151
- }
152
- function checkCondition(x1, op, x2, data, fnPool, session, isRoot) {
153
- if (typeof x2 === "function")
154
- return x2(x1);
155
- switch (op) {
156
- case "eq":
157
- return x1 == x2;
158
- case "gt":
159
- return x1 > x2;
160
- case "gte":
161
- return x1 >= x2;
162
- case "lt":
163
- return x1 < x2;
164
- case "lte":
165
- return x1 <= x2;
166
- case "ne":
167
- return x1 != x2;
168
- case "or":
169
- {
170
- if (!Array.isArray(x2))
171
- return false;
172
- return x2.some(function (item) {
173
- if (item !== null && typeof item === "object")
174
- return checkConditions(item, x1, fnPool, session, isRoot);
175
- return x1 == item;
176
- });
177
- }
178
- case "and":
179
- {
180
- if (!Array.isArray(x2))
181
- return false;
182
- return x2.every(function (item) {
183
- if (item !== null && typeof item === "object")
184
- return checkConditions(item, x1, fnPool, session, isRoot);
185
- return x1 == item;
186
- });
187
- }
188
- case "not":
189
- return !checkConditions(x2, x1, fnPool, session, isRoot);
190
- case "exists":
191
- return x2 ? x1 !== undefined : x1 === undefined;
192
- case "type":
193
- {
194
- switch (x2) {
195
- case "number":
196
- case "string":
197
- case "boolean":
198
- case "function":
199
- case "undefined":
200
- return typeof x1 === x2;
201
- case "null":
202
- return x1 === null;
203
- case "object":
204
- return typeof x1 === x2 && x1 !== null && !Array.isArray(x1);
205
- case "array":
206
- return Array.isArray(x1);
207
- case "regex":
208
- return x1 instanceof RegExp;
209
- case "date":
210
- return x1 instanceof Date;
211
- case "html-element":
212
- return x1 instanceof HTMLElement;
213
- }
214
- }
215
- case "in":
216
- {
217
- if (!Array.isArray(x2))
218
- return false;
219
- return x2.indexOf(x1) !== -1;
220
- }
221
- case "nin":
222
- {
223
- if (!Array.isArray(x2))
224
- return false;
225
- return x2.indexOf(x1) === -1;
226
- }
227
- case "size":
228
- {
229
- if (x1 === undefined || x1 === null || Array.isArray(x1))
230
- return x1 ? x1.length == x2 : x2 == 0;
231
- return false;
232
- }
233
- case "all":
234
- {
235
- if (!Array.isArray(x1) || !Array.isArray(x2))
236
- return false;
237
- return x2.every(function (item) {
238
- return x1.indexOf(item) !== -1;
239
- });
240
- }
241
- case "mod":
242
- {
243
- if (typeof x1 !== "number" || !Array.isArray(x2) || x2.length !== 2)
244
- return false;
245
- return x1 % x2[0] === x2[1];
246
- }
247
- case "regex":
248
- {
249
- if (typeof x1 !== "string")
250
- return false;
251
- var re;
252
- if (typeof x2 === "string") {
253
- re = new RegExp(x1);
254
- }
255
- else if (Array.isArray(x2)) {
256
- if (x2.length)
257
- re = new RegExp(x2[0], x2.length > 1 ? x2[1] : undefined);
258
- }
259
- else if (x2 instanceof RegExp) {
260
- re = x2;
261
- }
262
- return re && re.test(x1);
263
- }
264
- }
265
- return false;
266
- }
267
- function checkConditions(cond, data, fnPool, session, isRoot) {
268
- for (var field in cond) {
269
- if (field && field.charAt(0) === _conditionOperatorCharacter) {
270
- if (!checkCondition(data, field.substr(1), cond[field], data, fnPool, session, isRoot))
271
- return false;
272
- }
273
- else {
274
- var x1 = isRoot && field && field.charAt(0) === _specialDataQueryCharacter
275
- ? findSpecialCharacter(field.substr(1), session)
276
- : findValueByPath(data, field);
277
- var value = cond[field];
278
- if (typeof value === "string") {
279
- var fn = value && value.charAt(0) === _functionQueryCharacter ? findFunctionForDomStyle({ field: value, fnPool: fnPool, session: session }) : null;
280
- if (fn && !fn(x1))
281
- return false;
282
- value = findValueForDomStyle({ field: value, data: data, fnPool: fnPool, session: session });
283
- if (!checkCondition(x1, "eq", value, data, fnPool, session))
284
- return false;
285
- }
286
- else if (value !== null && typeof value === "object") {
287
- if (!checkConditions(value, x1, fnPool, session))
288
- return false;
289
- }
290
- else {
291
- if (x1 !== value)
292
- return false;
293
- }
294
- }
295
- }
296
- return true;
297
- }
298
- function addEventListener(dom, eventName, handler, useCapture) {
299
- if (typeof handler !== "function")
300
- return;
301
- if (typeof dom.addEventListener == "function")
302
- dom.addEventListener(eventName, handler, useCapture);
303
- else if (typeof dom.attachEvent == "function")
304
- dom.attachEvent("on" + eventName, handler);
305
- }
306
- function callFunction(dom, data, fnPool, config, session) {
307
- var fn = findFunctionForDomStyle({ field: config.fn, fnPool: fnPool, session: session });
308
- if (!fn)
309
- return;
310
- var me = dom;
311
- var thisField = config["this"];
312
- if (thisField) {
313
- if (thisField.charAt(0) === _dataQueryCharacter)
314
- me = findDataForDomStyle({ field: thisField, data: data, session: session });
315
- else
316
- me = findDataForDomStyle({ field: _dataQueryCharacter + thisField, data: window, session: session });
317
- }
318
- var parametes = Array.isArray(config.data) ? config.data.map(function (item) { return findDataForDomStyle({ field: item, data: data, session: session }); }) : [findDataForDomStyle({ field: config.data, data: data, session: session })];
319
- fn.apply(me, parametes);
320
- }
321
- function findValueByPath(obj, path) {
322
- if (obj === undefined || typeof (path) !== "string")
323
- return undefined;
324
- if (obj === null)
325
- return undefined;
326
- var seg = path.split(".");
327
- var r = obj;
328
- var index = 0;
329
- if (seg.length > 0 && seg[0] === "this") {
330
- index = 1;
331
- }
332
- for (var i = index; i < seg.length; i++) {
333
- if (r === null)
334
- return undefined;
335
- if (typeof r === "object" || typeof r === "string" || typeof r === "function")
336
- r = r[seg[i]];
337
- else
338
- return undefined;
339
- }
340
- return r;
341
- }
342
- function findFunctionForDomStyle(args) {
343
- var field = args.field;
344
- if (typeof field === "function") {
345
- return field;
346
- }
347
- else if (typeof field === "string") {
348
- var fnPool = args.fnPool;
349
- var fn = field === "" || field.charAt(0) !== _functionQueryCharacter
350
- ? findValueByPath(window, field)
351
- : findValueByPath(fnPool, field.substr(1));
352
- return typeof fn === "function" ? fn : undefined;
353
- }
354
- return undefined;
355
- }
356
- function findDataForDomStyle(args) {
357
- var field = args.field;
358
- if (typeof field !== "string" || field === "")
359
- return field;
360
- var data = args.data;
361
- if (field.charAt(0) === _dataQueryCharacter) {
362
- if (field.length > 1 && field.charAt(1) === _specialDataQueryCharacter) {
363
- return findSpecialCharacter(field.substr(2), args.session);
364
- }
365
- return findValueByPath(data, field.substr(1));
366
- }
367
- else if (field.charAt(0) === _languageQueryCharacter) {
368
- if (field.length === 1)
369
- return field;
370
- if (field.charAt(1) === _languageQueryCharacter)
371
- return field.substr(1);
372
- var currentMt = session ? session.mt ? session.mt : mt : mt;
373
- var lang = currentMt.getCurrentLanguage();
374
- if (!lang)
375
- return "";
376
- return lang.data[field.substr(1)];
377
- }
378
- return field;
379
- }
380
- function mergeData(args) {
381
- var mask = args.mask;
382
- if (typeof mask !== "string" && typeof mask !== "object" || mask === null)
383
- return mask;
384
- var data = args.data;
385
- var session = args.session;
386
- if (typeof mask === "string") {
387
- return findDataForDomStyle({ field: mask, data: data, session: session });
388
- }
389
- if (Array.isArray(mask)) {
390
- return mask.map(function (item) {
391
- return mergeData({ mask: item, data: data, session: session });
392
- });
393
- }
394
- var obj = {};
395
- for (var m in mask) {
396
- obj[m] = mergeData({ mask: mask[m], data: data, session: session });
397
- }
398
- return obj;
399
- }
400
- function findSpecialCharacter(path, session) {
401
- return findValueByPath(session.specialData, path);
402
- }
403
- function findValueForDomStyle(args) {
404
- var field = args.field;
405
- var data = args.data;
406
- var fnPool = args.fnPool;
407
- var preferFunction = args.preferFunction;
408
- var session = args.session;
409
- var fn;
410
- if (typeof field === "string") {
411
- if (field === "")
412
- return "";
413
- switch (field.charAt(0)) {
414
- case _dataQueryCharacter:
415
- if (field.length > 1 && field.charAt(1) === _specialDataQueryCharacter) {
416
- return findSpecialCharacter(field.substr(2), session);
417
- }
418
- return findValueByPath(data, field.substr(1));
419
- case _functionQueryCharacter:
420
- fn = findValueByPath(fnPool, field.substr(1));
421
- return typeof fn === "function" ? fn : undefined;
422
- case _languageQueryCharacter:
423
- var currentMt = session ? session.mt ? session.mt : mt : mt;
424
- var lang = currentMt.getCurrentLanguage();
425
- if (!lang)
426
- return "";
427
- return lang.data[field.substr(1)];
428
- default:
429
- if (!preferFunction)
430
- return field;
431
- var gfn = findValueByPath(fnPool, field.substr(1));
432
- return typeof gfn === "function" ? gfn : undefined;
433
- }
434
- }
435
- else if (typeof field === "object") {
436
- if (field === null)
437
- return null;
438
- if ("fn" in field && field.fn) {
439
- var value = undefined;
440
- if (Array.isArray(field.fn)) {
441
- if (Array.isArray(field.data)) {
442
- for (var i = 0; i < field.fn.length; i++) {
443
- fn = findFunctionForDomStyle({ field: field.fn[i], fnPool: fnPool, session: session });
444
- if (!fn)
445
- break;
446
- var oldValue = session.specialData.pipeline;
447
- session.specialData.pipeline = { result: value };
448
- var p = field.data[i] === undefined || field.data[i] === null ? value : mergeData({ mask: field.data[i], data: data, session: session });
449
- session.specialData.pipeline = oldValue;
450
- if (Array.isArray(field.data[i]))
451
- value = fn.apply(null, p);
452
- else
453
- value = fn(p);
454
- }
455
- }
456
- else {
457
- value = findDataForDomStyle({ field: field.data, data: data, session: session });
458
- field.fn.forEach(function (fnName) {
459
- fn = findFunctionForDomStyle({ field: fnName, fnPool: fnPool, session: session });
460
- if (!fn)
461
- return;
462
- value = fn(value);
463
- });
464
- }
465
- }
466
- else if (typeof field.fn === "string") {
467
- fn = findFunctionForDomStyle({ field: field.fn, fnPool: fnPool, session: session });
468
- if (fn) {
469
- value = mergeData({ mask: field.data, data: data, session: session });
470
- if (Array.isArray(field.data))
471
- value = fn.apply(null, value);
472
- else
473
- value = fn(value);
474
- }
475
- }
476
- return value;
477
- }
478
- }
479
- return field;
480
- }
481
- function bindDomAttr(arg) {
482
- var attrs = arg.config;
483
- var dom = arg.dom;
484
- var data = arg.data;
485
- var fnPool = arg.fnPool;
486
- var mediator = arg.mediator;
487
- var session = arg.session;
488
- var skipInner = false;
489
- for (var attrName in attrs) {
490
- var value = findValueForDomStyle({ field: attrs[attrName], data: data, fnPool: fnPool, session: session });
491
- if (attrName === "html") {
492
- if (mediator.skipInner)
493
- continue;
494
- dom.innerHTML = value;
495
- skipInner = true;
496
- continue;
497
- }
498
- else if (attrName === "text") {
499
- if (mediator.skipInner)
500
- continue;
501
- dom.innerHTML = encodeHtml(value);
502
- skipInner = true;
503
- continue;
504
- }
505
- else if (attrName === "value") {
506
- if (dom.nodeName === "TEXTAREA")
507
- dom.innerHTML = encodeHtml(value);
508
- else
509
- dom.setAttribute(attrName, (value === null || value === undefined) ? "" : value);
510
- }
511
- else if (attrName.indexOf(".") === -1)
512
- dom.setAttribute(attrName, value);
513
- else {
514
- var segs = attrName.split(".");
515
- var cur = dom;
516
- for (var i = 0; i < segs.length - 1; i++) {
517
- if (cur === null || typeof cur !== "object") {
518
- cur = null;
519
- break;
520
- }
521
- cur = cur[segs[i]];
522
- }
523
- if (cur !== null)
524
- cur[segs[segs.length - 1]] = value;
525
- }
526
- }
527
- delete dom.dataset.mtAttr;
528
- mediator.skipInner = skipInner;
529
- }
530
- function bindDomIterate(arg) {
531
- var config = arg.config;
532
- var dom = arg.dom;
533
- var data = arg.data;
534
- var fnPool = arg.fnPool;
535
- var mediator = arg.mediator;
536
- var arr = findValueForDomStyle({ field: config.data, data: data, session: arg.session });
537
- if (config.mode !== "key-value" && arr !== null && arr !== undefined && !Array.isArray(arr)) {
538
- dom.dataset[arg.name + "Error"] = "Invalid type error: Expected array (field: '" + config.data + "')";
539
- mediator.skipInner = stop;
540
- mediator.skipInner = true;
541
- return;
542
- }
543
- else if (config.mode === "key-value" && arr !== null && arr !== undefined && typeof arr != "object") {
544
- dom.dataset[arg.name + "Error"] = "Invalid type error: Expected object (field: '" + config.data + "')";
545
- mediator.skipInner = stop;
546
- mediator.skipInner = true;
547
- return;
548
- }
549
- var iterateMe = config.iterateMe === true;
550
- var children;
551
- if (!iterateMe) {
552
- children = [];
553
- while (dom.firstChild) {
554
- children.push(dom.firstChild);
555
- dom.removeChild(dom.firstChild);
556
- }
557
- }
558
- if (!arr) {
559
- mediator.skipInner = true;
560
- children = null;
561
- delete dom.dataset[arg.name];
562
- dom.innerHTML = "";
563
- return;
564
- }
565
- var parentIterateScope = arg.session.specialData.iterate;
566
- var currentIterateScope = {};
567
- if (config.inject && typeof config.inject == "object") {
568
- for (var m in config.inject) {
569
- currentIterateScope[m] = findDataForDomStyle({ field: config.inject[m], data: data, session: arg.session });
570
- }
571
- }
572
- currentIterateScope.parent = parentIterateScope;
573
- arg.session.specialData.iterate = currentIterateScope;
574
- var filter = null;
575
- if (config.mode !== "key-value") {
576
- if (config.filter) {
577
- var fnFilter = findFunctionForDomStyle({ field: config.filter, fnPool: fnPool, session: arg.session });
578
- if (typeof fnFilter === "function")
579
- filter = fnFilter;
580
- }
581
- if (config.transform) {
582
- var fnTransform = findFunctionForDomStyle({ field: config.transform, fnPool: fnPool, session: arg.session });
583
- if (typeof fnTransform === "function") {
584
- var transformedArr = [];
585
- arr.forEach(function (item, idx, array) {
586
- transformedArr.push(fnTransform(item, idx, array, data));
587
- });
588
- arr = transformedArr;
589
- }
590
- }
591
- if (config.sort) {
592
- if (typeof config.sort === "string") {
593
- var fnSort = findFunctionForDomStyle({ field: config.sort, fnPool: fnPool, session: arg.session });
594
- if (typeof fnSort === "function")
595
- arr.sort(fnSort);
596
- else if (!fnSort && config.sort.charAt(0) === _dataQueryCharacter)
597
- arr.sort(fieldComparer(config.sort, arg.session));
598
- }
599
- else if (typeof config.sort === "object") {
600
- arr.sort(multiFieldComparer(config.sort, arg.session));
601
- }
602
- }
603
- delete dom.dataset[arg.name];
604
- arr.forEach(function (item, idx, array) {
605
- if (filter && !filter(item, idx, array, data))
606
- return;
607
- currentIterateScope.index = idx;
608
- currentIterateScope.index1 = idx + 1;
609
- if (iterateMe) {
610
- var node = dom.cloneNode(true);
611
- dom.parentNode.insertBefore(node, dom);
612
- var oldScope = arg.session.scope;
613
- if (config.scope)
614
- arg.session.scope += "/" + config.scope + "[" + idx + "]";
615
- arg.session.mt.bindDom(node, item, fnPool, arg.session);
616
- if (config.scope)
617
- arg.session.scope = oldScope;
618
- }
619
- else {
620
- children.forEach(function (child) {
621
- var node = child.cloneNode(true);
622
- dom.appendChild(node);
623
- var oldScope = arg.session.scope;
624
- if (config.scope)
625
- arg.session.scope += "/" + config.scope + "[" + idx + "]";
626
- arg.session.mt.bindDom(node, item, fnPool, arg.session);
627
- if (config.scope)
628
- arg.session.scope = oldScope;
629
- });
630
- }
631
- });
632
- }
633
- else {
634
- if (config.filter) {
635
- var fnFilter = findFunctionForDomStyle({ field: config.filter, fnPool: fnPool, session: arg.session });
636
- if (typeof fnFilter === "function")
637
- filter = fnFilter;
638
- }
639
- delete dom.dataset[arg.name];
640
- var idx = 0;
641
- var index = 0;
642
- for (var key in arr) {
643
- if (filter && !filter(key, idx, arr, data)) {
644
- idx++;
645
- continue;
646
- }
647
- idx++;
648
- currentIterateScope.index = index;
649
- currentIterateScope.index1 = index + 1;
650
- index++;
651
- if (iterateMe) {
652
- var node = dom.cloneNode(true);
653
- dom.parentNode.insertBefore(node, dom);
654
- var oldScope = arg.session.scope;
655
- if (config.scope)
656
- arg.session.scope += "/" + config.scope + "[" + key + "]";
657
- arg.session.mt.bindDom(node, { key: key, value: arr[key] }, fnPool, arg.session);
658
- if (config.scope)
659
- arg.session.scope = oldScope;
660
- }
661
- else {
662
- children.forEach(function (child) {
663
- var node = child.cloneNode(true);
664
- dom.appendChild(node);
665
- var oldScope = arg.session.scope;
666
- if (config.scope)
667
- arg.session.scope += "/" + config.scope + "[" + key + "]";
668
- arg.session.mt.bindDom(node, { key: key, value: arr[key] }, fnPool, arg.session);
669
- if (config.scope)
670
- arg.session.scope = oldScope;
671
- });
672
- }
673
- }
674
- }
675
- if (config.oniterated) {
676
- var fnOnIterated = findFunctionForDomStyle({ field: config.oniterated, fnPool: fnPool, session: arg.session });
677
- if (typeof fnOnIterated == "function") {
678
- try {
679
- fnOnIterated.call(dom, arr);
680
- }
681
- catch (e) {
682
- console.error(e);
683
- }
684
- }
685
- }
686
- arg.session.specialData.iterate = parentIterateScope;
687
- if (config.inject && typeof config.inject == "object") {
688
- for (var m in config.inject) {
689
- currentIterateScope[m] = null;
690
- }
691
- }
692
- currentIterateScope.parent = null;
693
- currentIterateScope = null;
694
- if (iterateMe) {
695
- dom.parentNode.removeChild(dom);
696
- mediator.stop = true;
697
- }
698
- mediator.skipInner = true;
699
- }
700
- function bindDomCond(arg) {
701
- var config = arg.config;
702
- var dom = arg.dom;
703
- var data = arg.data;
704
- var fnPool = arg.fnPool;
705
- var mediator = arg.mediator;
706
- if (typeof config !== "object")
707
- return;
708
- var r = checkConditions(config, data, fnPool, arg.session, true);
709
- if (!r) {
710
- dom.parentNode.removeChild(dom);
711
- mediator.stop = true;
712
- mediator.skipInner = true;
713
- }
714
- delete dom.dataset[arg.name];
715
- }
716
- function bindDomEvent(arg) {
717
- var config = arg.config;
718
- var dom = arg.dom;
719
- for (var eventName in config) {
720
- var fn = findFunctionForDomStyle({ field: config[eventName], fnPool: arg.fnPool, session: arg.session });
721
- if (fn && arg.fnPool && arg.fnPool.__context) {
722
- (function (f, eName, context) {
723
- addEventListener(dom, eName, function () {
724
- var length = arguments.length;
725
- arguments.length = length + 1;
726
- arguments[length] = context;
727
- f.apply(this, arguments);
728
- }, false);
729
- })(fn, eventName, arg.fnPool.__context);
730
- }
731
- else
732
- addEventListener(dom, eventName, fn, false);
733
- }
734
- delete dom.dataset[arg.name];
735
- }
736
- function bindCallFunction(arg) {
737
- if (Array.isArray(arg.config)) {
738
- arg.config.forEach(function (c) {
739
- callFunction(arg.dom, arg.data, arg.fnPool, c, arg.session);
740
- });
741
- }
742
- else
743
- callFunction(arg.dom, arg.data, arg.fnPool, arg.config, arg.session);
744
- delete arg.dom.dataset[arg.name];
745
- }
746
- var _mediatorWatcher = {};
747
- function realtimeWatchHandler() {
748
- var value = this.value || "";
749
- var watcherConfig = _mediatorWatcher[this.__mtWatchName];
750
- if (!watcherConfig)
751
- return;
752
- var orphans = null;
753
- watcherConfig.realtime.forEach(function (watcherId) {
754
- var dom = document.getElementById(watcherId);
755
- if (!dom) {
756
- if (!orphans)
757
- orphans = [];
758
- orphans.push(dom);
759
- return;
760
- }
761
- bindDomAttr({
762
- dom: dom,
763
- config: dom.__mtWatcherConfig.attr,
764
- name: "watchAttr",
765
- data: {
766
- value: value
767
- },
768
- fnPool: dom.__mtWatcherFnPool,
769
- mediator: {}
770
- });
771
- });
772
- if (orphans)
773
- orphans.forEach(function (removedId) {
774
- var idx;
775
- while ((idx = watcherConfig.realtime.indexOf(removedId)) !== -1) {
776
- watcherConfig.normal.splice(idx, 1);
777
- }
778
- });
779
- }
780
- function watchHandler() {
781
- var value = this.nodeName === "INPUT" && (this.type === "checkbox" || this.type === "radio") && !this.checked ? null : this.value;
782
- var watcherConfig = _mediatorWatcher[this.__mtWatchName];
783
- if (!watcherConfig)
784
- return;
785
- var orphans = null;
786
- watcherConfig.normal.forEach(function (watcherId) {
787
- var dom = document.getElementById(watcherId);
788
- if (!dom) {
789
- if (!orphans)
790
- orphans = [];
791
- orphans.push(dom);
792
- return;
793
- }
794
- bindDomAttr({
795
- dom: dom,
796
- config: dom.__mtWatcherConfig.attr,
797
- name: "watchAttr",
798
- data: {
799
- value: value
800
- },
801
- fnPool: dom.__mtWatcherFnPool,
802
- mediator: {}
803
- });
804
- });
805
- if (orphans)
806
- orphans.forEach(function (removedId) {
807
- var idx;
808
- while ((idx = watcherConfig.normal.indexOf(removedId)) !== -1) {
809
- watcherConfig.normal.splice(idx, 1);
810
- }
811
- });
812
- }
813
- var _watcherIdIndexSeed = 0;
814
- function mtWatch(arg) {
815
- var name = arg.session.scope + "#" + arg.config.name;
816
- var watcherConfig = _mediatorWatcher[name];
817
- if (!watcherConfig) {
818
- _mediatorWatcher[name] = watcherConfig = {
819
- realtime: [],
820
- normal: []
821
- };
822
- }
823
- var id = arg.dom.id;
824
- if (!id) {
825
- arg.dom.id = id = "__mtWatcher_" + _watcherIdIndexSeed;
826
- _watcherIdIndexSeed++;
827
- }
828
- watcherConfig[arg.config.realtime ? "realtime" : "normal"].push(id);
829
- arg.dom.__mtWatcherConfig = arg.config;
830
- arg.dom.__mtWatcherFnPool = arg.fnPool;
831
- delete arg.dom.dataset[arg.name];
832
- }
833
- function mtDefineWatchee(arg) {
834
- var nodeName = arg.dom.nodeName;
835
- if (nodeName !== "INPUT" && nodeName !== "SELECT" && nodeName !== "TEXTAREA") {
836
- dom.dataset[arg.name + "Error"] = "Cannot define watchee in this element";
837
- return;
838
- }
839
- var name = arg.session.scope + "#" + arg.config.name;
840
- arg.dom.__mtWatchName = name;
841
- if (this.nodeName === "INPUT" && (this.type === "checkbox" || this.type === "radio")) {
842
- addEventListener(arg.dom, "click", realtimeWatchHandler, false);
843
- addEventListener(arg.dom, "click", watchHandler, false);
844
- }
845
- else if (nodeName === "SELECT") {
846
- addEventListener(arg.dom, "change", realtimeWatchHandler, false);
847
- addEventListener(arg.dom, "change", watchHandler, false);
848
- }
849
- else {
850
- addEventListener(arg.dom, "keyup", realtimeWatchHandler, false);
851
- addEventListener(arg.dom, "change", watchHandler, false);
852
- }
853
- delete arg.dom.dataset[arg.name];
854
- }
855
- var _mediatorChecker = {};
856
- var _validationDefaultMessages = {
857
- "%!required%": "Required",
858
- "%!maxLength%": "Too long",
859
- "%!minLength%": "Too short",
860
- "%!min%": "Too small",
861
- "%!max%": "Too big",
862
- "%!pattern%": "Invalid"
863
- };
864
- function checkHandler(e) {
865
- var value = this.nodeName === "INPUT" && (this.type === "checkbox" || this.type === "radio") && !this.checked ? "" : this.value || "";
866
- var messageIds = _mediatorChecker[this.__mtCheckName];
867
- if (!messageIds)
868
- return;
869
- var mtCheckConfig = this.__mtCheckConfig;
870
- var errorMsg = "";
871
- if (!errorMsg && mtCheckConfig.required && !value) {
872
- errorMsg = mtCheckConfig.requiredMessage || "%!required%";
873
- }
874
- if (!errorMsg && "maxLength" in mtCheckConfig && value !== "" && value.length > mtCheckConfig.maxLength) {
875
- errorMsg = mtCheckConfig.maxLengthMessage || "%!maxLength%";
876
- }
877
- if (!errorMsg && "minLength" in mtCheckConfig && value !== "" && value.length > mtCheckConfig.minLength) {
878
- errorMsg = mtCheckConfig.minLengthMessage || "%!minLength%";
879
- }
880
- if (!errorMsg && "min" in mtCheckConfig && value !== "" && value < mtCheckConfig.min) {
881
- errorMsg = mtCheckConfig.minMessage || "%!min%";
882
- }
883
- if (!errorMsg && "max" in mtCheckConfig && value !== "" && value < mtCheckConfig.max) {
884
- errorMsg = mtCheckConfig.maxMessage || "%!max%";
885
- }
886
- if (!errorMsg && "pattern" in mtCheckConfig && value !== "" && !value.match(new RegExp(mtCheckConfig.pattern, mtCheckConfig.patternOptions))) {
887
- errorMsg = mtCheckConfig.patternMessage || "%!pattern%";
888
- }
889
- if (!errorMsg && "complex" in mtCheckConfig && value !== "") {
890
- }
891
- if (!errorMsg && "custom" in mtCheckConfig && value !== "") {
892
- var fn = findFunctionForDomStyle({ field: mtCheckConfig.custom, fnPool: this.__mtCheckFnPool });
893
- if (fn) {
894
- var s = fn(value, this, e);
895
- if (s && typeof s == "string")
896
- errorMsg = s;
897
- }
898
- }
899
- var orphans = null;
900
- messageIds.forEach(function (messageId) {
901
- var dom = document.getElementById(messageId);
902
- if (!dom) {
903
- if (!orphans)
904
- orphans = [];
905
- orphans.push(dom);
906
- return;
907
- }
908
- switch (dom.__mtCheckMessageConfig.mode) {
909
- case "show-hide":
910
- case "showHide":
911
- if (errorMsg) {
912
- if (errorMsg in _validationDefaultMessages)
913
- dom.innerHTML = dom.__mtCheckMessageOriginHtml;
914
- else
915
- dom.innerHTML = encodeHtml(errorMsg);
916
- dom.style.display = "block";
917
- }
918
- else {
919
- dom.innerHTML = "";
920
- dom.style.display = "none";
921
- }
922
- break;
923
- case "toggle-class":
924
- case "toggleClass":
925
- if (errorMsg) {
926
- if (errorMsg in _validationDefaultMessages)
927
- dom.innerHTML = dom.__mtCheckMessageOriginHtml;
928
- else
929
- dom.innerHTML = encodeHtml(errorMsg);
930
- if (!dom.className) {
931
- dom.className = dom.__mtCheckMessageConfig.toggleClass;
932
- break;
933
- }
934
- var classes = dom.className.split(/\s/);
935
- if (classes.indexOf(dom.__mtCheckMessageConfig.toggleClass) === -1) {
936
- classes.push(dom.__mtCheckMessageConfig.toggleClass);
937
- dom.className = classes.join(" ");
938
- }
939
- }
940
- else {
941
- dom.innerHTML = "";
942
- if (!dom.className)
943
- break;
944
- var classes = dom.className.split(/\s/);
945
- for (var i = classes.length - 1; i > 0; i--) {
946
- if (classes[i] === dom.__mtCheckMessageConfig.toggleClass)
947
- classes.splice(i, 1);
948
- }
949
- dom.className = classes.join(" ");
950
- }
951
- break;
952
- default:
953
- var message = _validationDefaultMessages[errorMsg] || errorMsg;
954
- dom.innerHTML = message ? encodeHtml(message) : "";
955
- break;
956
- }
957
- });
958
- if (orphans)
959
- orphans.forEach(function (removedId) {
960
- var idx;
961
- while ((idx = messageIds.indexOf(removedId)) !== -1) {
962
- messageIds.splice(idx, 1);
963
- }
964
- });
965
- }
966
- var _checkerIdIndexSeed = 0;
967
- function mtCheck(arg) {
968
- var nodeName = arg.dom.nodeName;
969
- if (nodeName !== "INPUT" && nodeName !== "SELECT" && nodeName !== "TEXTAREA") {
970
- arg.dom.dataset[arg.name + "Error"] = "Cannot check this element";
971
- return;
972
- }
973
- var name = arg.session.scope + "#" + arg.config.name;
974
- arg.dom.__mtCheckName = name;
975
- arg.dom.__mtCheckConfig = arg.config;
976
- arg.dom.__mtCheckFnPool = arg.fnPool;
977
- addEventListener(arg.dom, this.nodeName === "INPUT" && (this.type === "checkbox" || this.type === "radio") ? "click" : "change", checkHandler, false);
978
- addEventListener(arg.dom, "blur", checkHandler, false);
979
- delete arg.dom.dataset[arg.name];
980
- }
981
- function mtCheckMessage(arg) {
982
- var name = arg.session.scope + "#" + arg.config.name;
983
- var checkerConfig = _mediatorChecker[name];
984
- if (!checkerConfig) {
985
- _mediatorChecker[name] = checkerConfig = [];
986
- }
987
- var id = arg.dom.id;
988
- if (!id) {
989
- arg.dom.id = id = "__mtCheckMessage_" + _checkerIdIndexSeed;
990
- _checkerIdIndexSeed++;
991
- }
992
- checkerConfig.push(id);
993
- arg.dom.__mtCheckMessageConfig = arg.config;
994
- arg.dom.__mtCheckMessageOriginHtml = arg.dom.innerHTML;
995
- delete arg.dom.dataset[arg.name];
996
- }
997
- mt.builtinFeatures = {
998
- mtCond: bindDomCond,
999
- mtIterate: bindDomIterate,
1000
- mtCall: bindCallFunction,
1001
- mtAttr: bindDomAttr,
1002
- mtEvent: bindDomEvent,
1003
- mtWatch: mtWatch,
1004
- mtDefineWatchee: mtDefineWatchee,
1005
- mtCheck: mtCheck,
1006
- mtCheckMessage: mtCheckMessage
1007
- };
1008
- mt.fnUtils = {
1009
- findValueByPath: findValueByPath,
1010
- findDataForDomStyle: findDataForDomStyle,
1011
- findFunctionForDomStyle: findFunctionForDomStyle,
1012
- findValueForDomStyle: findValueForDomStyle,
1013
- mergeData: mergeData,
1014
- fieldComparer: fieldComparer,
1015
- multiFieldComparer: multiFieldComparer,
1016
- checkCondition: checkCondition,
1017
- checkConditions: checkConditions,
1018
- addEventListener: addEventListener
1019
- };
1020
- if (typeof formatValue == "function")
1021
- mt.fnUtils.formatValue = formatValue;
1022
- for (var feature in mt.builtinFeatures) {
1023
- mt.registerDomFeature(feature, mt.builtinFeatures[feature]);
1024
- }
1025
- })();
1
+ (function () {
2
+ function MrgiaTemplate() {
3
+ var _debugMode = false;
4
+ this.debugMode = function (value) {
5
+ if (value === undefined)
6
+ return _debugMode;
7
+ _debugMode = !!value;
8
+ };
9
+
10
+ var _domBinders = {};
11
+
12
+ this.registerDomFeature = function (name, fnHandler) {
13
+ if (typeof name !== "string")
14
+ throw new TypeError("Dom binder name must be a string");
15
+ if (typeof fnHandler !== "function")
16
+ throw new TypeError("fnHandler must be a function");
17
+ _domBinders[name] = fnHandler;
18
+ };
19
+
20
+
21
+
22
+ this.bindDom = function (dom, data, fnPool, session) {
23
+ if (!dom)
24
+ return;
25
+
26
+ if (dom.nodeType !== Node.ELEMENT_NODE)
27
+ return;
28
+
29
+ session = session || {};
30
+ if (!session.mt)
31
+ session.mt = this;
32
+ if (typeof session.scope != "string")
33
+ session.scope = "";
34
+ if (typeof session.specialData != "object" && !session.specialData)
35
+ session.specialData = {};
36
+
37
+ if (dom.dataset) {
38
+ var mediator = {};
39
+ var skipInner = undefined;
40
+ for (var name in _domBinders) {
41
+ var config = dom.dataset[name];
42
+ if (!config)
43
+ continue;
44
+ if (typeof config === "string") {
45
+ try {
46
+ config = JSON.parse(config);
47
+ } catch (e) {
48
+ dom.dataset[name + "Error"] = "Parse error";
49
+ skipInner = true;
50
+ continue;
51
+ }
52
+ }
53
+ if (typeof config !== "object") {
54
+ dom.dataset[name + "Error"] = "Invalid configuration";
55
+ skipInner = true;
56
+ continue;
57
+ }
58
+
59
+ if (!dom.__mt_features)
60
+ dom.__mt_features = {};
61
+ dom.__mt_features[name] = dom.dataset[name];
62
+
63
+ _domBinders[name]({
64
+ name: name,
65
+ dom: dom,
66
+ data: data,
67
+ fnPool: fnPool,
68
+ config: config,
69
+ mediator: mediator,
70
+ session: session
71
+ });
72
+ if (mediator.stop)
73
+ return;
74
+ if (mediator.skipInner)
75
+ skipInner = true;
76
+ mediator.skipInner = skipInner;
77
+ }
78
+ if (skipInner)
79
+ return;
80
+ }
81
+
82
+ for (var i = 0; i < dom.childNodes.length; i++) {
83
+ this.bindDom(dom.childNodes[i], data, fnPool, session);
84
+ }
85
+ };
86
+
87
+ this.rebindDom = function (dom, data, fnPool, session) {
88
+ if (dom.__mt_features) {
89
+ for (var name in dom.__mt_features) {
90
+ dom.dataset[name] = dom.__mt_features[name];
91
+ }
92
+ }
93
+ this.bindDom(dom, data, fnPool, session);
94
+ };
95
+
96
+ var _languages = [];
97
+ var _currentLanguage = null;
98
+ this.addLanguage = function (language) {
99
+ if (!language || !language.code || !language.data || typeof language.data !== "object")
100
+ return;
101
+
102
+ if (_languages.some(function (lang) { return lang.code === language.code; }))
103
+ return;
104
+
105
+ var lang = { code: language.code, data: language.data };
106
+ _languages.push(lang);
107
+ if (_languages.length === 1)
108
+ _currentLanguage = lang;
109
+ };
110
+
111
+ this.setCurrentLanguage = function (code) {
112
+ return _languages.some(function (lang) {
113
+ if (lang.code === code) {
114
+ _currentLanguage = lang;
115
+ return true;
116
+ }
117
+ return false;
118
+ });
119
+ };
120
+
121
+ this.getCurrentLanguage = function () {
122
+ return _currentLanguage;
123
+ };
124
+ }
125
+
126
+ window.mt = new MrgiaTemplate();
127
+ window.MrgiaTemplate = MrgiaTemplate;
128
+ })();
129
+
130
+ (function () {
131
+ // #region DOM template processor style
132
+ var _dataQueryCharacter = "$";
133
+ var _specialDataQueryCharacter = "~";
134
+ var _functionQueryCharacter = "@";
135
+ var _conditionOperatorCharacter = "$";
136
+ var _languageQueryCharacter = "#";
137
+
138
+ // #region helper functions
139
+ function encodeHtml(data) {
140
+ if (data === undefined || data === null)
141
+ return "";
142
+ var html = typeof (data) === "string" ? data : data.toString();
143
+ html = html.replace(/&/g, "&amp;");
144
+ html = html.replace(/</g, "&lt;");
145
+ html = html.replace(/>/g, "&gt;");
146
+ html = html.replace(/"/g, "&quot;");
147
+ html = html.replace(/'/g, "&apos;");
148
+ return html;
149
+ }
150
+
151
+ function fieldComparer(fieldPath, session) {
152
+ return function (a, b) {
153
+ var x = findValueForDomStyle({ field: fieldPath, data: a, session: session });
154
+ var y = findValueForDomStyle({ field: fieldPath, data: b, session: session });
155
+ return x > y ? 1 : x < y ? -1 : 0;
156
+ };
157
+ }
158
+
159
+ function multiFieldComparer(fields, session) {
160
+ return function (a, b) {
161
+ for (var m in fields) {
162
+ if (!m || m.charAt(0) !== "$" || fields[m] !== 1 && fields[m] !== -1)
163
+ continue;
164
+ var x = findValueForDomStyle({ field: m, data: a, session: session });
165
+ var y = findValueForDomStyle({ field: m, data: b, session: session });
166
+ if (x > y)
167
+ return fields[m];
168
+ else if (x < y)
169
+ return -fields[m];
170
+ }
171
+ return 0;
172
+ }
173
+ }
174
+
175
+ function checkCondition(x1, op, x2, data, fnPool, session, isRoot) {
176
+ if (typeof x2 === "function")
177
+ return x2(x1);
178
+
179
+ switch (op) {
180
+ case "eq":
181
+ return x1 == x2;
182
+ case "gt":
183
+ return x1 > x2;
184
+ case "gte":
185
+ return x1 >= x2;
186
+ case "lt":
187
+ return x1 < x2;
188
+ case "lte":
189
+ return x1 <= x2;
190
+ case "ne":
191
+ return x1 != x2;
192
+ case "or":
193
+ {
194
+ if (!Array.isArray(x2))
195
+ return false;
196
+ return x2.some(function (item) {
197
+ if (item !== null && typeof item === "object")
198
+ return checkConditions(item, x1, fnPool, session, isRoot);
199
+ return x1 == item;
200
+ });
201
+ }
202
+ case "and":
203
+ {
204
+ if (!Array.isArray(x2))
205
+ return false;
206
+ return x2.every(function (item) {
207
+ if (item !== null && typeof item === "object")
208
+ return checkConditions(item, x1, fnPool, session, isRoot);
209
+ return x1 == item;
210
+ });
211
+ }
212
+ case "not":
213
+ return !checkConditions(x2, x1, fnPool, session, isRoot);
214
+ case "exists":
215
+ return x2 ? x1 !== undefined : x1 === undefined;
216
+ case "type":
217
+ {
218
+ switch (x2) {
219
+ case "number":
220
+ case "string":
221
+ case "boolean":
222
+ case "function":
223
+ case "undefined":
224
+ return typeof x1 === x2;
225
+ case "null":
226
+ return x1 === null;
227
+ case "object":
228
+ return typeof x1 === x2 && x1 !== null && !Array.isArray(x1);
229
+ case "array":
230
+ return Array.isArray(x1);
231
+ case "regex":
232
+ return x1 instanceof RegExp;
233
+ case "date":
234
+ return x1 instanceof Date;
235
+ case "html-element":
236
+ return x1 instanceof HTMLElement;
237
+ }
238
+ }
239
+ case "in":
240
+ {
241
+ if (!Array.isArray(x2))
242
+ return false;
243
+ return x2.indexOf(x1) !== -1;
244
+ }
245
+ case "nin":
246
+ {
247
+ if (!Array.isArray(x2))
248
+ return false;
249
+ return x2.indexOf(x1) === -1;
250
+ }
251
+ case "size":
252
+ {
253
+ if (x1 === undefined || x1 === null || Array.isArray(x1))
254
+ return x1 ? x1.length == x2 : x2 == 0;
255
+ return false;
256
+ }
257
+ case "all":
258
+ {
259
+ if (!Array.isArray(x1) || !Array.isArray(x2))
260
+ return false;
261
+ return x2.every(function (item) {
262
+ return x1.indexOf(item) !== -1;
263
+ });
264
+ }
265
+ case "mod":
266
+ {
267
+ if (typeof x1 !== "number" || !Array.isArray(x2) || x2.length !== 2)
268
+ return false;
269
+ return x1 % x2[0] === x2[1];
270
+ }
271
+ case "regex":
272
+ {
273
+ if (typeof x1 !== "string")
274
+ return false;
275
+
276
+ var re;
277
+ if (typeof x2 === "string") {
278
+ re = new RegExp(x1);
279
+ }
280
+ else if (Array.isArray(x2)) {
281
+ if (x2.length)
282
+ re = new RegExp(x2[0], x2.length > 1 ? x2[1] : undefined);
283
+ }
284
+ else if (x2 instanceof RegExp) {
285
+ re = x2;
286
+ }
287
+
288
+ return re && re.test(x1);
289
+ }
290
+ }
291
+ return false;
292
+ }
293
+ function checkConditions(cond, data, fnPool, session, isRoot) {
294
+ for (var field in cond) {
295
+ if (field && field.charAt(0) === _conditionOperatorCharacter) {
296
+ if (!checkCondition(data, field.substr(1), cond[field], data, fnPool, session, isRoot))
297
+ return false;
298
+ }
299
+ else {
300
+ var x1 = isRoot && field && field.charAt(0) === _specialDataQueryCharacter
301
+ ? findSpecialCharacter(field.substr(1), session)
302
+ : findValueByPath(data, field);
303
+ var value = cond[field];
304
+ if (typeof value === "string") {
305
+ var fn = value && value.charAt(0) === _functionQueryCharacter ? findFunctionForDomStyle({ field: value, fnPool: fnPool, session: session }) : null;
306
+ if (fn && !fn(x1))
307
+ return false;
308
+
309
+ value = findValueForDomStyle({ field: value, data: data, fnPool: fnPool, session: session });
310
+ if (!checkCondition(x1, "eq", value, data, fnPool, session))
311
+ return false;
312
+ } else if (value !== null && typeof value === "object") {
313
+ if (!checkConditions(value, x1, fnPool, session))
314
+ return false;
315
+ } else {
316
+ if (x1 !== value)
317
+ return false;
318
+ }
319
+ }
320
+ }
321
+ return true;
322
+ }
323
+
324
+ function addEventListener(dom, eventName, handler, useCapture) {
325
+ if (typeof handler !== "function")
326
+ return;
327
+
328
+ if (typeof dom.addEventListener == "function")
329
+ dom.addEventListener(eventName, handler, useCapture);
330
+ else if (typeof dom.attachEvent == "function")
331
+ dom.attachEvent("on" + eventName, handler);
332
+ }
333
+
334
+ function callFunction(dom, data, fnPool, config, session) {
335
+ var fn = findFunctionForDomStyle({ field: config.fn, fnPool: fnPool, session: session });
336
+ if (!fn)
337
+ return;
338
+ var me = dom;
339
+ var thisField = config["this"];
340
+ if (thisField) {
341
+ if (thisField.charAt(0) === _dataQueryCharacter)
342
+ me = findDataForDomStyle({ field: thisField, data: data, session: session });
343
+ else
344
+ me = findDataForDomStyle({ field: _dataQueryCharacter + thisField, data: window, session: session });
345
+ }
346
+ var parametes = Array.isArray(config.data) ? config.data.map(function (item) { return findDataForDomStyle({ field: item, data: data, session: session }); }) : [findDataForDomStyle({ field: config.data, data: data, session: session })];
347
+ fn.apply(me, parametes);
348
+ }
349
+
350
+ // #endregion
351
+
352
+ // #region Helper functions - new version
353
+ function findValueByPath(obj, path) {
354
+ // kiểm tra giá trị của obj và path
355
+ if (obj === undefined || typeof (path) !== "string") return undefined;
356
+ if (obj === null) return undefined;
357
+ // tách path thành các trường, các trường phân cấp bởi dấu .
358
+ var seg = path.split(".");
359
+ // thực hiện lấy giá trị theo cây phân cấp
360
+ var r = obj;
361
+ var index = 0;
362
+ if (seg.length > 0 && seg[0] === "this") {
363
+ index = 1;
364
+ }
365
+ for (var i = index; i < seg.length; i++) {
366
+ // kiểm tra null
367
+ if (r === null)
368
+ return undefined;
369
+
370
+ // obj là kiểu object thì trả về giá trị thuộc tính
371
+ if (typeof r === "object" || typeof r === "string" || typeof r === "function")
372
+ r = r[seg[i]];
373
+ else
374
+ // nếu obj không hợp lệ thì trả về undefined
375
+ return undefined;
376
+ }
377
+ return r;
378
+ }
379
+
380
+ /**
381
+ * Lấy về hàm được quy định bởi field trong fnPool
382
+ * Nếu field là hàm --> trả về nó luôn
383
+ * Nếu field là chuỗi:
384
+ * Nếu bắt đầu bởi @ --> lấy hàm trong fnPool (field là field path sau khi bỏ @)
385
+ * Nếu không bắt đầu bởi @ --> lấy hàm trong window (field là field path)
386
+ * Nếu field thuộc các kiểu khác --> trả về undefined
387
+ *//**
388
+ * args
389
+ * field: trên trường, có dạng a.b hoặc @a.b
390
+ * fnPool: đối tượng chứa hàm
391
+ */
392
+ function findFunctionForDomStyle(args) {
393
+ var field = args.field;
394
+ if (typeof field === "function") {
395
+ return field;
396
+ } else if (typeof field === "string") {
397
+ var fnPool = args.fnPool;
398
+ var fn = field === "" || field.charAt(0) !== _functionQueryCharacter
399
+ ? findValueByPath(window, field)
400
+ : findValueByPath(fnPool, field.substr(1));
401
+ return typeof fn === "function" ? fn : undefined;
402
+ }
403
+
404
+ return undefined;
405
+ }
406
+
407
+ /**
408
+ * Lấy về dữ liệu được quy định bởi field trong data
409
+ * Nếu field là chuỗi và bắt đầu bởi $ --> lấy giá trị trong data (field là field path sau khi bỏ $)
410
+ * Nếu field là chuỗi và bắt đầu bởi # --> lấy giá trị trong kho ngôn ngữ (field là field path sau khi bỏ #)
411
+ * Trái lại --> trả về nó luôn
412
+ *//**
413
+ * args
414
+ * field: trên trường, có dạng a.b hoặc @a.b
415
+ * data: đối tượng chứa hàm
416
+ * session: phiên bind hiện tại (optional)
417
+ */
418
+ function findDataForDomStyle(args) {
419
+ var field = args.field;
420
+
421
+ if (typeof field !== "string" || field === "")
422
+ return field;
423
+
424
+ var data = args.data;
425
+ if (field.charAt(0) === _dataQueryCharacter) {
426
+ if (field.length > 1 && field.charAt(1) === _specialDataQueryCharacter) {
427
+ return findSpecialCharacter(field.substr(2), args.session);
428
+ }
429
+ return findValueByPath(data, field.substr(1));
430
+ }
431
+ else if (field.charAt(0) === _languageQueryCharacter) {
432
+
433
+ if (field.length === 1)
434
+ return field;
435
+ if (field.charAt(1) === _languageQueryCharacter)
436
+ return field.substr(1);
437
+
438
+ var currentMt = session ? session.mt ? session.mt : mt : mt;
439
+ var lang = currentMt.getCurrentLanguage();
440
+ if (!lang)
441
+ return "";
442
+ return lang.data[field.substr(1)];
443
+ }
444
+
445
+ return field;
446
+ }
447
+
448
+ /**
449
+ * Lấy về dữ liệu được quy định bởi mask trong data
450
+ *//**
451
+ * args
452
+ * mask: biến chứa các cấu hình cho việc merge data
453
+ * data: đối tượng chứa hàm
454
+ * session: phiên bind hiện tại (optional)
455
+ */
456
+ function mergeData(args) {
457
+ var mask = args.mask;
458
+ if (typeof mask !== "string" && typeof mask !== "object" || mask === null)
459
+ return mask;
460
+
461
+ var data = args.data;
462
+ var session = args.session;
463
+ if (typeof mask === "string") {
464
+ return findDataForDomStyle({ field: mask, data: data, session: session });
465
+ }
466
+
467
+ if (Array.isArray(mask)) {
468
+ return mask.map(function (item) {
469
+ return mergeData({ mask: item, data: data, session: session });
470
+ });
471
+ }
472
+
473
+ var obj = {};
474
+ for (var m in mask) {
475
+ obj[m] = mergeData({ mask: mask[m], data: data, session: session });
476
+ }
477
+ return obj;
478
+ }
479
+
480
+ function findSpecialCharacter(path, session) {
481
+ return findValueByPath(session.specialData, path);
482
+ }
483
+
484
+ function findValueForDomStyle(args) {
485
+ var field = args.field;
486
+ var data = args.data;
487
+ var fnPool = args.fnPool;
488
+ var preferFunction = args.preferFunction;
489
+ var session = args.session;
490
+
491
+ var fn;
492
+ if (typeof field === "string") {
493
+ if (field === "")
494
+ return "";
495
+ switch (field.charAt(0)) {
496
+ case _dataQueryCharacter:
497
+ if (field.length > 1 && field.charAt(1) === _specialDataQueryCharacter) {
498
+ return findSpecialCharacter(field.substr(2), session);
499
+ }
500
+ return findValueByPath(data, field.substr(1));
501
+ case _functionQueryCharacter:
502
+ fn = findValueByPath(fnPool, field.substr(1));
503
+ return typeof fn === "function" ? fn : undefined;
504
+ case _languageQueryCharacter:
505
+ var currentMt = session ? session.mt ? session.mt : mt : mt;
506
+ var lang = currentMt.getCurrentLanguage();
507
+ if (!lang)
508
+ return "";
509
+ return lang.data[field.substr(1)];
510
+ default:
511
+ if (!preferFunction)
512
+ return field;
513
+ var gfn = findValueByPath(fnPool, field.substr(1));
514
+ return typeof gfn === "function" ? gfn : undefined;
515
+ }
516
+ }
517
+ else if (typeof field === "object") {
518
+ if (field === null)
519
+ return null;
520
+ if ("fn" in field && field.fn) {
521
+ var value = undefined;
522
+ if (Array.isArray(field.fn)) {
523
+ if (Array.isArray(field.data)) {
524
+ // xử lý cho trường hợp {"fn": ["@add", "@round"], "data": [["$a", "$b"], ["$~pipeline.result", 2]]}
525
+ // xử lý cho trường hợp {"fn": ["@add", "@round"], "data": [["$a", "$b"], "$~pipeline.result"]}
526
+ // xử lý cho trường hợp {"fn": ["@add", "@round"], "data": [["$a", "$b"]]}
527
+ // xử lý cho trường hợp {"fn": ["@round", "JSON.stringify"], "data": ["$a", "$~pipeline.result"]}
528
+ for (var i = 0; i < field.fn.length; i++) {
529
+ fn = findFunctionForDomStyle({ field: field.fn[i], fnPool: fnPool, session: session });
530
+ if (!fn)
531
+ break;
532
+
533
+ var oldValue = session.specialData.pipeline;
534
+ session.specialData.pipeline = { result: value };
535
+ var p = field.data[i] === undefined || field.data[i] === null ? value : mergeData({ mask: field.data[i], data: data, session: session });
536
+ session.specialData.pipeline = oldValue;
537
+ if (Array.isArray(field.data[i]))
538
+ // xử lý cho trường hợp {"fn": ["@add", "@round"], "data": [["$a", "$b"], ["$~pipeline.result", 2]]}
539
+ value = fn.apply(null, p);
540
+ else
541
+ // xử lý cho trường hợp {"fn": ["@round", "JSON.stringify"], "data": ["$a", "$~pipeline.result"]}
542
+ value = fn(p);
543
+ }
544
+ }
545
+ else {
546
+ // xử lý cho trường hợp {"fn": ["@toDate", "@frag", "JSON.stringify"], "data": "$joinedDate"}
547
+ // xử lý cho trường hợp {"fn": ["@toDate", "@frag", "JSON.stringify"], "data": 20134}
548
+ value = findDataForDomStyle({ field: field.data, data: data, session: session });
549
+ field.fn.forEach(function (fnName) {
550
+ fn = findFunctionForDomStyle({ field: fnName, fnPool: fnPool, session: session });
551
+ if (!fn)
552
+ return;
553
+ value = fn(value);
554
+ });
555
+ }
556
+ }
557
+ else if (typeof field.fn === "string") {
558
+ fn = findFunctionForDomStyle({ field: field.fn, fnPool: fnPool, session: session });
559
+ if (fn) {
560
+ value = mergeData({ mask: field.data, data: data, session: session });
561
+ if (Array.isArray(field.data))
562
+ // xử lý cho trường hợp {"fn": "@round", "data": ["$point", 2]}
563
+ value = fn.apply(null, value);
564
+ else
565
+ // xử lý cho trường hợp {"fn": "@round", "data": "$point"}
566
+ // xử lý cho trường hợp {"fn": "@round", "data": 1}
567
+ value = fn(value);
568
+ }
569
+ }
570
+ return value;
571
+ }
572
+ }
573
+ return field;
574
+ }
575
+
576
+ // #endregion
577
+
578
+ // #region bind data
579
+
580
+ /**
581
+ * @private
582
+ * @method
583
+ * @param {DomFeatureMediator} arg
584
+ */
585
+ function bindDomAttr(arg) {
586
+ var attrs = arg.config;
587
+ var dom = arg.dom;
588
+ var data = arg.data;
589
+ var fnPool = arg.fnPool;
590
+ var mediator = arg.mediator;
591
+ var session = arg.session;
592
+
593
+ var skipInner = false;
594
+ for (var attrName in attrs) {
595
+ var value = findValueForDomStyle({ field: attrs[attrName], data: data, fnPool: fnPool, session: session });
596
+ if (attrName === "html") {
597
+ if (mediator.skipInner)
598
+ continue;
599
+ dom.innerHTML = value;
600
+ skipInner = true;
601
+ continue;
602
+ }
603
+ else if (attrName === "text") {
604
+ if (mediator.skipInner)
605
+ continue;
606
+ dom.innerHTML = encodeHtml(value);
607
+ skipInner = true;
608
+ continue;
609
+ }
610
+ else if (attrName === "value") {
611
+ if (dom.nodeName === "TEXTAREA")
612
+ dom.innerHTML = encodeHtml(value);
613
+ else
614
+ dom.setAttribute(attrName, (value === null || value === undefined) ? "" : value);
615
+ }
616
+ else if (attrName.indexOf(".") === -1)
617
+ dom.setAttribute(attrName, value);
618
+ else {
619
+ var segs = attrName.split(".");
620
+ var cur = dom;
621
+ for (var i = 0; i < segs.length - 1; i++) {
622
+ if (cur === null || typeof cur !== "object") {
623
+ cur = null;
624
+ break;
625
+ }
626
+ cur = cur[segs[i]];
627
+ }
628
+ if (cur !== null)
629
+ cur[segs[segs.length - 1]] = value;
630
+ }
631
+ }
632
+ delete dom.dataset.mtAttr;
633
+ mediator.skipInner = skipInner;
634
+ }
635
+
636
+ /**
637
+ * @private
638
+ * @method
639
+ * @param {DomFeatureMediator} arg
640
+ */
641
+ function bindDomIterate(arg) {
642
+ var config = arg.config;
643
+ var dom = arg.dom;
644
+ var data = arg.data;
645
+ var fnPool = arg.fnPool;
646
+ var mediator = arg.mediator;
647
+
648
+ var arr = findValueForDomStyle({ field: config.data, data: data, session: arg.session });
649
+
650
+ if (config.mode !== "key-value" && arr !== null && arr !== undefined && !Array.isArray(arr)) {
651
+ dom.dataset[arg.name + "Error"] = "Invalid type error: Expected array (field: '" + config.data + "')";
652
+ mediator.skipInner = stop;
653
+ mediator.skipInner = true;
654
+ return;
655
+ }
656
+ else if (config.mode === "key-value" && arr !== null && arr !== undefined && typeof arr != "object") {
657
+ dom.dataset[arg.name + "Error"] = "Invalid type error: Expected object (field: '" + config.data + "')";
658
+ mediator.skipInner = stop;
659
+ mediator.skipInner = true;
660
+ return;
661
+ }
662
+
663
+ var iterateMe = config.iterateMe === true;
664
+
665
+ var children;
666
+
667
+ if (!iterateMe) {
668
+ children = [];
669
+ while (dom.firstChild) {
670
+ children.push(dom.firstChild);
671
+ dom.removeChild(dom.firstChild);
672
+ }
673
+ }
674
+
675
+ if (!arr) {
676
+ mediator.skipInner = true;
677
+ children = null;
678
+ delete dom.dataset[arg.name];
679
+ dom.innerHTML = "";
680
+ return;
681
+ }
682
+
683
+ var parentIterateScope = arg.session.specialData.iterate;
684
+ var currentIterateScope = {};
685
+
686
+ if (config.inject && typeof config.inject == "object") {
687
+ for (var m in config.inject) {
688
+ currentIterateScope[m] = findDataForDomStyle({ field: config.inject[m], data: data, session: arg.session });
689
+ }
690
+ }
691
+ currentIterateScope.parent = parentIterateScope;
692
+ arg.session.specialData.iterate = currentIterateScope;
693
+
694
+ var filter = null;
695
+ if (config.mode !== "key-value") {
696
+
697
+ if (config.filter) {
698
+ var fnFilter = findFunctionForDomStyle({ field: config.filter, fnPool: fnPool, session: arg.session });
699
+ if (typeof fnFilter === "function")
700
+ filter = fnFilter;
701
+ }
702
+
703
+ if (config.transform) {
704
+ var fnTransform = findFunctionForDomStyle({ field: config.transform, fnPool: fnPool, session: arg.session });
705
+ if (typeof fnTransform === "function") {
706
+ var transformedArr = [];
707
+ arr.forEach(function (item, idx, array) {
708
+ transformedArr.push(fnTransform(item, idx, array, data));
709
+ });
710
+ arr = transformedArr;
711
+ }
712
+ }
713
+
714
+ if (config.sort) {
715
+ if (typeof config.sort === "string") {
716
+ var fnSort = findFunctionForDomStyle({ field: config.sort, fnPool: fnPool, session: arg.session });
717
+ if (typeof fnSort === "function")
718
+ arr.sort(fnSort);
719
+ else if (!fnSort && config.sort.charAt(0) === _dataQueryCharacter)
720
+ arr.sort(fieldComparer(config.sort, arg.session));
721
+ } else if (typeof config.sort === "object") {
722
+ arr.sort(multiFieldComparer(config.sort, arg.session));
723
+ }
724
+ }
725
+
726
+ delete dom.dataset[arg.name];
727
+
728
+ arr.forEach(function (item, idx, array) {
729
+ if (filter && !filter(item, idx, array, data))
730
+ return;
731
+
732
+ currentIterateScope.index = idx;
733
+ currentIterateScope.index1 = idx + 1;
734
+
735
+ if (iterateMe) {
736
+ var node = dom.cloneNode(true);
737
+ dom.parentNode.insertBefore(node, dom);
738
+
739
+ var oldScope = arg.session.scope;
740
+ if (config.scope)
741
+ arg.session.scope += "/" + config.scope + "[" + idx + "]";
742
+
743
+ arg.session.mt.bindDom(node, item, fnPool, arg.session);
744
+
745
+ if (config.scope)
746
+ arg.session.scope = oldScope;
747
+ } else {
748
+ children.forEach(function (child) {
749
+ var node = child.cloneNode(true);
750
+ dom.appendChild(node);
751
+
752
+ var oldScope = arg.session.scope;
753
+ if (config.scope)
754
+ arg.session.scope += "/" + config.scope + "[" + idx + "]";
755
+
756
+ arg.session.mt.bindDom(node, item, fnPool, arg.session);
757
+
758
+ if (config.scope)
759
+ arg.session.scope = oldScope;
760
+ });
761
+ }
762
+ });
763
+
764
+ } else {
765
+
766
+ if (config.filter) {
767
+ var fnFilter = findFunctionForDomStyle({ field: config.filter, fnPool: fnPool, session: arg.session });
768
+ if (typeof fnFilter === "function")
769
+ filter = fnFilter;
770
+ }
771
+ delete dom.dataset[arg.name];
772
+ var idx = 0;
773
+ var index = 0;
774
+ for (var key in arr) {
775
+ if (filter && !filter(key, idx, arr, data)) {
776
+ idx++;
777
+ continue;
778
+ }
779
+
780
+ idx++;
781
+
782
+ currentIterateScope.index = index;
783
+ currentIterateScope.index1 = index + 1;
784
+ index++;
785
+
786
+ if (iterateMe) {
787
+ var node = dom.cloneNode(true);
788
+ dom.parentNode.insertBefore(node, dom);
789
+
790
+ var oldScope = arg.session.scope;
791
+ if (config.scope)
792
+ arg.session.scope += "/" + config.scope + "[" + key + "]";
793
+
794
+ arg.session.mt.bindDom(node, { key: key, value: arr[key] }, fnPool, arg.session);
795
+
796
+ if (config.scope)
797
+ arg.session.scope = oldScope;
798
+ } else {
799
+ children.forEach(function (child) {
800
+ var node = child.cloneNode(true);
801
+ dom.appendChild(node);
802
+
803
+ var oldScope = arg.session.scope;
804
+ if (config.scope)
805
+ arg.session.scope += "/" + config.scope + "[" + key + "]";
806
+
807
+ arg.session.mt.bindDom(node, { key: key, value: arr[key] }, fnPool, arg.session);
808
+
809
+ if (config.scope)
810
+ arg.session.scope = oldScope;
811
+ });
812
+ }
813
+ }
814
+ }
815
+
816
+ if (config.oniterated) {
817
+ var fnOnIterated = findFunctionForDomStyle({ field: config.oniterated, fnPool: fnPool, session: arg.session });
818
+ if (typeof fnOnIterated == "function") {
819
+ try {
820
+ fnOnIterated.call(dom, arr);
821
+ } catch (e) {
822
+ console.error(e);
823
+ }
824
+ }
825
+ }
826
+
827
+
828
+ // clean up
829
+ arg.session.specialData.iterate = parentIterateScope;
830
+ if (config.inject && typeof config.inject == "object") {
831
+ for (var m in config.inject) {
832
+ currentIterateScope[m] = null;
833
+ }
834
+ }
835
+ currentIterateScope.parent = null;
836
+ currentIterateScope = null;
837
+
838
+ if (iterateMe) {
839
+ dom.parentNode.removeChild(dom);
840
+ mediator.stop = true;
841
+ }
842
+
843
+ mediator.skipInner = true;
844
+ }
845
+
846
+ /**
847
+ * @private
848
+ * @method
849
+ * @param {DomFeatureMediator} arg
850
+ */
851
+ function bindDomCond(arg) {
852
+ var config = arg.config;
853
+ var dom = arg.dom;
854
+ var data = arg.data;
855
+ var fnPool = arg.fnPool;
856
+ var mediator = arg.mediator;
857
+
858
+ if (typeof config !== "object")
859
+ return;
860
+
861
+ var r = checkConditions(config, data, fnPool, arg.session, true);
862
+ if (!r) {
863
+ dom.parentNode.removeChild(dom);
864
+ mediator.stop = true;
865
+ mediator.skipInner = true;
866
+ }
867
+
868
+ delete dom.dataset[arg.name];
869
+ }
870
+
871
+ /**
872
+ * @private
873
+ * @method
874
+ * @param {DomFeatureMediator} arg
875
+ */
876
+ function bindDomEvent(arg) {
877
+ var config = arg.config;
878
+ var dom = arg.dom;
879
+
880
+ for (var eventName in config) {
881
+ var fn = findFunctionForDomStyle({ field: config[eventName], fnPool: arg.fnPool, session: arg.session });
882
+ if (fn && arg.fnPool && arg.fnPool.__context) {
883
+ (function (f, eName, context) {
884
+ addEventListener(dom, eName, function () {
885
+ var length = arguments.length;
886
+ arguments.length = length + 1;
887
+ arguments[length] = context;
888
+ f.apply(this, arguments);
889
+ }, false);
890
+ })(fn, eventName, arg.fnPool.__context);
891
+ }
892
+ else
893
+ addEventListener(dom, eventName, fn, false);
894
+ }
895
+
896
+ delete dom.dataset[arg.name];
897
+ }
898
+
899
+
900
+ function bindCallFunction(arg) {
901
+ if (Array.isArray(arg.config)) {
902
+ arg.config.forEach(function (c) {
903
+ callFunction(arg.dom, arg.data, arg.fnPool, c, arg.session);
904
+ });
905
+ } else
906
+ callFunction(arg.dom, arg.data, arg.fnPool, arg.config, arg.session);
907
+
908
+ delete arg.dom.dataset[arg.name];
909
+ }
910
+ // #endregion
911
+
912
+ // #region watch
913
+ var _mediatorWatcher = {};
914
+
915
+ function realtimeWatchHandler() {
916
+ var value = this.value || "";
917
+
918
+ var watcherConfig = _mediatorWatcher[this.__mtWatchName];
919
+ if (!watcherConfig)
920
+ return;
921
+
922
+ var orphans = null;
923
+ watcherConfig.realtime.forEach(function (watcherId) {
924
+ var dom = document.getElementById(watcherId);
925
+ if (!dom) {
926
+ if (!orphans)
927
+ orphans = [];
928
+ orphans.push(dom);
929
+ return;
930
+ }
931
+ bindDomAttr({
932
+ dom: dom,
933
+ config: dom.__mtWatcherConfig.attr,
934
+ name: "watchAttr",
935
+ data: {
936
+ value: value
937
+ },
938
+ fnPool: dom.__mtWatcherFnPool,
939
+ mediator: {}
940
+ });
941
+ });
942
+ if (orphans)
943
+ orphans.forEach(function (removedId) {
944
+ var idx;
945
+ while ((idx = watcherConfig.realtime.indexOf(removedId)) !== -1) {
946
+ watcherConfig.normal.splice(idx, 1);
947
+ }
948
+ });
949
+ }
950
+
951
+ function watchHandler() {
952
+ var value = this.nodeName === "INPUT" && (this.type === "checkbox" || this.type === "radio") && !this.checked ? null : this.value;
953
+
954
+ var watcherConfig = _mediatorWatcher[this.__mtWatchName];
955
+ if (!watcherConfig)
956
+ return;
957
+
958
+ var orphans = null;
959
+ watcherConfig.normal.forEach(function (watcherId) {
960
+ var dom = document.getElementById(watcherId);
961
+ if (!dom) {
962
+ if (!orphans)
963
+ orphans = [];
964
+ orphans.push(dom);
965
+ return;
966
+ }
967
+ bindDomAttr({
968
+ dom: dom,
969
+ config: dom.__mtWatcherConfig.attr,
970
+ name: "watchAttr",
971
+ data: {
972
+ value: value
973
+ },
974
+ fnPool: dom.__mtWatcherFnPool,
975
+ mediator: {}
976
+ });
977
+ });
978
+ if (orphans)
979
+ orphans.forEach(function (removedId) {
980
+ var idx;
981
+ while ((idx = watcherConfig.normal.indexOf(removedId)) !== -1) {
982
+ watcherConfig.normal.splice(idx, 1);
983
+ }
984
+ });
985
+ }
986
+
987
+ var _watcherIdIndexSeed = 0;
988
+
989
+ /**
990
+ * @private
991
+ * @method
992
+ * @param {DomFeatureMediator} arg
993
+ */
994
+ function mtWatch(arg) {
995
+ var name = arg.session.scope + "#" + arg.config.name;
996
+ var watcherConfig = _mediatorWatcher[name];
997
+ if (!watcherConfig) {
998
+ _mediatorWatcher[name] = watcherConfig = {
999
+ realtime: [],
1000
+ normal: []
1001
+ };
1002
+ }
1003
+
1004
+ var id = arg.dom.id;
1005
+ if (!id) {
1006
+ arg.dom.id = id = "__mtWatcher_" + _watcherIdIndexSeed;
1007
+ _watcherIdIndexSeed++;
1008
+ }
1009
+
1010
+ watcherConfig[arg.config.realtime ? "realtime" : "normal"].push(id);
1011
+
1012
+ arg.dom.__mtWatcherConfig = arg.config;
1013
+ arg.dom.__mtWatcherFnPool = arg.fnPool;
1014
+ //arg.dom.dataset.watchPath = name;
1015
+ delete arg.dom.dataset[arg.name];
1016
+ }
1017
+
1018
+ /**
1019
+ * @private
1020
+ * @method
1021
+ * @param {DomFeatureMediator} arg
1022
+ */
1023
+ function mtDefineWatchee(arg) {
1024
+ var nodeName = arg.dom.nodeName;
1025
+ if (nodeName !== "INPUT" && nodeName !== "SELECT" && nodeName !== "TEXTAREA") {
1026
+ dom.dataset[arg.name + "Error"] = "Cannot define watchee in this element";
1027
+ return;
1028
+ }
1029
+
1030
+ var name = arg.session.scope + "#" + arg.config.name;
1031
+
1032
+ arg.dom.__mtWatchName = name;
1033
+ //arg.dom.dataset.watchPath = name;
1034
+
1035
+ if (this.nodeName === "INPUT" && (this.type === "checkbox" || this.type === "radio")) {
1036
+ addEventListener(arg.dom, "click", realtimeWatchHandler, false);
1037
+ addEventListener(arg.dom, "click", watchHandler, false);
1038
+ }
1039
+ else if (nodeName === "SELECT") {
1040
+ addEventListener(arg.dom, "change", realtimeWatchHandler, false);
1041
+ addEventListener(arg.dom, "change", watchHandler, false);
1042
+ }
1043
+ else {
1044
+ addEventListener(arg.dom, "keyup", realtimeWatchHandler, false);
1045
+ addEventListener(arg.dom, "change", watchHandler, false);
1046
+ }
1047
+ delete arg.dom.dataset[arg.name];
1048
+ }
1049
+ // #endregion
1050
+
1051
+ // #region check
1052
+ var _mediatorChecker = {};
1053
+
1054
+ var _validationDefaultMessages = {
1055
+ "%!required%": "Required",
1056
+ "%!maxLength%": "Too long",
1057
+ "%!minLength%": "Too short",
1058
+ "%!min%": "Too small",
1059
+ "%!max%": "Too big",
1060
+ "%!pattern%": "Invalid"
1061
+ }
1062
+
1063
+ function checkHandler(e) {
1064
+ var value = this.nodeName === "INPUT" && (this.type === "checkbox" || this.type === "radio") && !this.checked ? "" : this.value || "";
1065
+
1066
+ var messageIds = _mediatorChecker[this.__mtCheckName];
1067
+ if (!messageIds)
1068
+ return;
1069
+
1070
+ var mtCheckConfig = this.__mtCheckConfig;
1071
+
1072
+ var errorMsg = "";
1073
+ if (!errorMsg && mtCheckConfig.required && !value) {
1074
+ errorMsg = mtCheckConfig.requiredMessage || "%!required%";
1075
+ }
1076
+
1077
+ if (!errorMsg && "maxLength" in mtCheckConfig && value !== "" && value.length > mtCheckConfig.maxLength) {
1078
+ errorMsg = mtCheckConfig.maxLengthMessage || "%!maxLength%";
1079
+ }
1080
+
1081
+ if (!errorMsg && "minLength" in mtCheckConfig && value !== "" && value.length > mtCheckConfig.minLength) {
1082
+ errorMsg = mtCheckConfig.minLengthMessage || "%!minLength%";
1083
+ }
1084
+
1085
+ if (!errorMsg && "min" in mtCheckConfig && value !== "" && value < mtCheckConfig.min) {
1086
+ errorMsg = mtCheckConfig.minMessage || "%!min%";
1087
+ }
1088
+
1089
+ if (!errorMsg && "max" in mtCheckConfig && value !== "" && value < mtCheckConfig.max) {
1090
+ errorMsg = mtCheckConfig.maxMessage || "%!max%";
1091
+ }
1092
+
1093
+ if (!errorMsg && "pattern" in mtCheckConfig && value !== "" && !value.match(new RegExp(mtCheckConfig.pattern, mtCheckConfig.patternOptions))) {
1094
+ errorMsg = mtCheckConfig.patternMessage || "%!pattern%";
1095
+ }
1096
+
1097
+ if (!errorMsg && "complex" in mtCheckConfig && value !== "") {
1098
+
1099
+ }
1100
+
1101
+ if (!errorMsg && "custom" in mtCheckConfig && value !== "") {
1102
+ var fn = findFunctionForDomStyle({ field: mtCheckConfig.custom, fnPool: this.__mtCheckFnPool });
1103
+ if (fn) {
1104
+ var s = fn(value, this, e);
1105
+ if (s && typeof s == "string")
1106
+ errorMsg = s;
1107
+ }
1108
+ }
1109
+
1110
+ var orphans = null;
1111
+ messageIds.forEach(function (messageId) {
1112
+ var dom = document.getElementById(messageId);
1113
+ if (!dom) {
1114
+ if (!orphans)
1115
+ orphans = [];
1116
+ orphans.push(dom);
1117
+ return;
1118
+ }
1119
+
1120
+ switch (dom.__mtCheckMessageConfig.mode) {
1121
+ case "show-hide":
1122
+ case "showHide":
1123
+ if (errorMsg) {
1124
+ //if (/^%!\w+%$/.test(errorMsg))
1125
+ if (errorMsg in _validationDefaultMessages)
1126
+ dom.innerHTML = dom.__mtCheckMessageOriginHtml;
1127
+ else
1128
+ dom.innerHTML = encodeHtml(errorMsg);
1129
+ dom.style.display = "block";
1130
+ } else {
1131
+ dom.innerHTML = "";
1132
+ dom.style.display = "none";
1133
+ }
1134
+ break;
1135
+ case "toggle-class":
1136
+ case "toggleClass":
1137
+ if (errorMsg) {
1138
+ //if (/^%!\w+%$/.test(errorMsg))
1139
+ if (errorMsg in _validationDefaultMessages)
1140
+ dom.innerHTML = dom.__mtCheckMessageOriginHtml;
1141
+ else
1142
+ dom.innerHTML = encodeHtml(errorMsg);
1143
+
1144
+ if (!dom.className) {
1145
+ dom.className = dom.__mtCheckMessageConfig.toggleClass;
1146
+ break;
1147
+ }
1148
+ var classes = dom.className.split(/\s/);
1149
+ if (classes.indexOf(dom.__mtCheckMessageConfig.toggleClass) === -1) {
1150
+ classes.push(dom.__mtCheckMessageConfig.toggleClass);
1151
+ dom.className = classes.join(" ");
1152
+ }
1153
+ } else {
1154
+ dom.innerHTML = "";
1155
+
1156
+ if (!dom.className)
1157
+ break;
1158
+
1159
+ var classes = dom.className.split(/\s/);
1160
+ for (var i = classes.length - 1; i > 0; i--) {
1161
+ if (classes[i] === dom.__mtCheckMessageConfig.toggleClass)
1162
+ classes.splice(i, 1);
1163
+ }
1164
+
1165
+ dom.className = classes.join(" ");
1166
+ }
1167
+ break;
1168
+ default:
1169
+ var message = _validationDefaultMessages[errorMsg] || errorMsg;
1170
+ dom.innerHTML = message ? encodeHtml(message) : "";
1171
+ break;
1172
+ }
1173
+ });
1174
+
1175
+ if (orphans)
1176
+ orphans.forEach(function (removedId) {
1177
+ var idx;
1178
+ while ((idx = messageIds.indexOf(removedId)) !== -1) {
1179
+ messageIds.splice(idx, 1);
1180
+ }
1181
+ });
1182
+ }
1183
+
1184
+ var _checkerIdIndexSeed = 0;
1185
+
1186
+ /**
1187
+ * @private
1188
+ * @method
1189
+ * @param {DomFeatureMediator} arg
1190
+ */
1191
+ function mtCheck(arg) {
1192
+
1193
+ var nodeName = arg.dom.nodeName;
1194
+ if (nodeName !== "INPUT" && nodeName !== "SELECT" && nodeName !== "TEXTAREA") {
1195
+ arg.dom.dataset[arg.name + "Error"] = "Cannot check this element";
1196
+ return;
1197
+ }
1198
+
1199
+ var name = arg.session.scope + "#" + arg.config.name;
1200
+
1201
+ arg.dom.__mtCheckName = name;
1202
+ arg.dom.__mtCheckConfig = arg.config;
1203
+ arg.dom.__mtCheckFnPool = arg.fnPool;
1204
+
1205
+ addEventListener(arg.dom, this.nodeName === "INPUT" && (this.type === "checkbox" || this.type === "radio") ? "click" : "change", checkHandler, false);
1206
+ addEventListener(arg.dom, "blur", checkHandler, false);
1207
+
1208
+ delete arg.dom.dataset[arg.name];
1209
+ }
1210
+
1211
+ /**
1212
+ * @private
1213
+ * @method
1214
+ * @param {DomFeatureMediator} arg
1215
+ */
1216
+ function mtCheckMessage(arg) {
1217
+ var name = arg.session.scope + "#" + arg.config.name;
1218
+ var checkerConfig = _mediatorChecker[name];
1219
+ if (!checkerConfig) {
1220
+ _mediatorChecker[name] = checkerConfig = [];
1221
+ }
1222
+
1223
+ var id = arg.dom.id;
1224
+ if (!id) {
1225
+ arg.dom.id = id = "__mtCheckMessage_" + _checkerIdIndexSeed;
1226
+ _checkerIdIndexSeed++;
1227
+ }
1228
+
1229
+ checkerConfig.push(id);
1230
+
1231
+ arg.dom.__mtCheckMessageConfig = arg.config;
1232
+ arg.dom.__mtCheckMessageOriginHtml = arg.dom.innerHTML;
1233
+ delete arg.dom.dataset[arg.name];
1234
+ }
1235
+ // #endregion
1236
+
1237
+ // chỉ có ý nghĩa để cho bên ngoài sử dụng, bên trong thư viện này không dùng tham chiếu builtinFeatures mà dùng trực tiếp các hàm
1238
+ mt.builtinFeatures = {
1239
+ mtCond: bindDomCond,
1240
+ mtIterate: bindDomIterate,
1241
+ mtCall: bindCallFunction,
1242
+ mtAttr: bindDomAttr,
1243
+ mtEvent: bindDomEvent,
1244
+ mtWatch: mtWatch,
1245
+ mtDefineWatchee: mtDefineWatchee,
1246
+ mtCheck: mtCheck,
1247
+ mtCheckMessage: mtCheckMessage
1248
+ };
1249
+ mt.fnUtils = {
1250
+ findValueByPath: findValueByPath,
1251
+ findDataForDomStyle: findDataForDomStyle,
1252
+ findFunctionForDomStyle: findFunctionForDomStyle,
1253
+ findValueForDomStyle: findValueForDomStyle,
1254
+ mergeData: mergeData,
1255
+ fieldComparer: fieldComparer,
1256
+ multiFieldComparer: multiFieldComparer,
1257
+ checkCondition: checkCondition,
1258
+ checkConditions: checkConditions,
1259
+ addEventListener: addEventListener
1260
+ };
1261
+ if (typeof formatValue == "function")
1262
+ mt.fnUtils.formatValue = formatValue;
1263
+
1264
+
1265
+ for (var feature in mt.builtinFeatures) {
1266
+ mt.registerDomFeature(feature, mt.builtinFeatures[feature]);
1267
+ }
1268
+ })();