mushroom-cli 1.1.1 → 1.1.2

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