mushroom-cli 1.1.2 → 1.2.0-alpha.10

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