UNPKG

229 kBJavaScriptView Raw
1/* fb-core.js | 2019-07-22T19:06:13+08:00 | lincong1987@gmail.com */
2
3/*@add(fb-polyfill)*/
4
5
6(function (global, factory) {
7 typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
8 typeof define === 'function' && define.amd ? define(['exports'], factory) :
9 (global = global || self, factory(global.FireBird = {}));
10}(this, function (exports) { 'use strict';
11
12 var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
13
14 function createCommonjsModule(fn, module) {
15 return module = { exports: {} }, fn(module, module.exports), module.exports;
16 }
17
18 var yox = createCommonjsModule(function (module, exports) {
19 /**
20 * yox.js v1.0.0-alpha.95
21 * (c) 2017-2019 musicode
22 * Released under the MIT License.
23 */
24 (function (global, factory) {
25 module.exports = factory();
26 })(commonjsGlobal, function () {
27
28 var SYNTAX_IF = '#if';
29 var SYNTAX_ELSE = 'else';
30 var SYNTAX_ELSE_IF = 'else if';
31 var SYNTAX_EACH = '#each';
32 var SYNTAX_PARTIAL = '#partial';
33 var SYNTAX_IMPORT = '>';
34 var SYNTAX_SPREAD = '...';
35 var SYNTAX_COMMENT = /^!\s/;
36 var SLOT_DATA_PREFIX = '$slot_';
37 var SLOT_NAME_DEFAULT = 'children';
38 var HINT_STRING = 1;
39 var HINT_NUMBER = 2;
40 var HINT_BOOLEAN = 3;
41 var DIRECTIVE_ON = 'on';
42 var DIRECTIVE_LAZY = 'lazy';
43 var DIRECTIVE_MODEL = 'model';
44 var DIRECTIVE_EVENT = 'event';
45 var DIRECTIVE_BINDING = 'binding';
46 var DIRECTIVE_CUSTOM = 'o';
47 var MODIFER_NATIVE = 'native';
48 var MODEL_PROP_DEFAULT = 'value';
49 var NAMESPACE_HOOK = '.hook';
50 var HOOK_BEFORE_CREATE = 'beforeCreate';
51 var HOOK_AFTER_CREATE = 'afterCreate';
52 var HOOK_BEFORE_MOUNT = 'beforeMount';
53 var HOOK_AFTER_MOUNT = 'afterMount';
54 var HOOK_BEFORE_UPDATE = 'beforeUpdate';
55 var HOOK_AFTER_UPDATE = 'afterUpdate';
56 var HOOK_BEFORE_DESTROY = 'beforeDestroy';
57 var HOOK_AFTER_DESTROY = 'afterDestroy';
58 /**
59 * 为了压缩,定义的常量
60 */
61
62 var TRUE = true;
63 var FALSE = false;
64 var NULL = null;
65 var UNDEFINED;
66 var MINUS_ONE = -1;
67 var RAW_TRUE = 'true';
68 var RAW_FALSE = 'false';
69 var RAW_NULL = 'null';
70 var RAW_UNDEFINED = 'undefined';
71 var RAW_KEY = 'key';
72 var RAW_REF = 'ref';
73 var RAW_SLOT = 'slot';
74 var RAW_NAME = 'name';
75 var RAW_FILTER = 'filter';
76 var RAW_PARTIAL = 'partial';
77 var RAW_COMPONENT = 'component';
78 var RAW_DIRECTIVE = 'directive';
79 var RAW_TRANSITION = 'transition';
80 var RAW_THIS = 'this';
81 var RAW_VALUE = 'value';
82 var RAW_LENGTH = 'length';
83 var RAW_FUNCTION = 'function';
84 var RAW_TEMPLATE = 'template';
85 var RAW_WILDCARD = '*';
86 var RAW_DOT = '.';
87 var RAW_SLASH = '/';
88 var KEYPATH_PARENT = '..';
89 var KEYPATH_CURRENT = RAW_THIS;
90 /**
91 * Single instance for window in browser
92 */
93
94 var WINDOW = typeof window !== RAW_UNDEFINED ? window : UNDEFINED;
95 /**
96 * Single instance for document in browser
97 */
98
99 var DOCUMENT = typeof document !== RAW_UNDEFINED ? document : UNDEFINED;
100 /**
101 * Single instance for global in nodejs or browser
102 */
103
104 var GLOBAL = typeof commonjsGlobal !== RAW_UNDEFINED ? commonjsGlobal : WINDOW;
105 /**
106 * tap 事件
107 *
108 * 非常有用的抽象事件,比如 pc 端是 click 事件,移动端是 touchend 事件
109 *
110 * 这样只需 on-tap="handler" 就可以完美兼容各端
111 *
112 * 框架未实现此事件,通过 Yox.dom.addSpecialEvent 提供给外部扩展
113 *
114 */
115
116 var EVENT_TAP = 'tap';
117 /**
118 * 点击事件
119 */
120
121 var EVENT_CLICK = 'click';
122 /**
123 * 输入事件
124 */
125
126 var EVENT_INPUT = 'input';
127 /**
128 * 变化事件
129 */
130
131 var EVENT_CHANGE = 'change';
132 /**
133 * 唯一内置的特殊事件:model
134 */
135
136 var EVENT_MODEL = 'model';
137 /**
138 * Single instance for noop function
139 */
140
141 var EMPTY_FUNCTION = function () {};
142 /**
143 * 空对象,很多地方会用到,比如 `a || EMPTY_OBJECT` 确保是个对象
144 */
145
146
147 var EMPTY_OBJECT = {};
148 /**
149 * 空数组
150 */
151
152 var EMPTY_ARRAY = [];
153 /**
154 * 空字符串
155 */
156
157 var EMPTY_STRING = '';
158
159 function isDef(target) {
160 return target !== UNDEFINED;
161 }
162
163 function isUndef(target) {
164 return target === UNDEFINED;
165 }
166 /**
167 * Check if value is a function.
168 *
169 * @param value
170 * @return
171 */
172
173
174 function func(value) {
175 return typeof value === RAW_FUNCTION;
176 }
177 /**
178 * Check if value is an array.
179 *
180 * @param value
181 * @return
182 */
183
184
185 function array(value) {
186 return Array.isArray(value);
187 }
188 /**
189 * Check if value is an object.
190 *
191 * @param value
192 * @return
193 */
194
195
196 function object(value) {
197 // 低版本 IE 会把 null 当作 object
198 return value !== NULL && typeof value === 'object';
199 }
200 /**
201 * Check if value is a string.
202 *
203 * @param value
204 * @return
205 */
206
207
208 function string(value) {
209 return typeof value === 'string';
210 }
211 /**
212 * Check if value is a number.
213 *
214 * @param value
215 * @return
216 */
217
218
219 function number(value) {
220 return typeof value === 'number';
221 }
222 /**
223 * Check if value is boolean.
224 *
225 * @param value
226 * @return
227 */
228
229
230 function boolean(value) {
231 return typeof value === 'boolean';
232 }
233 /**
234 * Check if value is numeric.
235 *
236 * @param value
237 * @return
238 */
239
240
241 function numeric(value) {
242 return number(value) || string(value) && !isNaN(parseFloat(value)) && isFinite(value);
243 }
244
245 var is =
246 /*#__PURE__*/
247 {
248 func: func,
249 array: array,
250 object: object,
251 string: string,
252 number: number,
253 "boolean": boolean,
254 numeric: numeric
255 };
256 /**
257 * 任性地执行一个函数,不管它有没有、是不是
258 *
259 * @param fn 调用的函数
260 * @param context 执行函数时的 this 指向
261 * @param args 调用函数的参数,多参数时传入数组
262 * @return 调用函数的返回值
263 */
264
265 function execute(fn, context, args) {
266 if (func(fn)) {
267 return array(args) ? fn.apply(context, args) : isDef(context) ? fn.call(context, args) : isDef(args) ? fn(args) : fn();
268 }
269 }
270
271 var CustomEvent =
272 /** @class */
273 function () {
274 /**
275 * 构造函数
276 *
277 * 可以传事件名称,也可以传原生事件对象
278 */
279 function CustomEvent(type, originalEvent) {
280 // 这里不设置命名空间
281 // 因为有没有命名空间取决于 Emitter 的构造函数有没有传 true
282 // CustomEvent 自己无法决定
283 this.type = type;
284 this.phase = CustomEvent.PHASE_CURRENT;
285
286 if (originalEvent) {
287 this.originalEvent = originalEvent;
288 }
289 }
290 /**
291 * 阻止事件的默认行为
292 */
293
294
295 CustomEvent.prototype.preventDefault = function () {
296 var instance = this;
297
298 if (!instance.isPrevented) {
299 var originalEvent = instance.originalEvent;
300
301 if (originalEvent) {
302 originalEvent.preventDefault();
303 }
304
305 instance.isPrevented = TRUE;
306 }
307
308 return instance;
309 };
310 /**
311 * 停止事件广播
312 */
313
314
315 CustomEvent.prototype.stopPropagation = function () {
316 var instance = this;
317
318 if (!instance.isStoped) {
319 var originalEvent = instance.originalEvent;
320
321 if (originalEvent) {
322 originalEvent.stopPropagation();
323 }
324
325 instance.isStoped = TRUE;
326 }
327
328 return instance;
329 };
330
331 CustomEvent.prototype.prevent = function () {
332 return this.preventDefault();
333 };
334
335 CustomEvent.prototype.stop = function () {
336 return this.stopPropagation();
337 };
338
339 CustomEvent.PHASE_CURRENT = 0;
340 CustomEvent.PHASE_UPWARD = 1;
341 CustomEvent.PHASE_DOWNWARD = MINUS_ONE;
342 return CustomEvent;
343 }();
344 /**
345 * 遍历数组
346 *
347 * @param array
348 * @param callback 返回 false 可停止遍历
349 * @param reversed 是否逆序遍历
350 */
351
352
353 function each(array, callback, reversed) {
354 var length = array.length;
355
356 if (length) {
357 if (reversed) {
358 for (var i = length - 1; i >= 0; i--) {
359 if (callback(array[i], i) === FALSE) {
360 break;
361 }
362 }
363 } else {
364 for (var i = 0; i < length; i++) {
365 if (callback(array[i], i) === FALSE) {
366 break;
367 }
368 }
369 }
370 }
371 }
372
373 function nativePush(array, item) {
374 array[array.length] = item;
375 }
376
377 function nativeUnshift(array, item) {
378 array.unshift(item);
379 }
380 /**
381 * 添加
382 *
383 * @param array
384 * @param value
385 * @param action
386 */
387
388
389 function addItem(array$1, value, action) {
390 if (array(value)) {
391 each(value, function (item) {
392 action(array$1, item);
393 });
394 } else {
395 action(array$1, value);
396 }
397 }
398 /**
399 * 往后加
400 *
401 * @param array
402 * @param target
403 */
404
405
406 function push(array, target) {
407 addItem(array, target, nativePush);
408 }
409 /**
410 * 往前加
411 *
412 * @param array
413 * @param target
414 */
415
416
417 function unshift(array, target) {
418 addItem(array, target, nativeUnshift);
419 }
420 /**
421 * 数组项在数组中的位置
422 *
423 * @param array 数组
424 * @param target 数组项
425 * @param strict 是否全等判断,默认是全等
426 * @return 如果未找到,返回 -1
427 */
428
429
430 function indexOf(array, target, strict) {
431 var result = MINUS_ONE;
432 each(array, function (item, index) {
433 if (strict === FALSE ? item == target : item === target) {
434 result = index;
435 return FALSE;
436 }
437 });
438 return result;
439 }
440 /**
441 * 获取数组最后一项
442 *
443 * @param array 数组
444 * @return
445 */
446
447
448 function last(array) {
449 var length = array.length;
450
451 if (length > 0) {
452 return array[length - 1];
453 }
454 }
455 /**
456 * 弹出数组最后一项
457 *
458 * 项目里用的太多,仅用于节省字符...
459 *
460 * @param array 数组
461 * @return 弹出的数组项
462 */
463
464
465 function pop(array) {
466 var length = array.length;
467
468 if (length > 0) {
469 return array.pop();
470 }
471 }
472 /**
473 * 删除数组项
474 *
475 * @param array 数组
476 * @param item 待删除项
477 * @param strict 是否全等判断,默认是全等
478 * @return 删除的数量
479 */
480
481
482 function remove(array, target, strict) {
483 var result = 0;
484 each(array, function (item, index) {
485 if (strict === FALSE ? item == target : item === target) {
486 array.splice(index, 1);
487 result++;
488 }
489 }, TRUE);
490 return result;
491 }
492 /**
493 * 数组是否包含 item
494 *
495 * @param array 数组
496 * @param target 可能包含的数组项
497 * @param strict 是否全等判断,默认是全等
498 * @return
499 */
500
501
502 function has(array, target, strict) {
503 return indexOf(array, target, strict) >= 0;
504 }
505 /**
506 * 把类数组转成数组
507 *
508 * @param array 类数组
509 * @return
510 */
511
512
513 function toArray(array$1) {
514 return array(array$1) ? array$1 : execute(EMPTY_ARRAY.slice, array$1);
515 }
516 /**
517 * 把数组转成对象
518 *
519 * @param array 数组
520 * @param key 数组项包含的字段名称,如果数组项是基本类型,可不传
521 * @param value
522 * @return
523 */
524
525
526 function toObject(array, key, value) {
527 var result = {};
528 each(array, function (item) {
529 result[key ? item[key] : item] = value || item;
530 });
531 return result;
532 }
533 /**
534 * 把数组合并成字符串
535 *
536 * @param array
537 * @param separator
538 * @return
539 */
540
541
542 function join(array, separator) {
543 return array.join(separator);
544 }
545 /**
546 * 用于判断长度大于 0 的数组
547 *
548 * @param array
549 * @return
550 */
551
552
553 function falsy(array$1) {
554 return !array(array$1) || !array$1.length;
555 }
556
557 var array$1 =
558 /*#__PURE__*/
559 {
560 each: each,
561 push: push,
562 unshift: unshift,
563 indexOf: indexOf,
564 last: last,
565 pop: pop,
566 remove: remove,
567 has: has,
568 toArray: toArray,
569 toObject: toObject,
570 join: join,
571 falsy: falsy
572 };
573 var camelizePattern = /-([a-z])/gi,
574 hyphenatePattern = /\B([A-Z])/g,
575 capitalizePattern = /^[a-z]/,
576 camelizeCache = {},
577 hyphenateCache = {},
578 capitalizeCache = {};
579 /**
580 * 连字符转成驼峰
581 *
582 * @param str
583 * @return 驼峰格式的字符串
584 */
585
586 function camelize(str) {
587 if (!camelizeCache[str]) {
588 camelizeCache[str] = str.replace(camelizePattern, function ($0, $1) {
589 return upper($1);
590 });
591 }
592
593 return camelizeCache[str];
594 }
595 /**
596 * 驼峰转成连字符
597 *
598 * @param str
599 * @return 连字符格式的字符串
600 */
601
602
603 function hyphenate(str) {
604 if (!hyphenateCache[str]) {
605 hyphenateCache[str] = str.replace(hyphenatePattern, function ($0, $1) {
606 return '-' + lower($1);
607 });
608 }
609
610 return hyphenateCache[str];
611 }
612 /**
613 * 首字母大写
614 *
615 * @param str
616 * @return
617 */
618
619
620 function capitalize(str) {
621 if (!capitalizeCache[str]) {
622 capitalizeCache[str] = str.replace(capitalizePattern, upper);
623 }
624
625 return capitalizeCache[str];
626 }
627 /**
628 * 清除两侧空白符
629 *
630 * @param str
631 * @return 清除两侧空白符的字符串
632 */
633
634
635 function trim(str) {
636 return falsy$1(str) ? EMPTY_STRING : str.trim();
637 }
638 /**
639 * 截取字符串
640 *
641 * @param str
642 * @param start
643 * @param end
644 * @return
645 */
646
647
648 function slice(str, start, end) {
649 return number(end) ? start === end ? EMPTY_STRING : str.slice(start, end) : str.slice(start);
650 }
651 /**
652 * 获取子串的起始位置
653 *
654 * @param str
655 * @param part
656 * @param start
657 * @return
658 */
659
660
661 function indexOf$1(str, part, start) {
662 return str.indexOf(part, isDef(start) ? start : 0);
663 }
664 /**
665 * 获取子串的起始位置
666 *
667 * @param str
668 * @param part
669 * @param end
670 * @return
671 */
672
673
674 function lastIndexOf(str, part, end) {
675 return str.lastIndexOf(part, isDef(end) ? end : str.length);
676 }
677 /**
678 * str 是否以 part 开头
679 *
680 * @param str
681 * @param part
682 * @return
683 */
684
685
686 function startsWith(str, part) {
687 return indexOf$1(str, part) === 0;
688 }
689 /**
690 * str 是否以 part 结束
691 *
692 * @param str
693 * @param part
694 * @return
695 */
696
697
698 function endsWith(str, part) {
699 var offset = str.length - part.length;
700 return offset >= 0 && lastIndexOf(str, part) === offset;
701 }
702 /**
703 * 获取某个位置的字符
704 */
705
706
707 function charAt(str, index) {
708 return str.charAt(index || 0);
709 }
710 /**
711 * 获取某个位置的字符编码
712 */
713
714
715 function codeAt(str, index) {
716 return str.charCodeAt(index || 0);
717 }
718 /**
719 * 大写格式
720 */
721
722
723 function upper(str) {
724 return str.toUpperCase();
725 }
726 /**
727 * 小写格式
728 */
729
730
731 function lower(str) {
732 return str.toLowerCase();
733 }
734 /**
735 * str 是否包含 part
736 *
737 * @param str
738 * @param part
739 * @return 是否包含
740 */
741
742
743 function has$1(str, part) {
744 return indexOf$1(str, part) >= 0;
745 }
746 /**
747 * 判断长度大于 0 的字符串
748 *
749 * @param str
750 * @return
751 */
752
753
754 function falsy$1(str) {
755 return !string(str) || !str.length;
756 }
757
758 var string$1 =
759 /*#__PURE__*/
760 {
761 camelize: camelize,
762 hyphenate: hyphenate,
763 capitalize: capitalize,
764 trim: trim,
765 slice: slice,
766 indexOf: indexOf$1,
767 lastIndexOf: lastIndexOf,
768 startsWith: startsWith,
769 endsWith: endsWith,
770 charAt: charAt,
771 codeAt: codeAt,
772 upper: upper,
773 lower: lower,
774 has: has$1,
775 falsy: falsy$1
776 };
777 var dotPattern = /\./g,
778 asteriskPattern = /\*/g,
779 doubleAsteriskPattern = /\*\*/g,
780 splitCache = {},
781 patternCache = {};
782 /**
783 * 判断 keypath 是否以 prefix 开头,如果是,返回匹配上的前缀长度,否则返回 -1
784 *
785 * @param keypath
786 * @param prefix
787 * @return
788 */
789
790 function match(keypath, prefix) {
791 if (keypath === prefix) {
792 return prefix.length;
793 }
794
795 prefix += RAW_DOT;
796 return startsWith(keypath, prefix) ? prefix.length : MINUS_ONE;
797 }
798 /**
799 * 遍历 keypath 的每个部分
800 *
801 * @param keypath
802 * @param callback 返回 false 可中断遍历
803 */
804
805
806 function each$1(keypath, callback) {
807 // 如果 keypath 是 toString 之类的原型字段
808 // splitCache[keypath] 会取到原型链上的对象
809 var list = splitCache.hasOwnProperty(keypath) ? splitCache[keypath] : splitCache[keypath] = keypath.split(RAW_DOT);
810
811 for (var i = 0, lastIndex = list.length - 1; i <= lastIndex; i++) {
812 if (callback(list[i], i === lastIndex) === FALSE) {
813 break;
814 }
815 }
816 }
817 /**
818 * 遍历 keypath 的每个部分
819 *
820 * @param keypath1
821 * @param keypath2
822 */
823
824
825 function join$1(keypath1, keypath2) {
826 return keypath1 && keypath2 ? keypath1 + RAW_DOT + keypath2 : keypath1 || keypath2;
827 }
828 /**
829 * 是否模糊匹配
830 *
831 * @param keypath
832 */
833
834
835 function isFuzzy(keypath) {
836 return has$1(keypath, RAW_WILDCARD);
837 }
838 /**
839 * 模糊匹配 keypath
840 *
841 * @param keypath
842 * @param pattern
843 */
844
845
846 function matchFuzzy(keypath, pattern) {
847 var cache = patternCache[pattern];
848
849 if (!cache) {
850 var str = pattern.replace(dotPattern, '\\.').replace(asteriskPattern, '(\\w+)').replace(doubleAsteriskPattern, '([\.\\w]+?)');
851 cache = patternCache[pattern] = new RegExp("^" + str + "$");
852 }
853
854 var result = keypath.match(cache);
855
856 if (result) {
857 return result[1];
858 }
859 }
860 /**
861 * 全局 value holder,避免频繁的创建临时对象
862 */
863
864
865 var holder = {
866 value: UNDEFINED
867 };
868 /**
869 * 获取对象的 key 的数组
870 *
871 * @param object
872 * @return
873 */
874
875 function keys(object) {
876 return Object.keys(object);
877 }
878
879 function sortKeyByAsc(a, b) {
880 return a.length - b.length;
881 }
882
883 function sortKeyByDesc(a, b) {
884 return b.length - a.length;
885 }
886 /**
887 * 排序对象的 key
888 *
889 * @param object
890 * @param desc 是否逆序,默认从小到大排序
891 * @return
892 */
893
894
895 function sort(object, desc) {
896 return keys(object).sort(desc ? sortKeyByDesc : sortKeyByAsc);
897 }
898 /**
899 * 遍历对象
900 *
901 * @param object
902 * @param callback 返回 false 可停止遍历
903 */
904
905
906 function each$2(object, callback) {
907 for (var key in object) {
908 if (callback(object[key], key) === FALSE) {
909 break;
910 }
911 }
912 }
913 /**
914 * 清空对象所有的键值对
915 *
916 * @param object
917 */
918
919
920 function clear(object) {
921 each$2(object, function (_, key) {
922 delete object[key];
923 });
924 }
925 /**
926 * 扩展对象
927 *
928 * @return
929 */
930
931
932 function extend(original, object) {
933 each$2(object, function (value, key) {
934 original[key] = value;
935 });
936 return original;
937 }
938 /**
939 * 合并对象
940 *
941 * @return
942 */
943
944
945 function merge(object1, object2) {
946 return object1 && object2 ? extend(extend({}, object1), object2) : object1 || object2;
947 }
948 /**
949 * 拷贝对象
950 *
951 * @param object
952 * @param deep 是否需要深拷贝
953 * @return
954 */
955
956
957 function copy(object$1, deep) {
958 var result = object$1;
959
960 if (array(object$1)) {
961 if (deep) {
962 result = [];
963 each(object$1, function (item, index) {
964 result[index] = copy(item, deep);
965 });
966 } else {
967 result = object$1.slice();
968 }
969 } else if (object(object$1)) {
970 result = {};
971 each$2(object$1, function (value, key) {
972 result[key] = deep ? copy(value, deep) : value;
973 });
974 }
975
976 return result;
977 }
978 /**
979 * 从对象中查找一个 keypath
980 *
981 * 返回值是空时,表示没找到值
982 *
983 * @param object
984 * @param keypath
985 * @return
986 */
987
988
989 function get(object, keypath) {
990 each$1(keypath, function (key, isLast) {
991 if (object != NULL) {
992 // 先直接取值
993 var value = object[key],
994 // 紧接着判断值是否存在
995 // 下面会处理计算属性的值,不能在它后面设置 hasValue
996 hasValue = isDef(value); // 如果是计算属性,取计算属性的值
997
998 if (value && func(value.get)) {
999 value = value.get();
1000 }
1001
1002 if (isLast) {
1003 if (hasValue) {
1004 holder.value = value;
1005 object = holder;
1006 } else {
1007 object = UNDEFINED;
1008 }
1009 } else {
1010 object = value;
1011 }
1012 } else {
1013 object = UNDEFINED;
1014 return FALSE;
1015 }
1016 });
1017 return object;
1018 }
1019 /**
1020 * 为对象设置一个键值对
1021 *
1022 * @param object
1023 * @param keypath
1024 * @param value
1025 * @param autofill 是否自动填充不存在的对象,默认自动填充
1026 */
1027
1028
1029 function set(object, keypath, value, autofill) {
1030 each$1(keypath, function (key, isLast) {
1031 if (isLast) {
1032 object[key] = value;
1033 } else if (object[key]) {
1034 object = object[key];
1035 } else if (autofill) {
1036 object = object[key] = {};
1037 } else {
1038 return FALSE;
1039 }
1040 });
1041 }
1042 /**
1043 * 对象是否包含某个 key
1044 *
1045 * @param object
1046 * @param key
1047 * @return
1048 */
1049
1050
1051 function has$2(object, key) {
1052 // 不用 hasOwnProperty,性能差
1053 return isDef(object[key]);
1054 }
1055 /**
1056 * 是否是空对象
1057 *
1058 * @param object
1059 * @return
1060 */
1061
1062
1063 function falsy$2(object$1) {
1064 return !object(object$1) || array(object$1) || !keys(object$1).length;
1065 }
1066
1067 var object$1 =
1068 /*#__PURE__*/
1069 {
1070 keys: keys,
1071 sort: sort,
1072 each: each$2,
1073 clear: clear,
1074 extend: extend,
1075 merge: merge,
1076 copy: copy,
1077 get: get,
1078 set: set,
1079 has: has$2,
1080 falsy: falsy$2
1081 };
1082
1083 function toString(target, defaultValue) {
1084 return target != NULL && target.toString ? target.toString() : isDef(defaultValue) ? defaultValue : EMPTY_STRING;
1085 }
1086
1087 var DEBUG = 1;
1088 var INFO = 2;
1089 var WARN = 3;
1090 var ERROR = 4;
1091 var FATAL = 5;
1092 /**
1093 * 是否有原生的日志特性,没有必要单独实现
1094 */
1095
1096 var nativeConsole = typeof console !== RAW_UNDEFINED ? console : NULL,
1097
1098 /**
1099 * 当前是否是源码调试,如果开启了代码压缩,empty function 里的注释会被干掉
1100 * 源码模式默认选 INFO,因为 DEBUG 输出的日志太多,会导致性能急剧下降
1101 */
1102 defaultLogLevel = INFO,
1103 ///yox/.test(toString(EMPTY_FUNCTION)) ? INFO : WARN,
1104
1105 /**
1106 * console 样式前缀
1107 * ie 和 edge 不支持 console.log 样式
1108 */
1109 stylePrefix = WINDOW && /edge|msie|trident/i.test(WINDOW.navigator.userAgent) ? EMPTY_STRING : '%c',
1110
1111 /**
1112 * 日志打印函数
1113 */
1114 printLog = nativeConsole ? stylePrefix ? function (tag, msg, style) {
1115 nativeConsole.log(stylePrefix + tag, style, msg);
1116 } : function (tag, msg) {
1117 nativeConsole.log(tag, msg);
1118 } : EMPTY_FUNCTION;
1119 /**
1120 * 全局调试开关
1121 */
1122
1123 function getLogLevel() {
1124 if (GLOBAL) {
1125 var logLevel = GLOBAL['YOX_LOG_LEVEL'];
1126
1127 if (logLevel >= DEBUG && logLevel <= FATAL) {
1128 return logLevel;
1129 }
1130 }
1131
1132 return defaultLogLevel;
1133 }
1134
1135 function getStyle(backgroundColor) {
1136 return "background-color:" + backgroundColor + ";border-radius:12px;color:#fff;font-size:10px;padding:3px 6px;";
1137 }
1138 /**
1139 * 打印 debug 日志
1140 *
1141 * @param msg
1142 */
1143
1144
1145 function debug(msg, tag) {
1146 if (getLogLevel() <= DEBUG) {
1147 printLog(tag || 'Yox debug', msg, getStyle('#999'));
1148 }
1149 }
1150 /**
1151 * 打印 info 日志
1152 *
1153 * @param msg
1154 */
1155
1156
1157 function info(msg, tag) {
1158 if (getLogLevel() <= INFO) {
1159 printLog(tag || 'Yox info', msg, getStyle('#2db7f5'));
1160 }
1161 }
1162 /**
1163 * 打印 warn 日志
1164 *
1165 * @param msg
1166 */
1167
1168
1169 function warn(msg, tag) {
1170 if (getLogLevel() <= WARN) {
1171 printLog(tag || 'Yox warn', msg, getStyle('#f90'));
1172 }
1173 }
1174 /**
1175 * 打印 error 日志
1176 *
1177 * @param msg
1178 */
1179
1180
1181 function error(msg, tag) {
1182 if (getLogLevel() <= ERROR) {
1183 printLog(tag || 'Yox error', msg, getStyle('#ed4014'));
1184 }
1185 }
1186 /**
1187 * 致命错误,中断程序
1188 *
1189 * @param msg
1190 */
1191
1192
1193 function fatal(msg, tag) {
1194 if (getLogLevel() <= FATAL) {
1195 throw new Error("[" + (tag || 'Yox fatal') + "]: " + msg);
1196 }
1197 }
1198
1199 var logger =
1200 /*#__PURE__*/
1201 {
1202 DEBUG: DEBUG,
1203 INFO: INFO,
1204 WARN: WARN,
1205 ERROR: ERROR,
1206 FATAL: FATAL,
1207 debug: debug,
1208 info: info,
1209 warn: warn,
1210 error: error,
1211 fatal: fatal
1212 };
1213
1214 var Emitter =
1215 /** @class */
1216 function () {
1217 function Emitter(ns) {
1218 this.ns = ns || FALSE;
1219 this.listeners = {};
1220 }
1221 /**
1222 * 发射事件
1223 *
1224 * @param type 事件名称或命名空间
1225 * @param args 事件处理函数的参数列表
1226 * @param filter 自定义过滤器
1227 */
1228
1229
1230 Emitter.prototype.fire = function (type, args, filter) {
1231 var instance = this,
1232 namespace = string(type) ? instance.parse(type) : type,
1233 list = instance.listeners[namespace.type],
1234 isComplete = TRUE;
1235
1236 if (list) {
1237 // 避免遍历过程中,数组发生变化,比如增删了
1238 list = copy(list); // 判断是否是发射事件
1239 // 如果 args 的第一个参数是 CustomEvent 类型,表示发射事件
1240 // 因为事件处理函数的参数列表是 (event, data)
1241
1242 var event_1 = args && args[0] instanceof CustomEvent ? args[0] : UNDEFINED;
1243 each(list, function (options) {
1244 // 命名空间不匹配
1245 if (!matchNamespace(namespace.ns, options) // 在 fire 过程中被移除了
1246 || !has(list, options) // 传了 filter,则用 filter 判断是否过滤此 options
1247 || filter && !filter(namespace, args, options)) {
1248 return;
1249 } // 为 event 对象加上当前正在处理的 listener
1250 // 这样方便业务层移除事件绑定
1251 // 比如 on('xx', function) 这样定义了匿名 listener
1252 // 在这个 listener 里面获取不到当前 listener 的引用
1253 // 为了能引用到,有时候会先定义 var listener = function
1254 // 然后再 on('xx', listener) 这样其实是没有必要的
1255
1256
1257 if (event_1) {
1258 event_1.listener = options.fn;
1259 }
1260
1261 var result = execute(options.fn, options.ctx, args);
1262
1263 if (event_1) {
1264 event_1.listener = UNDEFINED;
1265 } // 执行次数
1266
1267
1268 options.num = options.num ? options.num + 1 : 1; // 注册的 listener 可以指定最大执行次数
1269
1270 if (options.num === options.max) {
1271 instance.off(namespace, options.fn);
1272 } // 如果没有返回 false,而是调用了 event.stop 也算是返回 false
1273
1274
1275 if (event_1) {
1276 if (result === FALSE) {
1277 event_1.prevent().stop();
1278 } else if (event_1.isStoped) {
1279 result = FALSE;
1280 }
1281 }
1282
1283 if (result === FALSE) {
1284 return isComplete = FALSE;
1285 }
1286 });
1287 }
1288
1289 return isComplete;
1290 };
1291 /**
1292 * 注册监听
1293 *
1294 * @param type
1295 * @param listener
1296 */
1297
1298
1299 Emitter.prototype.on = function (type, listener) {
1300 var instance = this,
1301 listeners = instance.listeners,
1302 options = func(listener) ? {
1303 fn: listener
1304 } : listener;
1305
1306 if (object(options) && func(options.fn)) {
1307 var namespace = string(type) ? instance.parse(type) : type;
1308 options.ns = namespace.ns;
1309 push(listeners[namespace.type] || (listeners[namespace.type] = []), options);
1310 } else {
1311 fatal("emitter.on(type, listener) invoke failed\uFF1A\n\n\"listener\" is expected to be a Function or an EmitterOptions.\n");
1312 }
1313 };
1314 /**
1315 * 取消监听
1316 *
1317 * @param type
1318 * @param listener
1319 */
1320
1321
1322 Emitter.prototype.off = function (type, listener) {
1323 var instance = this,
1324 listeners = instance.listeners;
1325
1326 if (type) {
1327 var namespace = string(type) ? instance.parse(type) : type,
1328 name = namespace.type,
1329 ns_1 = namespace.ns,
1330 matchListener_1 = createMatchListener(listener),
1331 each$1 = function (list, name) {
1332 each(list, function (options, index) {
1333 if (matchListener_1(options) && matchNamespace(ns_1, options)) {
1334 list.splice(index, 1);
1335 }
1336 }, TRUE);
1337
1338 if (!list.length) {
1339 delete listeners[name];
1340 }
1341 };
1342
1343 if (name) {
1344 if (listeners[name]) {
1345 each$1(listeners[name], name);
1346 }
1347 } else if (ns_1) {
1348 each$2(listeners, each$1);
1349 } // 在开发阶段进行警告,比如传了 listener 进来,listener 是个空值
1350 // 但你不知道它是空值
1351
1352
1353 {
1354 if (arguments.length > 1 && listener == NULL) {
1355 warn("emitter.off(type, listener) is invoked, but \"listener\" is " + listener + ".");
1356 }
1357 }
1358 } else {
1359 // 清空
1360 instance.listeners = {}; // 在开发阶段进行警告,比如传了 type 进来,type 是个空值
1361 // 但你不知道它是空值
1362
1363 {
1364 if (arguments.length > 0) {
1365 warn("emitter.off(type) is invoked, but \"type\" is " + type + ".");
1366 }
1367 }
1368 }
1369 };
1370 /**
1371 * 是否已监听某个事件
1372 *
1373 * @param type
1374 * @param listener
1375 */
1376
1377
1378 Emitter.prototype.has = function (type, listener) {
1379 var instance = this,
1380 listeners = instance.listeners,
1381 namespace = string(type) ? instance.parse(type) : type,
1382 name = namespace.type,
1383 ns = namespace.ns,
1384 result = TRUE,
1385 matchListener = createMatchListener(listener),
1386 each$1 = function (list) {
1387 each(list, function (options) {
1388 if (matchListener(options) && matchNamespace(ns, options)) {
1389 return result = FALSE;
1390 }
1391 });
1392 return result;
1393 };
1394
1395 if (name) {
1396 if (listeners[name]) {
1397 each$1(listeners[name]);
1398 }
1399 } else if (ns) {
1400 each$2(listeners, each$1);
1401 }
1402
1403 return !result;
1404 };
1405 /**
1406 * 把事件类型解析成命名空间格式
1407 *
1408 * @param type
1409 */
1410
1411
1412 Emitter.prototype.parse = function (type) {
1413 // 这里 ns 必须为字符串
1414 // 用于区分 event 对象是否已完成命名空间的解析
1415 var result = {
1416 type: type,
1417 ns: EMPTY_STRING
1418 }; // 是否开启命名空间
1419
1420 if (this.ns) {
1421 var index = indexOf$1(type, RAW_DOT);
1422
1423 if (index >= 0) {
1424 result.type = slice(type, 0, index);
1425 result.ns = slice(type, index + 1);
1426 }
1427 }
1428
1429 return result;
1430 };
1431
1432 return Emitter;
1433 }();
1434
1435 function matchTrue() {
1436 return TRUE;
1437 }
1438 /**
1439 * 外部会传入 Function 或 EmitterOptions 或 空
1440 *
1441 * 这里根据传入值的不同类型,创建不同的判断函数
1442 *
1443 * 如果传入的是 EmitterOptions,则全等判断
1444 *
1445 * 如果传入的是 Function,则判断函数是否全等
1446 *
1447 * 如果传入的是空,则直接返回 true
1448 *
1449 * @param listener
1450 */
1451
1452
1453 function createMatchListener(listener) {
1454 return func(listener) ? function (options) {
1455 return listener === options.fn;
1456 } : matchTrue;
1457 }
1458 /**
1459 * 判断 options 是否能匹配命名空间
1460 *
1461 * 如果 namespace 和 options.ns 都不为空,则需完全匹配
1462 *
1463 * 如果他们两个其中任何一个为空,则不判断命名空间
1464 *
1465 * @param namespace
1466 * @param options
1467 */
1468
1469
1470 function matchNamespace(namespace, options) {
1471 var ns = options.ns;
1472 return ns && namespace ? ns === namespace : TRUE;
1473 }
1474
1475 function isNative(target) {
1476 return func(target) && has$1(toString(target), '[native code]');
1477 }
1478
1479 var nextTick; // IE (10+) 和 node
1480
1481 if (typeof setImmediate === RAW_FUNCTION && isNative(setImmediate)) {
1482 nextTick = setImmediate;
1483 } // 用 MessageChannel 去做 setImmediate 的 polyfill
1484 // 原理是将新的 message 事件加入到原有的 dom events 之后
1485 // 兼容性 IE10+ 和其他标准浏览器
1486
1487
1488 if (typeof MessageChannel === RAW_FUNCTION && isNative(MessageChannel)) {
1489 nextTick = function (fn) {
1490 var channel = new MessageChannel();
1491 channel.port1.onmessage = fn;
1492 channel.port2.postMessage(1);
1493 };
1494 } else {
1495 nextTick = setTimeout;
1496 }
1497
1498 var nextTick$1 = nextTick;
1499 var shared;
1500
1501 var NextTask =
1502 /** @class */
1503 function () {
1504 function NextTask() {
1505 this.tasks = [];
1506 }
1507 /**
1508 * 全局单例
1509 */
1510
1511
1512 NextTask.shared = function () {
1513 return shared || (shared = new NextTask());
1514 };
1515 /**
1516 * 在队尾添加异步任务
1517 */
1518
1519
1520 NextTask.prototype.append = function (func, context) {
1521 var instance = this,
1522 tasks = instance.tasks;
1523 push(tasks, {
1524 fn: func,
1525 ctx: context
1526 });
1527
1528 if (tasks.length === 1) {
1529 nextTick$1(function () {
1530 instance.run();
1531 });
1532 }
1533 };
1534 /**
1535 * 在队首添加异步任务
1536 */
1537
1538
1539 NextTask.prototype.prepend = function (func, context) {
1540 var instance = this,
1541 tasks = instance.tasks;
1542 unshift(tasks, {
1543 fn: func,
1544 ctx: context
1545 });
1546
1547 if (tasks.length === 1) {
1548 nextTick$1(function () {
1549 instance.run();
1550 });
1551 }
1552 };
1553 /**
1554 * 清空异步队列
1555 */
1556
1557
1558 NextTask.prototype.clear = function () {
1559 this.tasks.length = 0;
1560 };
1561 /**
1562 * 立即执行异步任务,并清空队列
1563 */
1564
1565
1566 NextTask.prototype.run = function () {
1567 var tasks = this.tasks;
1568
1569 if (tasks.length) {
1570 this.tasks = [];
1571 each(tasks, function (task) {
1572 execute(task.fn, task.ctx);
1573 });
1574 }
1575 };
1576
1577 return NextTask;
1578 }();
1579
1580 var guid = 0;
1581
1582 function guid$1() {
1583 return ++guid;
1584 } // vnode.data 内部使用的几个字段
1585
1586
1587 var ID = '$id';
1588 var VNODE = '$vnode';
1589 var LOADING = '$loading';
1590 var COMPONENT = '$component';
1591 var LEAVING = '$leaving';
1592
1593 function update(api, vnode, oldVnode) {
1594 var node = vnode.node,
1595 nativeAttrs = vnode.nativeAttrs,
1596 oldNativeAttrs = oldVnode && oldVnode.nativeAttrs;
1597
1598 if (nativeAttrs || oldNativeAttrs) {
1599 var newValue_1 = nativeAttrs || EMPTY_OBJECT,
1600 oldValue_1 = oldNativeAttrs || EMPTY_OBJECT;
1601 each$2(newValue_1, function (attr, name) {
1602 if (!oldValue_1[name] || attr.value !== oldValue_1[name].value) {
1603 api.attr(node, name, attr.value);
1604 }
1605 });
1606 each$2(oldValue_1, function (_, name) {
1607 if (!newValue_1[name]) {
1608 api.removeAttr(node, name);
1609 }
1610 });
1611 }
1612 }
1613
1614 function update$1(api, vnode, oldVnode) {
1615 var node = vnode.node,
1616 nativeProps = vnode.nativeProps,
1617 oldNativeProps = oldVnode && oldVnode.nativeProps;
1618
1619 if (nativeProps || oldNativeProps) {
1620 var newValue_1 = nativeProps || EMPTY_OBJECT,
1621 oldValue_1 = oldNativeProps || EMPTY_OBJECT;
1622 each$2(newValue_1, function (prop, name) {
1623 if (!oldValue_1[name] || prop.value !== oldValue_1[name].value) {
1624 api.prop(node, name, prop.value);
1625 }
1626 });
1627 each$2(oldValue_1, function (prop, name) {
1628 if (!newValue_1[name]) {
1629 api.removeProp(node, name, prop.hint);
1630 }
1631 });
1632 }
1633 }
1634
1635 function update$2(vnode, oldVnode) {
1636 var data = vnode.data,
1637 directives = vnode.directives,
1638 oldDirectives = oldVnode && oldVnode.directives;
1639
1640 if (directives || oldDirectives) {
1641 var node_1 = data[COMPONENT] || vnode.node,
1642 isKeypathChange_1 = oldVnode && vnode.keypath !== oldVnode.keypath,
1643 newValue_1 = directives || EMPTY_OBJECT,
1644 oldValue_1 = oldDirectives || EMPTY_OBJECT;
1645 each$2(newValue_1, function (directive, name) {
1646 var _a = directive.hooks,
1647 once = _a.once,
1648 bind = _a.bind,
1649 unbind = _a.unbind;
1650
1651 if (!oldValue_1[name]) {
1652 bind(node_1, directive, vnode);
1653 } else if (once || directive.value !== oldValue_1[name].value || isKeypathChange_1) {
1654 if (unbind) {
1655 unbind(node_1, oldValue_1[name], oldVnode);
1656 }
1657
1658 bind(node_1, directive, vnode);
1659 }
1660 });
1661 each$2(oldValue_1, function (directive, name) {
1662 if (!newValue_1[name]) {
1663 var unbind = directive.hooks.unbind;
1664
1665 if (unbind) {
1666 unbind(node_1, directive, oldVnode);
1667 }
1668 }
1669 });
1670 }
1671 }
1672
1673 function remove$1(vnode) {
1674 var directives = vnode.directives;
1675
1676 if (directives) {
1677 var node_2 = vnode.data[COMPONENT] || vnode.node;
1678 each$2(directives, function (directive) {
1679 var unbind = directive.hooks.unbind;
1680
1681 if (unbind) {
1682 unbind(node_2, directive, vnode);
1683 }
1684 });
1685 }
1686 }
1687
1688 function update$3(vnode, oldVnode) {
1689 var data = vnode.data,
1690 ref = vnode.ref,
1691 props = vnode.props,
1692 slots = vnode.slots,
1693 directives = vnode.directives,
1694 context = vnode.context,
1695 node;
1696
1697 if (vnode.isComponent) {
1698 node = data[COMPONENT]; // 更新时才要 set
1699 // 因为初始化时,所有这些都经过构造函数完成了
1700
1701 if (oldVnode) {
1702 var model = directives && directives[DIRECTIVE_MODEL];
1703
1704 if (model) {
1705 if (!props) {
1706 props = {};
1707 }
1708
1709 props[node.$model] = model.value;
1710 }
1711
1712 {
1713 if (props) {
1714 each$2(props, function (value, key) {
1715 node.checkProp(key, value);
1716 });
1717 }
1718 }
1719 var result = merge(props, slots);
1720
1721 if (result) {
1722 node.forceUpdate(result);
1723 }
1724 }
1725 } else {
1726 node = vnode.node;
1727 }
1728
1729 if (ref) {
1730 var refs = context.$refs;
1731
1732 if (refs) {
1733 refs[ref] = node;
1734 }
1735 }
1736 }
1737
1738 function isPatchable(vnode, oldVnode) {
1739 return vnode.tag === oldVnode.tag && vnode.key === oldVnode.key;
1740 }
1741
1742 function createKeyToIndex(vnodes, startIndex, endIndex) {
1743 var result, vnode, key;
1744
1745 while (startIndex <= endIndex) {
1746 vnode = vnodes[startIndex];
1747
1748 if (vnode && (key = vnode.key)) {
1749 if (!result) {
1750 result = {};
1751 }
1752
1753 result[key] = startIndex;
1754 }
1755
1756 startIndex++;
1757 }
1758
1759 return result || EMPTY_OBJECT;
1760 }
1761
1762 function insertBefore(api, parentNode, node, referenceNode) {
1763 if (referenceNode) {
1764 api.before(parentNode, node, referenceNode);
1765 } else {
1766 api.append(parentNode, node);
1767 }
1768 }
1769
1770 function createComponent(vnode, options) {
1771 var child = (vnode.parent || vnode.context).createComponent(options, vnode);
1772 vnode.data[COMPONENT] = child;
1773 vnode.data[LOADING] = FALSE;
1774 update$3(vnode);
1775 update$2(vnode);
1776 return child;
1777 }
1778
1779 function createData() {
1780 var data = {};
1781 data[ID] = guid$1();
1782 return data;
1783 }
1784
1785 function createVnode(api, vnode) {
1786 var tag = vnode.tag,
1787 node = vnode.node,
1788 data = vnode.data,
1789 isComponent = vnode.isComponent,
1790 isComment = vnode.isComment,
1791 isText = vnode.isText,
1792 isStyle = vnode.isStyle,
1793 isOption = vnode.isOption,
1794 children = vnode.children,
1795 text = vnode.text,
1796 html = vnode.html,
1797 context = vnode.context;
1798
1799 if (node && data) {
1800 return;
1801 }
1802
1803 data = createData();
1804 vnode.data = data;
1805
1806 if (isText) {
1807 vnode.node = api.createText(text);
1808 return;
1809 }
1810
1811 if (isComment) {
1812 vnode.node = api.createComment(text);
1813 return;
1814 }
1815
1816 if (isComponent) {
1817 var componentOptions_1 = UNDEFINED; // 动态组件,tag 可能为空
1818
1819 if (tag) {
1820 context.loadComponent(tag, function (options) {
1821 if (has$2(data, LOADING)) {
1822 // 异步组件
1823 if (data[LOADING]) {
1824 // 尝试使用最新的 vnode
1825 if (data[VNODE]) {
1826 vnode = data[VNODE]; // 用完就删掉
1827
1828 delete data[VNODE];
1829 }
1830
1831 enterVnode(vnode, createComponent(vnode, options));
1832 }
1833 } // 同步组件
1834 else {
1835 componentOptions_1 = options;
1836 }
1837 });
1838 } // 不论是同步还是异步组件,都需要一个占位元素
1839
1840
1841 vnode.node = api.createComment(RAW_COMPONENT);
1842
1843 if (componentOptions_1) {
1844 createComponent(vnode, componentOptions_1);
1845 } else {
1846 data[LOADING] = TRUE;
1847 }
1848 } else {
1849 node = vnode.node = api.createElement(vnode.tag, vnode.isSvg);
1850
1851 if (children) {
1852 addVnodes(api, node, children);
1853 } else if (text) {
1854 api.text(node, text, isStyle, isOption);
1855 } else if (html) {
1856 api.html(node, html, isStyle, isOption);
1857 }
1858
1859 update(api, vnode);
1860 update$1(api, vnode);
1861 update$3(vnode);
1862 update$2(vnode);
1863 }
1864 }
1865
1866 function addVnodes(api, parentNode, vnodes, startIndex, endIndex, before) {
1867 var vnode,
1868 start = startIndex || 0,
1869 end = isDef(endIndex) ? endIndex : vnodes.length - 1;
1870
1871 while (start <= end) {
1872 vnode = vnodes[start];
1873 createVnode(api, vnode);
1874 insertVnode(api, parentNode, vnode, before);
1875 start++;
1876 }
1877 }
1878
1879 function insertVnode(api, parentNode, vnode, before) {
1880 var node = vnode.node,
1881 data = vnode.data,
1882 context = vnode.context,
1883 hasParent = api.parent(node); // 这里不调用 insertBefore,避免判断两次
1884
1885 if (before) {
1886 api.before(parentNode, node, before.node);
1887 } else {
1888 api.append(parentNode, node);
1889 } // 普通元素和组件的占位节点都会走到这里
1890 // 但是占位节点不用 enter,而是等组件加载回来之后再调 enter
1891
1892
1893 if (!hasParent) {
1894 var enter = UNDEFINED;
1895
1896 if (vnode.isComponent) {
1897 var component_1 = data[COMPONENT];
1898
1899 if (component_1) {
1900 enter = function () {
1901 enterVnode(vnode, component_1);
1902 };
1903 }
1904 } else if (!vnode.isStatic && !vnode.isText && !vnode.isComment) {
1905 enter = function () {
1906 enterVnode(vnode);
1907 };
1908 }
1909
1910 if (enter) {
1911 // 执行到这时,组件还没有挂载到 DOM 树
1912 // 如果此时直接触发 enter,外部还需要做多余的工作,比如 setTimeout
1913 // 索性这里直接等挂载到 DOM 数之后再触发
1914 // 注意:YoxInterface 没有声明 $observer,因为不想让外部访问,
1915 // 但是这里要用一次,所以加了 as any
1916 context.$observer.nextTask.prepend(enter);
1917 }
1918 }
1919 }
1920
1921 function removeVnodes(api, parentNode, vnodes, startIndex, endIndex) {
1922 var vnode,
1923 start = startIndex || 0,
1924 end = isDef(endIndex) ? endIndex : vnodes.length - 1;
1925
1926 while (start <= end) {
1927 vnode = vnodes[start];
1928
1929 if (vnode) {
1930 removeVnode(api, parentNode, vnode);
1931 }
1932
1933 start++;
1934 }
1935 }
1936
1937 function removeVnode(api, parentNode, vnode) {
1938 var node = vnode.node;
1939
1940 if (vnode.isStatic || vnode.isText || vnode.isComment) {
1941 api.remove(parentNode, node);
1942 } else {
1943 var done = function () {
1944 destroyVnode(api, vnode);
1945 api.remove(parentNode, node);
1946 },
1947 component_2;
1948
1949 if (vnode.isComponent) {
1950 component_2 = vnode.data[COMPONENT]; // 异步组件,还没加载成功就被删除了
1951
1952 if (!component_2) {
1953 done();
1954 return;
1955 }
1956 }
1957
1958 leaveVnode(vnode, component_2, done);
1959 }
1960 }
1961
1962 function destroyVnode(api, vnode) {
1963 /**
1964 * 如果一个子组件的模板是这样写的:
1965 *
1966 * <div>
1967 * {{#if visible}}
1968 * <slot name="children"/>
1969 * {{/if}}
1970 * </div>
1971 *
1972 * 当 visible 从 true 变为 false 时,不能销毁 slot 导入的任何 vnode
1973 * 不论是组件或是元素,都不能销毁,只能简单的 remove,
1974 * 否则子组件下一次展现它们时,会出问题
1975 */
1976 var data = vnode.data,
1977 children = vnode.children,
1978 parent = vnode.parent,
1979 slot = vnode.slot; // 销毁插槽组件
1980 // 如果宿主组件正在销毁,$vnode 属性会在调 destroy() 之前被删除
1981 // 这里表示的是宿主组件还没被销毁
1982 // 如果宿主组件被销毁了,则它的一切都要进行销毁
1983
1984 if (slot && parent && parent.$vnode) {
1985 // 如果更新时,父组件没有传入该 slot,则子组件需要销毁该 slot
1986 var slots = parent.get(slot); // slots 要么没有,要么是数组,不可能是别的
1987
1988 if (slots && has(slots, vnode)) {
1989 return;
1990 }
1991 }
1992
1993 if (vnode.isComponent) {
1994 var component_3 = data[COMPONENT];
1995
1996 if (component_3) {
1997 remove$1(vnode);
1998 component_3.destroy();
1999 } else {
2000 [data[LOADING] = FALSE];
2001 }
2002 } else {
2003 remove$1(vnode);
2004
2005 if (children) {
2006 each(children, function (child) {
2007 destroyVnode(api, child);
2008 });
2009 }
2010 }
2011 }
2012 /**
2013 * vnode 触发 enter hook 时,外部一般会做一些淡入动画
2014 */
2015
2016
2017 function enterVnode(vnode, component) {
2018 // 如果组件根元素和组件本身都写了 transition
2019 // 优先用外面定义的
2020 // 因为这明确是在覆盖配置
2021 var data = vnode.data,
2022 transition = vnode.transition;
2023
2024 if (component && !transition) {
2025 // 再看组件根元素是否有 transition
2026 transition = component.$vnode.transition;
2027 }
2028
2029 execute(data[LEAVING]);
2030
2031 if (transition) {
2032 var enter = transition.enter;
2033
2034 if (enter) {
2035 enter(vnode.node);
2036 return;
2037 }
2038 }
2039 }
2040 /**
2041 * vnode 触发 leave hook 时,外部一般会做一些淡出动画
2042 * 动画结束后才能移除节点,否则无法产生动画
2043 * 这里由外部调用 done 来通知内部动画结束
2044 */
2045
2046
2047 function leaveVnode(vnode, component, done) {
2048 // 如果组件根元素和组件本身都写了 transition
2049 // 优先用外面定义的
2050 // 因为这明确是在覆盖配置
2051 var data = vnode.data,
2052 transition = vnode.transition;
2053
2054 if (component && !transition) {
2055 // 再看组件根元素是否有 transition
2056 transition = component.$vnode.transition;
2057 }
2058
2059 if (transition) {
2060 var leave = transition.leave;
2061
2062 if (leave) {
2063 leave(vnode.node, data[LEAVING] = function () {
2064 if (data[LEAVING]) {
2065 done();
2066 data[LEAVING] = UNDEFINED;
2067 }
2068 });
2069 return;
2070 }
2071 } // 如果没有淡出动画,直接结束
2072
2073
2074 done();
2075 }
2076
2077 function updateChildren(api, parentNode, children, oldChildren) {
2078 var startIndex = 0,
2079 endIndex = children.length - 1,
2080 startVnode = children[startIndex],
2081 endVnode = children[endIndex],
2082 oldStartIndex = 0,
2083 oldEndIndex = oldChildren.length - 1,
2084 oldStartVnode = oldChildren[oldStartIndex],
2085 oldEndVnode = oldChildren[oldEndIndex],
2086 oldKeyToIndex,
2087 oldIndex;
2088
2089 while (oldStartIndex <= oldEndIndex && startIndex <= endIndex) {
2090 // 下面有设为 UNDEFINED 的逻辑
2091 if (!startVnode) {
2092 startVnode = children[++startIndex];
2093 } else if (!endVnode) {
2094 endVnode = children[--endIndex];
2095 } else if (!oldStartVnode) {
2096 oldStartVnode = oldChildren[++oldStartIndex];
2097 } else if (!oldEndVnode) {
2098 oldEndVnode = oldChildren[--oldEndIndex];
2099 } // 从头到尾比较,位置相同且值得 patch
2100 else if (isPatchable(startVnode, oldStartVnode)) {
2101 patch(api, startVnode, oldStartVnode);
2102 startVnode = children[++startIndex];
2103 oldStartVnode = oldChildren[++oldStartIndex];
2104 } // 从尾到头比较,位置相同且值得 patch
2105 else if (isPatchable(endVnode, oldEndVnode)) {
2106 patch(api, endVnode, oldEndVnode);
2107 endVnode = children[--endIndex];
2108 oldEndVnode = oldChildren[--oldEndIndex];
2109 } // 比较完两侧的节点,剩下就是 位置发生改变的节点 和 全新的节点
2110 // 当 endVnode 和 oldStartVnode 值得 patch
2111 // 说明元素被移到右边了
2112 else if (isPatchable(endVnode, oldStartVnode)) {
2113 patch(api, endVnode, oldStartVnode);
2114 insertBefore(api, parentNode, oldStartVnode.node, api.next(oldEndVnode.node));
2115 endVnode = children[--endIndex];
2116 oldStartVnode = oldChildren[++oldStartIndex];
2117 } // 当 oldEndVnode 和 startVnode 值得 patch
2118 // 说明元素被移到左边了
2119 else if (isPatchable(startVnode, oldEndVnode)) {
2120 patch(api, startVnode, oldEndVnode);
2121 insertBefore(api, parentNode, oldEndVnode.node, oldStartVnode.node);
2122 startVnode = children[++startIndex];
2123 oldEndVnode = oldChildren[--oldEndIndex];
2124 } // 尝试同级元素的 key
2125 else {
2126 if (!oldKeyToIndex) {
2127 oldKeyToIndex = createKeyToIndex(oldChildren, oldStartIndex, oldEndIndex);
2128 } // 新节点之前的位置
2129
2130
2131 oldIndex = startVnode.key ? oldKeyToIndex[startVnode.key] : UNDEFINED; // 移动元素
2132
2133 if (isDef(oldIndex)) {
2134 patch(api, startVnode, oldChildren[oldIndex]);
2135 oldChildren[oldIndex] = UNDEFINED;
2136 } // 新元素
2137 else {
2138 createVnode(api, startVnode);
2139 }
2140
2141 insertVnode(api, parentNode, startVnode, oldStartVnode);
2142 startVnode = children[++startIndex];
2143 }
2144 }
2145
2146 if (oldStartIndex > oldEndIndex) {
2147 addVnodes(api, parentNode, children, startIndex, endIndex, children[endIndex + 1]);
2148 } else if (startIndex > endIndex) {
2149 removeVnodes(api, parentNode, oldChildren, oldStartIndex, oldEndIndex);
2150 }
2151 }
2152
2153 function patch(api, vnode, oldVnode) {
2154 if (vnode === oldVnode) {
2155 return;
2156 }
2157
2158 var node = oldVnode.node,
2159 data = oldVnode.data; // 如果不能 patch,则删除重建
2160
2161 if (!isPatchable(vnode, oldVnode)) {
2162 // 同步加载的组件,初始化时不会传入占位节点
2163 // 它内部会自动生成一个注释节点,当它的根 vnode 和注释节点对比时,必然无法 patch
2164 // 于是走进此分支,为新组件创建一个 DOM 节点,然后继续 createComponent 后面的流程
2165 var parentNode = api.parent(node);
2166 createVnode(api, vnode);
2167
2168 if (parentNode) {
2169 insertVnode(api, parentNode, vnode, oldVnode);
2170 removeVnode(api, parentNode, oldVnode);
2171 }
2172
2173 return;
2174 }
2175
2176 vnode.node = node;
2177 vnode.data = data; // 组件正在异步加载,更新为最新的 vnode
2178 // 当异步加载完成时才能用上最新的 vnode
2179
2180 if (oldVnode.isComponent && data[LOADING]) {
2181 data[VNODE] = vnode;
2182 return;
2183 } // 两棵静态子树就别折腾了
2184
2185
2186 if (vnode.isStatic && oldVnode.isStatic) {
2187 return;
2188 }
2189
2190 update(api, vnode, oldVnode);
2191 update$1(api, vnode, oldVnode);
2192 update$3(vnode, oldVnode);
2193 update$2(vnode, oldVnode);
2194 var text = vnode.text,
2195 html = vnode.html,
2196 children = vnode.children,
2197 isStyle = vnode.isStyle,
2198 isOption = vnode.isOption,
2199 oldText = oldVnode.text,
2200 oldHtml = oldVnode.html,
2201 oldChildren = oldVnode.children;
2202
2203 if (string(text)) {
2204 if (text !== oldText) {
2205 api.text(node, text, isStyle, isOption);
2206 }
2207 } else if (string(html)) {
2208 if (html !== oldHtml) {
2209 api.html(node, html, isStyle, isOption);
2210 }
2211 } // 两个都有需要 diff
2212 else if (children && oldChildren) {
2213 if (children !== oldChildren) {
2214 updateChildren(api, node, children, oldChildren);
2215 }
2216 } // 有新的没旧的 - 新增节点
2217 else if (children) {
2218 if (string(oldText) || string(oldHtml)) {
2219 api.text(node, EMPTY_STRING, isStyle);
2220 }
2221
2222 addVnodes(api, node, children);
2223 } // 有旧的没新的 - 删除节点
2224 else if (oldChildren) {
2225 removeVnodes(api, node, oldChildren);
2226 } // 有旧的 text 没有新的 text
2227 else if (string(oldText) || string(oldHtml)) {
2228 api.text(node, EMPTY_STRING, isStyle);
2229 }
2230 }
2231
2232 function create(api, node, context, keypath) {
2233 return {
2234 tag: api.tag(node),
2235 data: createData(),
2236 node: node,
2237 context: context,
2238 keypath: keypath
2239 };
2240 }
2241
2242 function destroy(api, vnode, isRemove) {
2243 if (isRemove) {
2244 var parentNode = api.parent(vnode.node);
2245
2246 if (parentNode) {
2247 removeVnode(api, parentNode, vnode);
2248 } else {
2249 fatal("The vnode can't be destroyed without a parent node.");
2250 }
2251 } else {
2252 destroyVnode(api, vnode);
2253 }
2254 }
2255 /**
2256 * 元素 节点
2257 */
2258
2259
2260 var ELEMENT = 1;
2261 /**
2262 * 属性 节点
2263 */
2264
2265 var ATTRIBUTE = 2;
2266 /**
2267 * 指令 节点
2268 */
2269
2270 var DIRECTIVE = 3;
2271 /**
2272 * 属性 节点
2273 */
2274
2275 var PROPERTY = 4;
2276 /**
2277 * 文本 节点
2278 */
2279
2280 var TEXT = 5;
2281 /**
2282 * if 节点
2283 */
2284
2285 var IF = 6;
2286 /**
2287 * else if 节点
2288 */
2289
2290 var ELSE_IF = 7;
2291 /**
2292 * else 节点
2293 */
2294
2295 var ELSE = 8;
2296 /**
2297 * each 节点
2298 */
2299
2300 var EACH = 9;
2301 /**
2302 * partial 节点
2303 */
2304
2305 var PARTIAL = 10;
2306 /**
2307 * import 节点
2308 */
2309
2310 var IMPORT = 11;
2311 /**
2312 * 表达式 节点
2313 */
2314
2315 var EXPRESSION = 12;
2316 /**
2317 * 延展操作 节点
2318 */
2319
2320 var SPREAD = 13; // 特殊标签
2321
2322 var specialTags = {}; // 特殊属性
2323
2324 var specialAttrs = {}; // 名称 -> 类型的映射
2325
2326 var name2Type = {};
2327 specialTags[RAW_SLOT] = specialTags[RAW_TEMPLATE] = specialAttrs[RAW_KEY] = specialAttrs[RAW_REF] = specialAttrs[RAW_SLOT] = TRUE;
2328 name2Type['if'] = IF;
2329 name2Type['each'] = EACH;
2330 name2Type['partial'] = PARTIAL;
2331
2332 function createAttribute(name) {
2333 return {
2334 type: ATTRIBUTE,
2335 isStatic: TRUE,
2336 name: name
2337 };
2338 }
2339
2340 function createDirective(name, ns, modifier) {
2341 return {
2342 type: DIRECTIVE,
2343 ns: ns,
2344 name: name,
2345 key: join$1(ns, name),
2346 modifier: modifier
2347 };
2348 }
2349
2350 function createProperty(name, hint, value, expr, children) {
2351 return {
2352 type: PROPERTY,
2353 isStatic: TRUE,
2354 name: name,
2355 hint: hint,
2356 value: value,
2357 expr: expr,
2358 children: children
2359 };
2360 }
2361
2362 function createEach(from, to, equal, index) {
2363 return {
2364 type: EACH,
2365 from: from,
2366 to: to,
2367 equal: equal,
2368 index: index,
2369 isComplex: TRUE
2370 };
2371 }
2372
2373 function createElement(tag, isSvg, isStyle, isComponent) {
2374 return {
2375 type: ELEMENT,
2376 tag: tag,
2377 isSvg: isSvg,
2378 isStyle: isStyle,
2379 // 只有 <option> 没有 value 属性时才为 true
2380 isOption: FALSE,
2381 isComponent: isComponent,
2382 isStatic: !isComponent && tag !== RAW_SLOT
2383 };
2384 }
2385
2386 function createElse() {
2387 return {
2388 type: ELSE
2389 };
2390 }
2391
2392 function createElseIf(expr) {
2393 return {
2394 type: ELSE_IF,
2395 expr: expr
2396 };
2397 }
2398
2399 function createExpression(expr, safe) {
2400 return {
2401 type: EXPRESSION,
2402 expr: expr,
2403 safe: safe,
2404 isLeaf: TRUE
2405 };
2406 }
2407
2408 function createIf(expr) {
2409 return {
2410 type: IF,
2411 expr: expr
2412 };
2413 }
2414
2415 function createImport(name) {
2416 return {
2417 type: IMPORT,
2418 name: name,
2419 isComplex: TRUE,
2420 isLeaf: TRUE
2421 };
2422 }
2423
2424 function createPartial(name) {
2425 return {
2426 type: PARTIAL,
2427 name: name,
2428 isComplex: TRUE
2429 };
2430 }
2431
2432 function createSpread(expr, binding) {
2433 return {
2434 type: SPREAD,
2435 expr: expr,
2436 binding: binding,
2437 isLeaf: TRUE
2438 };
2439 }
2440
2441 function createText(text) {
2442 return {
2443 type: TEXT,
2444 text: text,
2445 isStatic: TRUE,
2446 isLeaf: TRUE
2447 };
2448 } // 首字母大写,或中间包含 -
2449
2450
2451 var componentNamePattern = /^[$A-Z]|-/,
2452 // HTML 实体(中间最多 6 位,没见过更长的)
2453 htmlEntityPattern = /&[#\w\d]{2,6};/,
2454 // 常见的自闭合标签
2455 selfClosingTagNames = 'area,base,embed,track,source,param,input,col,img,br,hr'.split(','),
2456 // 常见的 svg 标签
2457 svgTagNames = 'svg,g,defs,desc,metadata,symbol,use,image,path,rect,circle,line,ellipse,polyline,polygon,text,tspan,tref,textpath,marker,pattern,clippath,mask,filter,cursor,view,animate,font,font-face,glyph,missing-glyph,foreignObject'.split(','),
2458 // 常见的字符串类型的属性
2459 // 注意:autocomplete,autocapitalize 不是布尔类型
2460 stringProperyNames = 'id,class,name,value,for,accesskey,title,style,src,type,href,target,alt,placeholder,preload,poster,wrap,accept,pattern,dir,autocomplete,autocapitalize'.split(','),
2461 // 常见的数字类型的属性
2462 numberProperyNames = 'min,minlength,max,maxlength,step,width,height,size,rows,cols,tabindex'.split(','),
2463 // 常见的布尔类型的属性
2464 booleanProperyNames = 'disabled,checked,required,multiple,readonly,autofocus,autoplay,controls,loop,muted,novalidate,draggable,hidden,spellcheck'.split(','),
2465 // 某些属性 attribute name 和 property name 不同
2466 attr2Prop = {}; // 列举几个常见的
2467
2468 attr2Prop['for'] = 'htmlFor';
2469 attr2Prop['class'] = 'className';
2470 attr2Prop['accesskey'] = 'accessKey';
2471 attr2Prop['style'] = 'style.cssText';
2472 attr2Prop['novalidate'] = 'noValidate';
2473 attr2Prop['readonly'] = 'readOnly';
2474 attr2Prop['tabindex'] = 'tabIndex';
2475 attr2Prop['minlength'] = 'minLength';
2476 attr2Prop['maxlength'] = 'maxLength';
2477
2478 function isSelfClosing(tagName) {
2479 return has(selfClosingTagNames, tagName);
2480 }
2481
2482 function createAttribute$1(element, name) {
2483 // 组件用驼峰格式
2484 if (element.isComponent) {
2485 return createAttribute(camelize(name));
2486 } // 原生 dom 属性
2487 else {
2488 // 把 attr 优化成 prop
2489 var lowerName = lower(name); // <slot> 、<template> 或 svg 中的属性不用识别为 property
2490
2491 if (specialTags[element.tag] || element.isSvg) {
2492 return createAttribute(name);
2493 } // 尝试识别成 property
2494 else if (has(stringProperyNames, lowerName)) {
2495 return createProperty(attr2Prop[lowerName] || lowerName, HINT_STRING);
2496 } else if (has(numberProperyNames, lowerName)) {
2497 return createProperty(attr2Prop[lowerName] || lowerName, HINT_NUMBER);
2498 } else if (has(booleanProperyNames, lowerName)) {
2499 return createProperty(attr2Prop[lowerName] || lowerName, HINT_BOOLEAN);
2500 } // 没辙,还是个 attribute
2501
2502
2503 return createAttribute(name);
2504 }
2505 }
2506
2507 function getAttributeDefaultValue(element, name) {
2508 // 比如 <Dog isLive>
2509 if (element.isComponent) {
2510 return TRUE;
2511 } // <div data-name checked>
2512 else {
2513 return startsWith(name, 'data-') ? EMPTY_STRING : name;
2514 }
2515 }
2516
2517 function createElement$1(tagName) {
2518 var isSvg = has(svgTagNames, tagName),
2519 isComponent = FALSE; // 是 svg 就不可能是组件
2520 // 加这个判断的原因是,svg 某些标签含有 连字符 和 大写字母,比较蛋疼
2521
2522 if (!isSvg && componentNamePattern.test(tagName)) {
2523 isComponent = TRUE;
2524 }
2525
2526 return createElement(tagName, isSvg, tagName === 'style', isComponent);
2527 }
2528
2529 function compatElement(element) {
2530 var tag = element.tag,
2531 attrs = element.attrs,
2532 hasType = FALSE,
2533 hasValue = FALSE;
2534
2535 if (attrs) {
2536 each(attrs, function (attr) {
2537 var name = attr.type === PROPERTY ? attr.name : UNDEFINED;
2538
2539 if (name === 'type') {
2540 hasType = TRUE;
2541 } else if (name === RAW_VALUE) {
2542 hasValue = TRUE;
2543 }
2544 });
2545 } // 补全 style 标签的 type
2546 // style 如果没有 type 则加一个 type="text/css"
2547 // 因为低版本 IE 没这个属性,没法正常渲染样式
2548
2549
2550 if (element.isStyle && !hasType) {
2551 push(element.attrs || (element.attrs = []), createProperty('type', HINT_STRING, 'text/css'));
2552 } // 低版本 IE 需要给 option 标签强制加 value
2553 else if (tag === 'option' && !hasValue) {
2554 element.isOption = TRUE;
2555 }
2556 }
2557
2558 function setElementText(element, text) {
2559 if (htmlEntityPattern.test(text)) {
2560 element.html = text;
2561 return TRUE;
2562 }
2563 }
2564
2565 function toNumber(target, defaultValue) {
2566 return numeric(target) ? +target : isDef(defaultValue) ? defaultValue : 0;
2567 }
2568 /**
2569 * 字面量
2570 */
2571
2572
2573 var LITERAL = 1;
2574 /**
2575 * 标识符
2576 */
2577
2578 var IDENTIFIER = 2;
2579 /**
2580 * 对象属性或数组下标
2581 */
2582
2583 var MEMBER = 3;
2584 /**
2585 * 一元表达式,如 - a
2586 */
2587
2588 var UNARY = 4;
2589 /**
2590 * 二元表达式,如 a + b
2591 */
2592
2593 var BINARY = 5;
2594 /**
2595 * 三元表达式,如 a ? b : c
2596 */
2597
2598 var TERNARY = 6;
2599 /**
2600 * 数组表达式,如 [ 1, 2, 3 ]
2601 */
2602
2603 var ARRAY = 7;
2604 /**
2605 * 对象表达式,如 { name: 'yox' }
2606 */
2607
2608 var OBJECT = 8;
2609 /**
2610 * 函数调用表达式,如 a()
2611 */
2612
2613 var CALL = 9;
2614
2615 function createArray(nodes, raw) {
2616 return {
2617 type: ARRAY,
2618 raw: raw,
2619 nodes: nodes
2620 };
2621 }
2622
2623 function createBinary(left, operator, right, raw) {
2624 return {
2625 type: BINARY,
2626 raw: raw,
2627 left: left,
2628 operator: operator,
2629 right: right
2630 };
2631 }
2632
2633 function createCall(name, args, raw) {
2634 return {
2635 type: CALL,
2636 raw: raw,
2637 name: name,
2638 args: args
2639 };
2640 }
2641
2642 function createIdentifier(raw, name, isProp) {
2643 var lookup = TRUE,
2644 offset = 0;
2645
2646 if (name === KEYPATH_CURRENT || name === KEYPATH_PARENT) {
2647 lookup = FALSE;
2648
2649 if (name === KEYPATH_PARENT) {
2650 offset = 1;
2651 }
2652
2653 name = EMPTY_STRING;
2654 } // 对象属性需要区分 a.b 和 a[b]
2655 // 如果不借用 Literal 无法实现这个判断
2656 // 同理,如果用了这种方式,就无法区分 a.b 和 a['b'],但是无所谓,这两种表示法本就一个意思
2657
2658
2659 return isProp ? createLiteral(name, raw) : createIdentifierInner(raw, name, lookup, offset);
2660 }
2661
2662 function createLiteral(value, raw) {
2663 return {
2664 type: LITERAL,
2665 raw: raw,
2666 value: value
2667 };
2668 }
2669
2670 function createObject(keys, values, raw) {
2671 return {
2672 type: OBJECT,
2673 raw: raw,
2674 keys: keys,
2675 values: values
2676 };
2677 }
2678
2679 function createTernary(test, yes, no, raw) {
2680 return {
2681 type: TERNARY,
2682 raw: raw,
2683 test: test,
2684 yes: yes,
2685 no: no
2686 };
2687 }
2688
2689 function createUnary(operator, node, raw) {
2690 return {
2691 type: UNARY,
2692 raw: raw,
2693 operator: operator,
2694 node: node
2695 };
2696 }
2697 /**
2698 * 通过判断 nodes 来决定是否需要创建 Member
2699 *
2700 * 创建 Member 至少需要 nodes 有两个节点
2701 */
2702
2703
2704 function createMemberIfNeeded(raw, nodes) {
2705 // 第一个节点要特殊处理
2706 var firstNode = nodes.shift(),
2707 // 是否向上查找
2708 lookup = TRUE,
2709 // 偏移量,默认从当前 context 开始查找
2710 offset = 0; // 表示传入的 nodes 至少有两个节点(弹出了一个)
2711
2712 if (nodes.length > 0) {
2713 // 处理剩下的 nodes
2714 // 这里要做两手准备:
2715 // 1. 如果全是 literal 节点,则编译时 join
2716 // 2. 如果不全是 literal 节点,则运行时 join
2717 // 是否全是 Literal 节点
2718 var isLiteral_1 = TRUE,
2719 // 静态节点
2720 staticNodes_1 = [],
2721 // 对于 this.a.b[c] 这样的
2722 // 要还原静态部分 this.a.b 的 raw
2723 // 虽然 raw 没什么大用吧,谁让我是洁癖呢
2724 staticRaw_1 = EMPTY_STRING,
2725 // 动态节点
2726 dynamicNodes_1 = [];
2727 each(nodes, function (node) {
2728 if (isLiteral_1) {
2729 if (node.type === LITERAL) {
2730 if (node.raw === KEYPATH_PARENT) {
2731 offset += 1;
2732 staticRaw_1 = staticRaw_1 ? staticRaw_1 + RAW_SLASH + KEYPATH_PARENT : KEYPATH_PARENT;
2733 return;
2734 }
2735
2736 if (node.raw !== KEYPATH_CURRENT) {
2737 var value = toString(node.value);
2738 push(staticNodes_1, value);
2739
2740 if (staticRaw_1) {
2741 staticRaw_1 += endsWith(staticRaw_1, KEYPATH_PARENT) ? RAW_SLASH : RAW_DOT;
2742 }
2743
2744 staticRaw_1 += value;
2745 }
2746 } else {
2747 isLiteral_1 = FALSE;
2748 }
2749 }
2750
2751 if (!isLiteral_1) {
2752 push(dynamicNodes_1, node);
2753 }
2754 }); // lookup 要求第一位元素是 Identifier,且它的 lookup 是 true 才为 true
2755 // 其他情况都为 false,如 "11".length 第一位元素是 Literal,不存在向上寻找的需求
2756 // 优化 1:计算 keypath
2757 //
2758 // 计算 keypath 的唯一方式是,第一位元素是 Identifier,后面都是 Literal
2759 // 否则就表示中间包含动态元素,这会导致无法计算静态路径
2760 // 如 a.b.c 可以算出 static keypath,而 a[b].c 则不行,因为 b 是动态的
2761 // 优化 2:计算 offset 并智能转成 Identifier
2762 //
2763 // 比如 xx 这样的表达式,应优化成 offset = 2,并转成 Identifier
2764 // 处理第一个节点
2765
2766 if (firstNode.type === IDENTIFIER) {
2767 lookup = firstNode.lookup;
2768 offset += firstNode.offset;
2769 var firstName = firstNode.name; // 不是 KEYPATH_THIS 或 KEYPATH_PARENT
2770
2771 if (firstName) {
2772 unshift(staticNodes_1, firstName);
2773 } // 转成 Identifier
2774
2775
2776 firstName = join(staticNodes_1, RAW_DOT); // 当 isLiteral 为 false 时
2777 // 需要为 lead 节点创建合适的 raw
2778
2779 var firstRaw = firstNode.raw;
2780
2781 if (staticRaw_1) {
2782 firstRaw += (firstRaw === KEYPATH_PARENT ? RAW_SLASH : RAW_DOT) + staticRaw_1;
2783 } // a.b.c
2784
2785
2786 if (isLiteral_1) {
2787 firstNode = createIdentifierInner(raw, firstName, lookup, offset);
2788 } // a[b]
2789 // this.a[b]
2790 else {
2791 firstNode = createMemberInner(raw, createIdentifierInner(firstRaw, firstName, lookup, offset), UNDEFINED, dynamicNodes_1, lookup, offset);
2792 }
2793 } else {
2794 // 例子:
2795 // "xxx".length
2796 // format().a.b
2797 if (isLiteral_1) {
2798 firstNode = createMemberInner(raw, firstNode, join(staticNodes_1, RAW_DOT), UNDEFINED, lookup, offset);
2799 } // 例子:
2800 // "xxx"[length]
2801 // format()[a]
2802 else {
2803 firstNode = createMemberInner(raw, firstNode, UNDEFINED, dynamicNodes_1, lookup, offset);
2804 }
2805 }
2806 }
2807
2808 return firstNode;
2809 }
2810
2811 function createIdentifierInner(raw, name, lookup, offset) {
2812 return {
2813 type: IDENTIFIER,
2814 raw: raw,
2815 name: name,
2816 lookup: lookup,
2817 offset: offset
2818 };
2819 }
2820
2821 function createMemberInner(raw, lead, keypath, nodes, lookup, offset) {
2822 return {
2823 type: MEMBER,
2824 raw: raw,
2825 lead: lead,
2826 keypath: keypath,
2827 nodes: nodes,
2828 lookup: lookup,
2829 offset: offset
2830 };
2831 }
2832
2833 var unary = {
2834 '+': TRUE,
2835 '-': TRUE,
2836 '~': TRUE,
2837 '!': TRUE,
2838 '!!': TRUE
2839 }; // 参考 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
2840
2841 var binary = {
2842 '*': 14,
2843 '/': 14,
2844 '%': 14,
2845 '+': 13,
2846 '-': 13,
2847 '<<': 12,
2848 '>>': 12,
2849 '>>>': 12,
2850 '<': 11,
2851 '<=': 11,
2852 '>': 11,
2853 '>=': 11,
2854 '==': 10,
2855 '!=': 10,
2856 '===': 10,
2857 '!==': 10,
2858 '&': 9,
2859 '^': 8,
2860 '|': 7,
2861 '&&': 6,
2862 '||': 5
2863 };
2864
2865 function compile(content) {
2866 if (!cache[content]) {
2867 var parser = new Parser(content);
2868 cache[content] = parser.scanTernary(CODE_EOF);
2869 }
2870
2871 return cache[content];
2872 }
2873
2874 var Parser =
2875 /** @class */
2876 function () {
2877 function Parser(content) {
2878 var instance = this,
2879 length = content.length;
2880 instance.index = MINUS_ONE;
2881 instance.end = length;
2882 instance.code = CODE_EOF;
2883 instance.content = content;
2884 instance.go();
2885 }
2886 /**
2887 * 移动一个字符
2888 */
2889
2890
2891 Parser.prototype.go = function (step) {
2892 var instance = this,
2893 index = instance.index,
2894 end = instance.end;
2895 index += step || 1;
2896
2897 if (index >= 0 && index < end) {
2898 instance.code = codeAt(instance.content, index);
2899 instance.index = index;
2900 } else {
2901 instance.code = CODE_EOF;
2902 instance.index = index < 0 ? MINUS_ONE : end;
2903 }
2904 };
2905 /**
2906 * 跳过空白符
2907 */
2908
2909
2910 Parser.prototype.skip = function (step) {
2911 var instance = this,
2912 reversed = step && step < 0; // 如果表达式是 " xyz ",到达结尾后,如果希望 skip(-1) 回到最后一个非空白符
2913 // 必须先判断最后一个字符是空白符,否则碰到 "xyz" 这样结尾不是空白符的,其实不应该回退
2914
2915 if (instance.code === CODE_EOF) {
2916 var oldIndex = instance.index;
2917 instance.go(step); // 如果跳一位之后不是空白符,还原,然后返回
2918
2919 if (!isWhitespace(instance.code)) {
2920 instance.go(oldIndex - instance.index);
2921 return;
2922 }
2923 } // 逆向时,只有位置真的发生过变化才需要在停止时正向移动一位
2924 // 比如 (a) 如果调用 skip 前位于 ),调用 skip(-1) ,结果应该是原地不动
2925 // 为了解决这个问题,应该首先判断当前是不是空白符,如果不是,直接返回
2926 else if (!isWhitespace(instance.code)) {
2927 return;
2928 } // 如果是正向的,停在第一个非空白符左侧
2929 // 如果是逆向的,停在第一个非空白符右侧
2930
2931
2932 while (TRUE) {
2933 if (isWhitespace(instance.code)) {
2934 instance.go(step);
2935 } else {
2936 if (reversed) {
2937 instance.go();
2938 }
2939
2940 break;
2941 }
2942 }
2943 };
2944 /**
2945 * 判断当前字符
2946 */
2947
2948
2949 Parser.prototype.is = function (code) {
2950 return this.code === code;
2951 };
2952 /**
2953 * 截取一段字符串
2954 */
2955
2956
2957 Parser.prototype.pick = function (startIndex, endIndex) {
2958 return slice(this.content, startIndex, isDef(endIndex) ? endIndex : this.index);
2959 };
2960 /**
2961 * 尝试解析下一个 token
2962 */
2963
2964
2965 Parser.prototype.scanToken = function () {
2966 var instance = this,
2967 code = instance.code,
2968 index = instance.index;
2969
2970 if (isIdentifierStart(code)) {
2971 return instance.scanTail(index, [instance.scanIdentifier(index)]);
2972 }
2973
2974 if (isDigit(code)) {
2975 return instance.scanNumber(index);
2976 }
2977
2978 switch (code) {
2979 case CODE_EOF:
2980 return;
2981 // 'x' "x"
2982
2983 case CODE_SQUOTE:
2984 case CODE_DQUOTE:
2985 return instance.scanTail(index, [instance.scanString(index, code)]);
2986 // .1 ./ ../
2987
2988 case CODE_DOT:
2989 instance.go();
2990 return isDigit(instance.code) ? instance.scanNumber(index) : instance.scanPath(index);
2991 // (xx)
2992
2993 case CODE_OPAREN:
2994 instance.go();
2995 return instance.scanTernary(CODE_CPAREN);
2996 // [xx, xx]
2997
2998 case CODE_OBRACK:
2999 return instance.scanTail(index, [createArray(instance.scanTuple(index, CODE_CBRACK), instance.pick(index))]);
3000 // { a: 'x', b: 'x' }
3001
3002 case CODE_OBRACE:
3003 return instance.scanObject(index);
3004 } // 因为 scanOperator 会导致 index 发生变化,只能放在最后尝试
3005
3006
3007 var operator = instance.scanOperator(index);
3008
3009 if (operator && unary[operator]) {
3010 var node = instance.scanTernary();
3011
3012 if (node) {
3013 if (node.type === LITERAL) {
3014 var value = node.value;
3015
3016 if (number(value)) {
3017 // 类似 ' -1 ' 这样的右侧有空格,需要撤回来
3018 instance.skip(MINUS_ONE);
3019 return createLiteral(-value, instance.pick(index));
3020 }
3021 } // 类似 ' -a ' 这样的右侧有空格,需要撤回来
3022
3023
3024 instance.skip(MINUS_ONE);
3025 return createUnary(operator, node, instance.pick(index));
3026 }
3027
3028 {
3029 // 一元运算只有操作符没有表达式?
3030 instance.fatal(index, "Expression expected.");
3031 }
3032 }
3033 };
3034 /**
3035 * 扫描数字
3036 *
3037 * 支持整数和小数
3038 *
3039 * @param startIndex
3040 * @return
3041 */
3042
3043
3044 Parser.prototype.scanNumber = function (startIndex) {
3045 var instance = this;
3046
3047 while (isNumber(instance.code)) {
3048 instance.go();
3049 }
3050
3051 var raw = instance.pick(startIndex); // 尝试转型,如果转型失败,则确定是个错误的数字
3052
3053 if (numeric(raw)) {
3054 return createLiteral(+raw, raw);
3055 }
3056
3057 {
3058 instance.fatal(startIndex, "Number expected.");
3059 }
3060 };
3061 /**
3062 * 扫描字符串
3063 *
3064 * 支持反斜线转义引号
3065 *
3066 * @param startIndex
3067 * @param endCode
3068 */
3069
3070
3071 Parser.prototype.scanString = function (startIndex, endCode) {
3072 var instance = this;
3073
3074 loop: while (TRUE) {
3075 // 这句有两个作用:
3076 // 1. 跳过开始的引号
3077 // 2. 驱动 index 前进
3078 instance.go();
3079
3080 switch (instance.code) {
3081 // \" \'
3082 case CODE_BACKSLASH:
3083 instance.go();
3084 break;
3085
3086 case endCode:
3087 instance.go();
3088 break loop;
3089
3090 case CODE_EOF:
3091 {
3092 // 到头了,字符串还没解析完呢?
3093 instance.fatal(startIndex, 'Unexpected end of text.');
3094 }
3095 break loop;
3096 }
3097 } // new Function 处理字符转义
3098
3099
3100 var raw = instance.pick(startIndex);
3101 return createLiteral(new Function("return " + raw)(), raw);
3102 };
3103 /**
3104 * 扫描对象字面量
3105 *
3106 * @param startIndex
3107 */
3108
3109
3110 Parser.prototype.scanObject = function (startIndex) {
3111 var instance = this,
3112 keys = [],
3113 values = [],
3114 isKey = TRUE,
3115 node; // 跳过 {
3116
3117 instance.go();
3118
3119 loop: while (TRUE) {
3120 switch (instance.code) {
3121 case CODE_CBRACE:
3122 instance.go();
3123 {
3124 // 对象的 keys 和 values 的长度不一致
3125 if (keys.length !== values.length) {
3126 instance.fatal(startIndex, 'The number of keys and values must be equal.');
3127 }
3128 }
3129 break loop;
3130
3131 case CODE_EOF:
3132 {
3133 // 到头了,对象还没解析完呢?
3134 instance.fatal(startIndex, 'Unexpected end of text.');
3135 }
3136 break loop;
3137 // :
3138
3139 case CODE_COLON:
3140 instance.go();
3141 isKey = FALSE;
3142 break;
3143 // ,
3144
3145 case CODE_COMMA:
3146 instance.go();
3147 isKey = TRUE;
3148 break;
3149
3150 default:
3151 // 解析 key 的时候,node 可以为空,如 { } 或 { name: 'xx', }
3152 // 解析 value 的时候,node 不能为空
3153 node = instance.scanTernary();
3154
3155 if (isKey) {
3156 if (node) {
3157 // 处理 { key : value } key 后面的空格
3158 instance.skip();
3159
3160 if (node.type === IDENTIFIER) {
3161 push(keys, node.name);
3162 } else if (node.type === LITERAL) {
3163 push(keys, node.value);
3164 } else {
3165 {
3166 // 对象的 key 必须是字面量或标识符
3167 instance.fatal(startIndex, 'The key of an object must be a literal or identifier.');
3168 }
3169 break loop;
3170 }
3171 }
3172 } else if (node) {
3173 // 处理 { key : value } value 后面的空格
3174 instance.skip();
3175 push(values, node);
3176 } // 类似这样 { key: }
3177 else {
3178 {
3179 // 对象的值没找到
3180 instance.fatal(startIndex, "The value of the object was not found.");
3181 }
3182 break loop;
3183 }
3184
3185 }
3186 }
3187
3188 return createObject(keys, values, instance.pick(startIndex));
3189 };
3190 /**
3191 * 扫描元组,即 `a, b, c` 这种格式,可以是参数列表,也可以是数组
3192 *
3193 * @param startIndex
3194 * @param endCode 元组的结束字符编码
3195 */
3196
3197
3198 Parser.prototype.scanTuple = function (startIndex, endCode) {
3199 var instance = this,
3200 nodes = [],
3201 node; // 跳过开始字符,如 [ 和 (
3202
3203 instance.go();
3204
3205 loop: while (TRUE) {
3206 switch (instance.code) {
3207 case endCode:
3208 instance.go();
3209 break loop;
3210
3211 case CODE_EOF:
3212 {
3213 // 到头了,tuple 还没解析完呢?
3214 instance.fatal(startIndex, 'Unexpected end of text.');
3215 }
3216 break loop;
3217
3218 case CODE_COMMA:
3219 instance.go();
3220 break;
3221
3222 default:
3223 // 1. ( )
3224 // 2. (1, 2, )
3225 // 这三个例子都会出现 scanTernary 为空的情况
3226 // 但是不用报错
3227 node = instance.scanTernary();
3228
3229 if (node) {
3230 // 为了解决 1 , 2 , 3 这样的写法
3231 // 当解析出值后,先跳过后面的空格
3232 instance.skip();
3233 push(nodes, node);
3234 }
3235
3236 }
3237 }
3238
3239 return nodes;
3240 };
3241 /**
3242 * 扫描路径,如 `./` 和 `../`
3243 *
3244 * 路径必须位于开头,如 ./../ 或 ,不存在 a/../b/../c 这样的情况,因为路径是用来切换或指定 context 的
3245 *
3246 * @param startIndex
3247 * @param prevNode
3248 */
3249
3250
3251 Parser.prototype.scanPath = function (startIndex) {
3252 var instance = this,
3253 nodes = [],
3254 name; // 进入此函数时,已确定前一个 code 是 CODE_DOT
3255 // 此时只需判断接下来是 ./ 还是 / 就行了
3256
3257 while (TRUE) {
3258 // 要么是 current 要么是 parent
3259 name = KEYPATH_CURRENT; // ../
3260
3261 if (instance.is(CODE_DOT)) {
3262 instance.go();
3263 name = KEYPATH_PARENT;
3264 }
3265
3266 push(nodes, createIdentifier(name, name, nodes.length > 0)); // 如果以 / 结尾,则命中 ./ 或 ../
3267
3268 if (instance.is(CODE_SLASH)) {
3269 instance.go(); // 没写错,这里不必强调 isIdentifierStart,数字开头也可以吧
3270
3271 if (isIdentifierPart(instance.code)) {
3272 push(nodes, instance.scanIdentifier(instance.index, TRUE));
3273 return instance.scanTail(startIndex, nodes);
3274 } else if (instance.is(CODE_DOT)) {
3275 // 先跳过第一个 .
3276 instance.go(); // 继续循环
3277 } else {
3278 // 类似 ./ 或 ../ 这样后面不跟标识符是想干嘛?报错可好?
3279 {
3280 instance.fatal(startIndex, last(nodes).raw + "/ must be followed by an identifier.");
3281 }
3282 break;
3283 }
3284 } // 类似 . 或 ..,可能就是想读取层级对象
3285 // 此处不用关心后面跟的具体是什么字符,那是其他函数的事情,就算报错也让别的函数去报
3286 // 此处也不用关心延展操作符,即 ...object,因为表达式引擎管不了这事,它没法把对象变成 attr1=value1 attr2=value2 的格式
3287 // 这应该是模板引擎该做的事
3288 else {
3289 break;
3290 }
3291 }
3292 };
3293 /**
3294 * 扫描变量
3295 */
3296
3297
3298 Parser.prototype.scanTail = function (startIndex, nodes) {
3299 var instance = this,
3300 node;
3301 /**
3302 * 标识符后面紧着的字符,可以是 ( . [,此外还存在各种组合,感受一下:
3303 *
3304 * a.b.c().length
3305 * a[b].c()()
3306 * a[b][c]()[d](e, f, g).length
3307 * [].length
3308 */
3309
3310 loop: while (TRUE) {
3311 switch (instance.code) {
3312 // a(x)
3313 case CODE_OPAREN:
3314 nodes = [createCall(createMemberIfNeeded(instance.pick(startIndex), nodes), instance.scanTuple(instance.index, CODE_CPAREN), instance.pick(startIndex))];
3315 break;
3316 // a.x
3317
3318 case CODE_DOT:
3319 instance.go(); // 接下来的字符,可能是数字,也可能是标识符,如果不是就报错
3320
3321 if (isIdentifierPart(instance.code)) {
3322 // 无需识别关键字
3323 push(nodes, instance.scanIdentifier(instance.index, TRUE));
3324 break;
3325 } else {
3326 {
3327 // . 后面跟的都是啥玩意啊
3328 instance.fatal(startIndex, 'Identifier or number expected.');
3329 }
3330 break loop;
3331 }
3332
3333 // a[]
3334
3335 case CODE_OBRACK:
3336 // 过掉 [
3337 instance.go();
3338 node = instance.scanTernary(CODE_CBRACK);
3339
3340 if (node) {
3341 push(nodes, node);
3342 break;
3343 } else {
3344 // [] 内部不能为空
3345 {
3346 instance.fatal(startIndex, "[] is not allowed.");
3347 }
3348 break loop;
3349 }
3350
3351 default:
3352 break loop;
3353 }
3354 }
3355
3356 return createMemberIfNeeded(instance.pick(startIndex), nodes);
3357 };
3358 /**
3359 * 扫描标识符
3360 *
3361 * @param startIndex
3362 * @param isProp 是否是对象的属性
3363 * @return
3364 */
3365
3366
3367 Parser.prototype.scanIdentifier = function (startIndex, isProp) {
3368 var instance = this;
3369
3370 while (isIdentifierPart(instance.code)) {
3371 instance.go();
3372 }
3373
3374 var raw = instance.pick(startIndex);
3375 return !isProp && raw in keywordLiterals ? createLiteral(keywordLiterals[raw], raw) : createIdentifier(raw, raw, isProp);
3376 };
3377 /**
3378 * 扫描运算符
3379 *
3380 * @param startIndex
3381 */
3382
3383
3384 Parser.prototype.scanOperator = function (startIndex) {
3385 var instance = this;
3386
3387 switch (instance.code) {
3388 // /、%、~、^
3389 case CODE_DIVIDE:
3390 case CODE_MODULO:
3391 case CODE_WAVE:
3392 case CODE_XOR:
3393 instance.go();
3394 break;
3395 // *
3396
3397 case CODE_MULTIPLY:
3398 instance.go();
3399 break;
3400 // +
3401
3402 case CODE_PLUS:
3403 instance.go();
3404 {
3405 // ++
3406 if (instance.is(CODE_PLUS)) {
3407 instance.fatal(startIndex, 'The operator "++" is not supported.');
3408 }
3409 }
3410 break;
3411 // -
3412
3413 case CODE_MINUS:
3414 instance.go();
3415 {
3416 // --
3417 if (instance.is(CODE_MINUS)) {
3418 instance.fatal(startIndex, 'The operator "--" is not supported.');
3419 }
3420 }
3421 break;
3422 // !、!!、!=、!==
3423
3424 case CODE_NOT:
3425 instance.go();
3426
3427 if (instance.is(CODE_NOT)) {
3428 instance.go();
3429 } else if (instance.is(CODE_EQUAL)) {
3430 instance.go();
3431
3432 if (instance.is(CODE_EQUAL)) {
3433 instance.go();
3434 }
3435 }
3436
3437 break;
3438 // &、&&
3439
3440 case CODE_AND:
3441 instance.go();
3442
3443 if (instance.is(CODE_AND)) {
3444 instance.go();
3445 }
3446
3447 break;
3448 // |、||
3449
3450 case CODE_OR:
3451 instance.go();
3452
3453 if (instance.is(CODE_OR)) {
3454 instance.go();
3455 }
3456
3457 break;
3458 // ==、===
3459
3460 case CODE_EQUAL:
3461 instance.go();
3462
3463 if (instance.is(CODE_EQUAL)) {
3464 instance.go();
3465
3466 if (instance.is(CODE_EQUAL)) {
3467 instance.go();
3468 }
3469 } // 一个等号要报错
3470 else {
3471 instance.fatal(startIndex, 'Assignment statements are not supported.');
3472 }
3473
3474 break;
3475 // <、<=、<<
3476
3477 case CODE_LESS:
3478 instance.go();
3479
3480 if (instance.is(CODE_EQUAL) || instance.is(CODE_LESS)) {
3481 instance.go();
3482 }
3483
3484 break;
3485 // >、>=、>>、>>>
3486
3487 case CODE_GREAT:
3488 instance.go();
3489
3490 if (instance.is(CODE_EQUAL)) {
3491 instance.go();
3492 } else if (instance.is(CODE_GREAT)) {
3493 instance.go();
3494
3495 if (instance.is(CODE_GREAT)) {
3496 instance.go();
3497 }
3498 }
3499
3500 break;
3501 }
3502
3503 if (instance.index > startIndex) {
3504 return instance.pick(startIndex);
3505 }
3506 };
3507 /**
3508 * 扫描二元运算
3509 */
3510
3511
3512 Parser.prototype.scanBinary = function (startIndex) {
3513 // 二元运算,如 a + b * c / d,这里涉及运算符的优先级
3514 // 算法参考 https://en.wikipedia.org/wiki/Shunting-yard_algorithm
3515 var instance = this,
3516 // 格式为 [ index1, node1, index2, node2, ... ]
3517 output = [],
3518 token,
3519 index,
3520 operator,
3521 operatorPrecedence,
3522 lastOperator,
3523 lastOperatorPrecedence;
3524
3525 while (TRUE) {
3526 instance.skip();
3527 push(output, instance.index);
3528 token = instance.scanToken();
3529
3530 if (token) {
3531 push(output, token);
3532 push(output, instance.index);
3533 instance.skip();
3534 operator = instance.scanOperator(instance.index); // 必须是二元运算符,一元不行
3535
3536 if (operator && (operatorPrecedence = binary[operator])) {
3537 // 比较前一个运算符
3538 index = output.length - 4; // 如果前一个运算符的优先级 >= 现在这个,则新建 Binary
3539 // 如 a + b * c / d,当从左到右读取到 / 时,发现和前一个 * 优先级相同,则把 b * c 取出用于创建 Binary
3540
3541 if ((lastOperator = output[index]) && (lastOperatorPrecedence = binary[lastOperator]) && lastOperatorPrecedence >= operatorPrecedence) {
3542 output.splice(index - 2, 5, createBinary(output[index - 2], lastOperator, output[index + 2], instance.pick(output[index - 3], output[index + 3])));
3543 }
3544
3545 push(output, operator);
3546 continue;
3547 } else {
3548 operator = UNDEFINED;
3549 }
3550 } // 比如不支持的表达式,a++ 之类的
3551 else {
3552 if (operator) {
3553 instance.fatal(startIndex, 'Invalid syntax.');
3554 }
3555 } // 没匹配到 token 或 operator 则跳出循环
3556
3557
3558 break;
3559 } // 类似 a + b * c 这种走到这会有 11 个
3560 // 此时需要从后往前遍历,因为确定后面的优先级肯定大于前面的
3561
3562
3563 while (TRUE) {
3564 // 最少的情况是 a + b,它有 7 个元素
3565 if (output.length >= 7) {
3566 index = output.length - 4;
3567 output.splice(index - 2, 5, createBinary(output[index - 2], output[index], output[index + 2], instance.pick(output[index - 3], output[index + 3])));
3568 } else {
3569 return output[1];
3570 }
3571 }
3572 };
3573 /**
3574 * 扫描三元运算
3575 *
3576 * @param endCode
3577 */
3578
3579
3580 Parser.prototype.scanTernary = function (endCode) {
3581 /**
3582 * https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
3583 *
3584 * ?: 运算符的优先级几乎是最低的,比它低的只有四种: 赋值、yield、延展、逗号
3585 * 我们不支持这四种,因此可认为 ?: 优先级最低
3586 */
3587 var instance = this;
3588 instance.skip();
3589 var index = instance.index,
3590 test = instance.scanBinary(index),
3591 yes,
3592 no;
3593
3594 if (instance.is(CODE_QUESTION)) {
3595 // 跳过 ?
3596 instance.go();
3597 yes = instance.scanBinary(index);
3598
3599 if (instance.is(CODE_COLON)) {
3600 // 跳过 :
3601 instance.go();
3602 no = instance.scanBinary(index);
3603 }
3604
3605 if (test && yes && no) {
3606 // 类似 ' a ? 1 : 0 ' 这样的右侧有空格,需要撤回来
3607 instance.skip(MINUS_ONE);
3608 test = createTernary(test, yes, no, instance.pick(index));
3609 } else {
3610 // 三元表达式语法错误
3611 instance.fatal(index, "Invalid ternary syntax.");
3612 }
3613 } // 过掉结束字符
3614
3615
3616 if (isDef(endCode)) {
3617 instance.skip();
3618
3619 if (instance.is(endCode)) {
3620 instance.go();
3621 } // 没匹配到结束字符要报错
3622 else {
3623 instance.fatal(index, "\"" + String.fromCharCode(endCode) + "\" expected, \"" + String.fromCharCode(instance.code) + "\" actually.");
3624 }
3625 }
3626
3627 return test;
3628 };
3629
3630 Parser.prototype.fatal = function (start, message) {
3631 {
3632 fatal("Error compiling expression\n\n" + this.content + "\n\nmessage: " + message + "\n");
3633 }
3634 };
3635
3636 return Parser;
3637 }();
3638
3639 var cache = {},
3640 CODE_EOF = 0,
3641 //
3642 CODE_DOT = 46,
3643 // .
3644 CODE_COMMA = 44,
3645 // ,
3646 CODE_SLASH = 47,
3647 // /
3648 CODE_BACKSLASH = 92,
3649 // \
3650 CODE_SQUOTE = 39,
3651 // '
3652 CODE_DQUOTE = 34,
3653 // "
3654 CODE_OPAREN = 40,
3655 // (
3656 CODE_CPAREN = 41,
3657 // )
3658 CODE_OBRACK = 91,
3659 // [
3660 CODE_CBRACK = 93,
3661 // ]
3662 CODE_OBRACE = 123,
3663 // {
3664 CODE_CBRACE = 125,
3665 // }
3666 CODE_QUESTION = 63,
3667 // ?
3668 CODE_COLON = 58,
3669 // :
3670 CODE_PLUS = 43,
3671 // +
3672 CODE_MINUS = 45,
3673 // -
3674 CODE_MULTIPLY = 42,
3675 // *
3676 CODE_DIVIDE = 47,
3677 // /
3678 CODE_MODULO = 37,
3679 // %
3680 CODE_WAVE = 126,
3681 // ~
3682 CODE_AND = 38,
3683 // &
3684 CODE_OR = 124,
3685 // |
3686 CODE_XOR = 94,
3687 // ^
3688 CODE_NOT = 33,
3689 // !
3690 CODE_LESS = 60,
3691 // <
3692 CODE_EQUAL = 61,
3693 // =
3694 CODE_GREAT = 62,
3695 // >
3696
3697 /**
3698 * 区分关键字和普通变量
3699 * 举个例子:a === true
3700 * 从解析器的角度来说,a 和 true 是一样的 token
3701 */
3702 keywordLiterals = {};
3703 keywordLiterals[RAW_TRUE] = TRUE;
3704 keywordLiterals[RAW_FALSE] = FALSE;
3705 keywordLiterals[RAW_NULL] = NULL;
3706 keywordLiterals[RAW_UNDEFINED] = UNDEFINED;
3707 /**
3708 * 是否是空白符,用下面的代码在浏览器测试一下
3709 *
3710 * ```
3711 * for (var i = 0; i < 200; i++) {
3712 * console.log(i, String.fromCharCode(i))
3713 * }
3714 * ```
3715 *
3716 * 从 0 到 32 全是空白符,100 往上分布比较散且较少用,唯一需要注意的是 160
3717 *
3718 * 160 表示 non-breaking space
3719 * http://www.adamkoch.com/2009/07/25/white-space-and-character-160/
3720 */
3721
3722 function isWhitespace(code) {
3723 return code > 0 && code < 33 || code === 160;
3724 }
3725 /**
3726 * 是否是数字
3727 */
3728
3729
3730 function isDigit(code) {
3731 return code > 47 && code < 58; // 0...9
3732 }
3733 /**
3734 * 是否是数字
3735 */
3736
3737
3738 function isNumber(code) {
3739 return isDigit(code) || code === CODE_DOT;
3740 }
3741 /**
3742 * 变量开始字符必须是 字母、下划线、$
3743 */
3744
3745
3746 function isIdentifierStart(code) {
3747 return code === 36 // $
3748 || code === 95 // _
3749 || code > 96 && code < 123 // a...z
3750 || code > 64 && code < 91; // A...Z
3751 }
3752 /**
3753 * 变量剩余的字符必须是 字母、下划线、$、数字
3754 */
3755
3756
3757 function isIdentifierPart(code) {
3758 return isIdentifierStart(code) || isDigit(code);
3759 } // 当前不位于 block 之间
3760
3761
3762 var BLOCK_MODE_NONE = 1,
3763 // {{ x }}
3764 BLOCK_MODE_SAFE = 2,
3765 // {{{ x }}}
3766 BLOCK_MODE_UNSAFE = 3,
3767 // 缓存编译正则
3768 patternCache$1 = {},
3769 // 指令分隔符,如 on-click 和 lazy-click
3770 directiveSeparator = '-',
3771 // 调用的方法
3772 methodPattern = /^[_$a-z]([\w]+)?$/,
3773 // 没有命名空间的事件
3774 eventPattern = /^[_$a-z]([\w]+)?$/i,
3775 // 有命名空间的事件
3776 eventNamespacePattern = /^[_$a-z]([\w]+)?\.[_$a-z]([\w]+)?$/i,
3777 // 换行符
3778 // 比较神奇是,有时候你明明看不到换行符,却真的存在一个,那就是 \r
3779 breaklinePattern = /^\s*[\n\r]\s*|\s*[\n\r]\s*$/g,
3780 // 区间遍历
3781 rangePattern = /\s*(=>|->)\s*/,
3782 // 标签
3783 tagPattern = /<(\/)?([$a-z][-a-z0-9]*)/i,
3784 // 注释
3785 commentPattern = /<!--[\s\S]*?-->/g,
3786 // 开始注释
3787 openCommentPattern = /^([\s\S]*?)<!--/,
3788 // 结束注释
3789 closeCommentPattern = /-->([\s\S]*?)$/,
3790 // 属性的 name
3791 // 支持 on-click.namespace="" 或 on-get-out="" 或 xml:xx=""
3792 attributePattern = /^\s*([-.:\w]+)(['"])?(?:=(['"]))?/,
3793 // 自闭合标签
3794 selfClosingTagPattern = /^\s*(\/)?>/;
3795 /**
3796 * 截取前缀之后的字符串
3797 */
3798
3799 function slicePrefix(str, prefix) {
3800 return trim(slice(str, prefix.length));
3801 }
3802
3803 function compile$1(content) {
3804 var nodeList = [],
3805 nodeStack = [],
3806 // 持有 if/elseif/else 节点
3807 ifStack = [],
3808 currentElement,
3809 currentAttribute,
3810 length = content.length,
3811 // 当前处理的位置
3812 index = 0,
3813 // 下一段开始的位置
3814 nextIndex = 0,
3815 // 开始定界符的位置,表示的是 {{ 的右侧位置
3816 openBlockIndex = 0,
3817 // 结束定界符的位置,表示的是 }} 的左侧位置
3818 closeBlockIndex = 0,
3819 // 当前正在处理或即将处理的 block 类型
3820 blockMode = BLOCK_MODE_NONE,
3821 // mustache 注释可能出现嵌套插值的情况
3822 blockStack = [],
3823 indexList = [],
3824 code,
3825 startQuote,
3826 fatal$1 = function (msg) {
3827 {
3828 fatal("Error compiling template\n\n" + content + "\n\nmessage: " + msg);
3829 }
3830 },
3831
3832 /**
3833 * 常见的两种情况:
3834 *
3835 * <div>
3836 * <input>1
3837 * </div>
3838 *
3839 * <div>
3840 * <input>
3841 * </div>
3842 */
3843 popSelfClosingElementIfNeeded = function (popingTagName) {
3844 var lastNode = last(nodeStack);
3845
3846 if (lastNode && lastNode.type === ELEMENT) {
3847 var element = lastNode;
3848
3849 if (element.tag !== popingTagName && isSelfClosing(element.tag)) {
3850 popStack(element.type, element.tag);
3851 }
3852 }
3853 },
3854 popStack = function (type, tagName) {
3855 var node = pop(nodeStack);
3856
3857 if (node && node.type === type) {
3858 var children = node.children,
3859 // 优化单个子节点
3860 child = children && children.length === 1 && children[0],
3861 isElement = type === ELEMENT,
3862 isAttribute = type === ATTRIBUTE,
3863 isProperty = type === PROPERTY,
3864 isDirective = type === DIRECTIVE;
3865 var currentBranch = last(nodeStack);
3866
3867 if (currentBranch) {
3868 if (currentBranch.isStatic && !node.isStatic) {
3869 currentBranch.isStatic = FALSE;
3870 }
3871
3872 if (!currentBranch.isComplex) {
3873 if (node.isComplex || isElement) {
3874 currentBranch.isComplex = TRUE;
3875 } // <div {{#if xx}} xx{{/if}}>
3876 else if (currentElement && currentElement !== currentBranch && (isAttribute || isProperty || isDirective)) {
3877 currentBranch.isComplex = TRUE;
3878 }
3879 }
3880 }
3881
3882 {
3883 if (isElement) {
3884 var element = node;
3885
3886 if (tagName && element.tag !== tagName) {
3887 fatal$1("End tag is \"" + tagName + "\"\uFF0Cbut start tag is \"" + element.tag + "\".");
3888 }
3889 }
3890 } // 除了 helper.specialAttrs 里指定的特殊属性,attrs 里的任何节点都不能单独拎出来赋给 element
3891 // 因为 attrs 可能存在 if,所以每个 attr 最终都不一定会存在
3892
3893 if (child) {
3894 switch (child.type) {
3895 case TEXT:
3896 // 属性的值如果是纯文本,直接获取文本值
3897 // 减少渲染时的遍历
3898 if (isElement) {
3899 processElementSingleText(node, child);
3900 } else if (isAttribute) {
3901 processAttributeSingleText(node, child);
3902 } else if (isProperty) {
3903 processPropertySingleText(node, child);
3904 } else if (isDirective) {
3905 processDirectiveSingleText(node, child);
3906 }
3907
3908 break;
3909
3910 case EXPRESSION:
3911 if (isElement) {
3912 processElementSingleExpression(node, child);
3913 } else if (isAttribute) {
3914 processAttributeSingleExpression(node, child);
3915 } else if (isProperty) {
3916 processPropertySingleExpression(node, child);
3917 } else if (isDirective) {
3918 processDirectiveSingleExpression();
3919 }
3920
3921 break;
3922 }
3923 } // 大于 1 个子节点,即有插值或 if 写法
3924 else if (children) {
3925 if (isDirective) {
3926 processDirectiveMultiChildren();
3927 } // 元素层级
3928 else if (!currentElement) {
3929 removeComment(children);
3930
3931 if (!children.length) {
3932 node.children = UNDEFINED;
3933 }
3934 }
3935 } // 0 个子节点
3936 else if (currentElement) {
3937 if (isAttribute) {
3938 processAttributeEmptyChildren(currentElement, node);
3939 } else if (isProperty) {
3940 processPropertyEmptyChildren(currentElement, node);
3941 } else if (isDirective) {
3942 processDirectiveEmptyChildren(currentElement, node);
3943 }
3944 }
3945
3946 if (type === EACH) {
3947 checkEach(node);
3948 } else if (type === PARTIAL) {
3949 checkPartial(node);
3950 } else if (isElement) {
3951 checkElement(node);
3952 } else if (currentElement) {
3953 if (isAttribute) {
3954 if (isSpecialAttr(currentElement, node)) {
3955 bindSpecialAttr(currentElement, node);
3956 }
3957 } else if (isDirective) {
3958 checkDirective(currentElement, node);
3959 }
3960 }
3961
3962 return node;
3963 } // 出栈节点类型不匹配
3964
3965
3966 {
3967 fatal$1("The type of poping node is not expected.");
3968 }
3969 },
3970 removeComment = function (children) {
3971 // 类似 <!-- xx {{name}} yy {{age}} zz --> 这样的注释里包含插值
3972 // 按照目前的解析逻辑,是根据定界符进行模板分拆
3973 // 一旦出现插值,children 长度必然大于 1
3974 var openIndex = MINUS_ONE,
3975 openText = EMPTY_STRING,
3976 closeIndex = MINUS_ONE,
3977 closeText = EMPTY_STRING;
3978 each(children, function (child, index) {
3979 if (child.type === TEXT) {
3980 // 有了结束 index,这里的任务是配对开始 index
3981 if (closeIndex >= 0) {
3982 openText = child.text; // 处理 <!-- <!-- 这样有多个的情况
3983
3984 while (openCommentPattern.test(openText)) {
3985 openText = RegExp.$1;
3986 openIndex = index;
3987 }
3988
3989 if (openIndex >= 0) {
3990 // openIndex 肯定小于 closeIndex,因为完整的注释在解析过程中会被干掉
3991 // 只有包含插值的注释才会走进这里
3992 var startIndex = openIndex,
3993 endIndex = closeIndex; // 现在要确定开始和结束的文本节点,是否包含正常文本
3994
3995 if (openText) {
3996 children[openIndex].text = openText;
3997 startIndex++;
3998 }
3999
4000 if (closeText) {
4001 // 合并开始和结束文本,如 1<!-- {{x}}{{y}} -->2
4002 // 这里要把 1 和 2 两个文本节点合并成一个
4003 if (openText) {
4004 children[openIndex].text += closeText;
4005 } else {
4006 children[closeIndex].text = closeText;
4007 endIndex--;
4008 }
4009 }
4010
4011 children.splice(startIndex, endIndex - startIndex + 1); // 重置,再继续寻找结束 index
4012
4013 openIndex = closeIndex = MINUS_ONE;
4014 }
4015 } else {
4016 // 从后往前遍历
4017 // 一旦发现能匹配 --> 就可以断定这是注释的结束 index
4018 // 剩下的就是找开始 index
4019 closeText = child.text; // 处理 --> --> 这样有多个的情况
4020
4021 while (closeCommentPattern.test(closeText)) {
4022 closeText = RegExp.$1;
4023 closeIndex = index;
4024 }
4025 }
4026 }
4027 }, TRUE);
4028 },
4029 processDirectiveMultiChildren = function () {
4030 // 不支持 on-click="1{{xx}}2" 或是 on-click="1{{#if x}}x{{else}}y{{/if}}2"
4031 // 1. 很难做性能优化
4032 // 2. 全局搜索不到事件名,不利于代码维护
4033 // 3. 不利于编译成静态函数
4034 {
4035 fatal$1('For performance, "{{" and "}}" are not allowed in directive value.');
4036 }
4037 },
4038 processElementSingleText = function (element, child) {
4039 // processElementSingleText 和 processElementSingleExpression
4040 // 不把元素子节点智能转换为 textContent property
4041 // 因为子节点还有 <div>1{{a}}{{b}}</div> 这样的情况
4042 // 还是在序列化的时候统一处理比较好
4043 // 唯独需要在这特殊处理的是 html 实体
4044 // 但这只是 WEB 平台的特殊逻辑,所以丢给 platform 处理
4045 if (!element.isComponent && !specialTags[element.tag] && setElementText(element, child.text)) {
4046 element.children = UNDEFINED;
4047 }
4048 },
4049 processElementSingleExpression = function (element, child) {
4050 if (!element.isComponent && !specialTags[element.tag] && !child.safe) {
4051 element.html = child.expr;
4052 element.children = UNDEFINED;
4053 }
4054 },
4055 processPropertyEmptyChildren = function (element, prop) {
4056 if (prop.hint === HINT_BOOLEAN) {
4057 prop.value = TRUE;
4058 } else {
4059 // string 或 number 类型的属性,如果不写值,直接忽略
4060 replaceChild(prop);
4061 }
4062 },
4063 processPropertySingleText = function (prop, child) {
4064 var text = child.text;
4065
4066 if (prop.hint === HINT_NUMBER) {
4067 prop.value = toNumber(text);
4068 } else if (prop.hint === HINT_BOOLEAN) {
4069 prop.value = text === RAW_TRUE || text === prop.name;
4070 } else {
4071 prop.value = text;
4072 }
4073
4074 prop.children = UNDEFINED;
4075 },
4076 processPropertySingleExpression = function (prop, child) {
4077 var expr = child.expr;
4078 prop.expr = expr;
4079 prop.children = UNDEFINED; // 对于有静态路径的表达式,可转为单向绑定指令,可实现精确更新视图,如下
4080 // <div class="{{className}}">
4081
4082 if (expr.type === IDENTIFIER) {
4083 prop.binding = TRUE;
4084 }
4085 },
4086 processAttributeEmptyChildren = function (element, attr) {
4087 if (isSpecialAttr(element, attr)) {
4088 {
4089 fatal$1("The value of \"" + attr.name + "\" is empty.");
4090 }
4091 } else {
4092 attr.value = getAttributeDefaultValue(element, attr.name);
4093 }
4094 },
4095 processAttributeSingleText = function (attr, child) {
4096 attr.value = child.text;
4097 attr.children = UNDEFINED;
4098 },
4099 processAttributeSingleExpression = function (attr, child) {
4100 var expr = child.expr;
4101 attr.expr = expr;
4102 attr.children = UNDEFINED; // 对于有静态路径的表达式,可转为单向绑定指令,可实现精确更新视图,如下
4103 // <div class="{{className}}">
4104
4105 if (expr.type === IDENTIFIER) {
4106 attr.binding = TRUE;
4107 }
4108 },
4109 processDirectiveEmptyChildren = function (element, directive) {
4110 directive.value = TRUE;
4111 },
4112 processDirectiveSingleText = function (directive, child) {
4113 var text = child.text,
4114 // model="xx" model="this.x" 值只能是标识符或 Member
4115 isModel = directive.ns === DIRECTIVE_MODEL,
4116 // lazy 的值必须是大于 0 的数字
4117 isLazy = directive.ns === DIRECTIVE_LAZY,
4118 // 校验事件名称
4119 // 且命名空间不能用 native
4120 isEvent = directive.ns === DIRECTIVE_EVENT,
4121 // 自定义指令运行不合法的表达式
4122 isCustom = directive.ns === DIRECTIVE_CUSTOM,
4123 // 指令的值是纯文本,可以预编译表达式,提升性能
4124 expr,
4125 error;
4126
4127 try {
4128 expr = compile(text);
4129 } catch (e) {
4130 error = e;
4131 }
4132
4133 if (expr) {
4134 {
4135 var raw = expr.raw;
4136
4137 if (isLazy) {
4138 if (expr.type !== LITERAL || !number(expr.value) || expr.value <= 0) {
4139 fatal$1('The value of lazy must be a number greater than 0.');
4140 }
4141 } // 如果指令表达式是函数调用,则只能调用方法(难道还有别的可以调用的吗?)
4142 else if (expr.type === CALL) {
4143 var methodName = expr.name;
4144
4145 if (methodName.type !== IDENTIFIER) {
4146 fatal$1('Invalid method name.');
4147 } // 函数调用调用方法,因此不能是 a.b() 的形式
4148 else if (!methodPattern.test(methodName.name)) {
4149 fatal$1('Invalid method name.');
4150 }
4151 } // 上面检测过方法调用,接下来事件指令只需要判断是否以下两种格式:
4152 // on-click="name" 或 on-click="name.namespace"
4153 else if (isEvent) {
4154 if (eventPattern.test(raw) || eventNamespacePattern.test(raw)) {
4155 // native 有特殊用处,不能给业务层用
4156 if (eventNamespacePattern.test(raw) && raw.split(RAW_DOT)[1] === MODIFER_NATIVE) {
4157 fatal$1("The event namespace \"" + MODIFER_NATIVE + "\" is not permitted.");
4158 } // <Button on-click="click"> 这种写法没有意义
4159
4160
4161 if (currentElement && currentElement.isComponent && directive.name === raw) {
4162 fatal$1("The event name listened and fired can't be the same.");
4163 }
4164 } // 事件转换名称只能是 [name] 或 [name.namespace] 格式
4165 else {
4166 fatal$1('The event name and namespace must be an identifier.');
4167 }
4168 }
4169
4170 if (isModel && expr.type !== IDENTIFIER) {
4171 fatal$1('The value of the model must be an identifier.');
4172 }
4173 }
4174 directive.expr = expr;
4175 directive.value = expr.type === LITERAL ? expr.value : text;
4176 } else {
4177 // 自定义指令支持错误的表达式
4178 // 反正是自定义的规则,爱怎么写就怎么写
4179 {
4180 if (!isCustom) {
4181 throw error;
4182 }
4183 }
4184 directive.value = text;
4185 }
4186
4187 directive.children = UNDEFINED;
4188 },
4189 processDirectiveSingleExpression = function (directive, child) {
4190 {
4191 fatal$1('For performance, "{{" and "}}" are not allowed in directive value.');
4192 }
4193 },
4194 checkCondition = function (condition) {
4195 var currentNode = condition,
4196 prevNode,
4197 hasChildren,
4198 hasNext;
4199
4200 while (TRUE) {
4201 if (currentNode.children) {
4202 if (!hasNext) {
4203 if (currentNode.next) {
4204 delete currentNode.next;
4205 }
4206 }
4207
4208 hasChildren = hasNext = TRUE;
4209 }
4210
4211 prevNode = currentNode.prev;
4212
4213 if (prevNode) {
4214 // prev 仅仅用在 checkCondition 函数中
4215 // 用完就可以删掉了
4216 delete currentNode.prev;
4217 currentNode = prevNode;
4218 } else {
4219 break;
4220 }
4221 } // 每个条件都是空内容,则删掉整个 if
4222
4223
4224 if (!hasChildren) {
4225 replaceChild(currentNode);
4226 }
4227 },
4228 checkEach = function (each) {
4229 // 没内容就干掉
4230 if (!each.children) {
4231 replaceChild(each);
4232 }
4233 },
4234 checkPartial = function (partial) {
4235 // 没内容就干掉
4236 if (!partial.children) {
4237 replaceChild(partial);
4238 }
4239 },
4240 checkElement = function (element) {
4241 var tag = element.tag,
4242 slot = element.slot,
4243 isTemplate = tag === RAW_TEMPLATE;
4244 {
4245 if (isTemplate) {
4246 if (element.key) {
4247 fatal$1("The \"key\" is not supported in <template>.");
4248 } else if (element.ref) {
4249 fatal$1("The \"ref\" is not supported in <template>.");
4250 } else if (element.attrs) {
4251 fatal$1("The attributes and directives are not supported in <template>.");
4252 } else if (!slot) {
4253 fatal$1("The \"slot\" is required in <template>.");
4254 }
4255 }
4256 } // 没有子节点,则意味着这个插槽没任何意义
4257
4258 if (isTemplate && slot && !element.children) {
4259 replaceChild(element);
4260 } // <slot /> 如果没写 name,自动加上默认名称
4261 else if (tag === RAW_SLOT && !element.name) {
4262 element.name = SLOT_NAME_DEFAULT;
4263 } else {
4264 compatElement(element);
4265 }
4266 },
4267 checkDirective = function (element, directive) {
4268 {
4269 // model 不能写在 if 里,影响节点的静态结构
4270 if (directive.ns === DIRECTIVE_MODEL) {
4271 if (last(nodeStack) !== element) {
4272 fatal$1("The \"model\" can't be used in an if block.");
4273 }
4274 }
4275 }
4276 },
4277 bindSpecialAttr = function (element, attr) {
4278 var name = attr.name,
4279 value = attr.value,
4280 // 这三个属性值要求是字符串
4281 isStringValueRequired = name === RAW_NAME || name === RAW_SLOT;
4282 {
4283 // 因为要拎出来给 element,所以不能用 if
4284 if (last(nodeStack) !== element) {
4285 fatal$1("The \"" + name + "\" can't be used in an if block.");
4286 } // 对于所有特殊属性来说,空字符串是肯定不行的,没有任何意义
4287
4288
4289 if (value === EMPTY_STRING) {
4290 fatal$1("The value of \"" + name + "\" is empty.");
4291 } else if (isStringValueRequired && falsy$1(value)) {
4292 fatal$1("The value of \"" + name + "\" can only be a string literal.");
4293 }
4294 }
4295 element[name] = isStringValueRequired ? value : attr;
4296 replaceChild(attr);
4297 },
4298 isSpecialAttr = function (element, attr) {
4299 return specialAttrs[attr.name] || element.tag === RAW_SLOT && attr.name === RAW_NAME;
4300 },
4301 replaceChild = function (oldNode, newNode) {
4302 var currentBranch = last(nodeStack),
4303 isAttr,
4304 list,
4305 index;
4306
4307 if (currentBranch) {
4308 isAttr = currentElement && currentElement === currentBranch;
4309 list = isAttr ? currentBranch.attrs : currentBranch.children;
4310 } else {
4311 list = nodeList;
4312 }
4313
4314 if (list) {
4315 index = indexOf(list, oldNode);
4316
4317 if (index >= 0) {
4318 if (newNode) {
4319 list[index] = newNode;
4320 } else {
4321 list.splice(index, 1);
4322
4323 if (currentBranch && !list.length) {
4324 if (isAttr) {
4325 delete currentBranch.attrs;
4326 } else {
4327 currentBranch.children = UNDEFINED;
4328 }
4329 }
4330 }
4331 }
4332 }
4333 },
4334 addChild = function (node) {
4335 /**
4336 * <div>
4337 * <input>
4338 * <div></div>
4339 * </div>
4340 *
4341 * <div>
4342 * <input>xxx
4343 * </div>
4344 */
4345 if (!currentElement) {
4346 popSelfClosingElementIfNeeded();
4347 }
4348
4349 var type = node.type,
4350 currentBranch = last(nodeStack); // else 系列只是 if 的递进节点,不需要加入 nodeList
4351
4352 if (type === ELSE || type === ELSE_IF) {
4353 var lastNode = pop(ifStack);
4354
4355 if (lastNode) {
4356 // 方便 checkCondition 逆向遍历
4357 node.prev = lastNode; // lastNode 只能是 if 或 else if 节点
4358
4359 if (lastNode.type === ELSE_IF || lastNode.type === IF) {
4360 lastNode.next = node;
4361 popStack(lastNode.type);
4362 push(ifStack, node);
4363 } else if (type === ELSE_IF) {
4364 {
4365 fatal$1('The "else" block must not be followed by an "else if" block.');
4366 }
4367 } else {
4368 fatal$1("The \"else\" block can't appear more than once in a conditional statement.");
4369 }
4370 } else {
4371 fatal$1('The "if" block is required.');
4372 }
4373 } else {
4374 if (currentBranch) {
4375 // 这里不能写 currentElement && !currentAttribute,举个例子
4376 //
4377 // <div id="x" {{#if}} name="xx" alt="xx" {{/if}}
4378 //
4379 // 当 name 属性结束后,条件满足,但此时已不是元素属性层级了
4380 if (currentElement && currentBranch.type === ELEMENT) {
4381 var attrs = currentElement.attrs || (currentElement.attrs = []); // node 没法转型,一堆可能的类型怎么转啊...
4382
4383 push(attrs, node);
4384 } else {
4385 var children = currentBranch.children || (currentBranch.children = []),
4386 lastChild = last(children); // 连续添加文本节点,则直接合并
4387
4388 if (lastChild && lastChild.type === TEXT && node.type === TEXT) {
4389 lastChild.text += node.text;
4390 return;
4391 } else {
4392 push(children, node);
4393 }
4394 }
4395 } else {
4396 push(nodeList, node);
4397 }
4398
4399 if (type === IF) {
4400 // 只要是 if 节点,并且和 element 同级,就加上 stub
4401 // 方便 virtual dom 进行对比
4402 // 这个跟 virtual dom 的实现原理密切相关,不加 stub 会有问题
4403 if (!currentElement) {
4404 node.stub = TRUE;
4405 }
4406
4407 push(ifStack, node);
4408 }
4409 }
4410
4411 if (node.isLeaf) {
4412 // 当前树枝节点如果是静态的,一旦加入了一个非静态子节点,改变当前树枝节点的 isStatic
4413 // 这里不处理树枝节点的进栈,因为当树枝节点出栈时,还有一次处理机会,那时它的 isStatic 已确定下来,不会再变
4414 if (currentBranch) {
4415 if (currentBranch.isStatic && !node.isStatic) {
4416 currentBranch.isStatic = FALSE;
4417 } // 当前树枝节点是简单节点,一旦加入了一个复杂子节点,当前树枝节点变为复杂节点
4418
4419
4420 if (!currentBranch.isComplex && node.isComplex) {
4421 currentBranch.isComplex = TRUE;
4422 }
4423 }
4424 } else {
4425 push(nodeStack, node);
4426 }
4427 },
4428 addTextChild = function (text) {
4429 // [注意]
4430 // 这里不能随便删掉
4431 // 因为收集组件的子节点会受影响,举个例子:
4432 // <Component>
4433 //
4434 // </Component>
4435 // 按现在的逻辑,这样的组件是没有子节点的,因为在这里过滤掉了,因此该组件没有 slot
4436 // 如果这里放开了,组件就会有一个 slot
4437 // trim 文本开始和结束位置的换行符
4438 text = text.replace(breaklinePattern, EMPTY_STRING);
4439
4440 if (text) {
4441 addChild(createText(text));
4442 }
4443 },
4444 htmlParsers = [function (content) {
4445 if (!currentElement) {
4446 var match = content.match(tagPattern); // 必须以 <tag 开头才能继续
4447 // 如果 <tag 前面有别的字符,会走进第四个 parser
4448
4449 if (match && match.index === 0) {
4450 var tag = match[2];
4451
4452 if (match[1] === RAW_SLASH) {
4453 /**
4454 * 处理可能存在的自闭合元素,如下
4455 *
4456 * <div>
4457 * <input>
4458 * </div>
4459 */
4460 popSelfClosingElementIfNeeded(tag);
4461 popStack(ELEMENT, tag);
4462 } else {
4463 /**
4464 * template 只能写在组件的第一级,如下:
4465 *
4466 * <Component>
4467 * <template slot="xx">
4468 * 111
4469 * </template>
4470 * </Component>
4471 */
4472 {
4473 if (tag === RAW_TEMPLATE) {
4474 var lastNode = last(nodeStack);
4475
4476 if (!lastNode || !lastNode.isComponent) {
4477 fatal$1('<template> can only be used within an component children.');
4478 }
4479 }
4480 }
4481 var node = createElement$1(tag);
4482 addChild(node);
4483 currentElement = node;
4484 }
4485
4486 return match[0];
4487 }
4488 }
4489 }, // 处理标签的 > 或 />,不论开始还是结束标签
4490 function (content) {
4491 var match = content.match(selfClosingTagPattern);
4492
4493 if (match) {
4494 // 处理开始标签的 > 或 />
4495 if (currentElement && !currentAttribute) {
4496 // 自闭合标签
4497 if (match[1] === RAW_SLASH) {
4498 popStack(currentElement.type, currentElement.tag);
4499 }
4500
4501 currentElement = UNDEFINED;
4502 } // 处理结束标签的 >
4503
4504
4505 return match[0];
4506 }
4507 }, // 处理 attribute directive 的 name 部分
4508 function (content) {
4509 // 当前在 element 层级
4510 if (currentElement && !currentAttribute) {
4511 var match = content.match(attributePattern);
4512
4513 if (match) {
4514 // <div class="11 name="xxx"></div>
4515 // 这里会匹配上 xxx",match[2] 就是那个引号
4516 {
4517 if (match[2]) {
4518 fatal$1("The previous attribute is not end.");
4519 }
4520 }
4521 var node,
4522 name = match[1];
4523
4524 if (name === DIRECTIVE_MODEL || name === RAW_TRANSITION) {
4525 node = createDirective(EMPTY_STRING, name);
4526 } // 这里要用 on- 判断前缀,否则 on 太容易重名了
4527 else if (startsWith(name, DIRECTIVE_ON + directiveSeparator)) {
4528 var event = slicePrefix(name, DIRECTIVE_ON + directiveSeparator);
4529 {
4530 if (!event) {
4531 fatal$1('The event name is required.');
4532 }
4533 }
4534
4535 var _a = camelize(event).split(RAW_DOT),
4536 directiveName = _a[0],
4537 diectiveModifier = _a[1],
4538 extra = _a[2];
4539
4540 node = createDirective(directiveName, DIRECTIVE_EVENT, diectiveModifier); // on-a.b.c
4541
4542 {
4543 if (string(extra)) {
4544 fatal$1('Invalid event namespace.');
4545 }
4546 }
4547 } // 当一个元素绑定了多个事件时,可分别指定每个事件的 lazy
4548 // 当只有一个事件时,可简写成 lazy
4549 // <div on-click="xx" lazy-click
4550 else if (startsWith(name, DIRECTIVE_LAZY)) {
4551 var lazy = slicePrefix(name, DIRECTIVE_LAZY);
4552
4553 if (startsWith(lazy, directiveSeparator)) {
4554 lazy = slicePrefix(lazy, directiveSeparator);
4555 }
4556
4557 node = createDirective(lazy ? camelize(lazy) : EMPTY_STRING, DIRECTIVE_LAZY);
4558 } // 这里要用 o- 判断前缀,否则 o 太容易重名了
4559 else if (startsWith(name, DIRECTIVE_CUSTOM + directiveSeparator)) {
4560 var custom = slicePrefix(name, DIRECTIVE_CUSTOM + directiveSeparator);
4561 {
4562 if (!custom) {
4563 fatal$1('The directive name is required.');
4564 }
4565 }
4566
4567 var _b = camelize(custom).split(RAW_DOT),
4568 directiveName = _b[0],
4569 diectiveModifier = _b[1],
4570 extra = _b[2];
4571
4572 node = createDirective(directiveName, DIRECTIVE_CUSTOM, diectiveModifier); // o-a.b.c
4573
4574 {
4575 if (string(extra)) {
4576 fatal$1('Invalid directive modifier.');
4577 }
4578 }
4579 } else {
4580 node = createAttribute$1(currentElement, name);
4581 }
4582
4583 addChild(node); // 这里先记下,下一个 handler 要匹配结束引号
4584
4585 startQuote = match[3]; // 有属性值才需要设置 currentAttribute,便于后续收集属性值
4586
4587 if (startQuote) {
4588 currentAttribute = node;
4589 } else {
4590 popStack(node.type);
4591 }
4592
4593 return match[0];
4594 }
4595 }
4596 }, function (content) {
4597 var text, match; // 处理 attribute directive 的 value 部分
4598
4599 if (currentAttribute && startQuote) {
4600 match = content.match(patternCache$1[startQuote] || (patternCache$1[startQuote] = new RegExp(startQuote))); // 有结束引号
4601
4602 if (match) {
4603 text = slice(content, 0, match.index);
4604 addTextChild(text);
4605 text += startQuote; // attribute directive 结束了
4606 // 此时如果一个值都没收集到,需设置一个空字符串
4607 // 否则无法区分 <div a b=""> 中的 a 和 b
4608
4609 if (!currentAttribute.children) {
4610 addChild(createText(EMPTY_STRING));
4611 }
4612
4613 popStack(currentAttribute.type);
4614 currentAttribute = UNDEFINED;
4615 } // 没有结束引号,整段匹配
4616 // 如 id="1{{x}}2" 中的 1
4617 else if (blockMode !== BLOCK_MODE_NONE) {
4618 text = content;
4619 addTextChild(text);
4620 } // 没找到结束引号
4621 else {
4622 fatal$1("Unterminated quoted string in \"" + currentAttribute.name + "\".");
4623 }
4624 } // 如果不加判断,类似 <div {{...obj}}> 这样写,会把空格当做一个属性
4625 // 收集文本只有两处:属性值、元素内容
4626 // 属性值通过上面的 if 处理过了,这里只需要处理元素内容
4627 else if (!currentElement) {
4628 // 获取 <tag 前面的字符
4629 match = content.match(tagPattern); // 元素层级的注释都要删掉
4630
4631 if (match) {
4632 text = slice(content, 0, match.index);
4633
4634 if (text) {
4635 addTextChild(text.replace(commentPattern, EMPTY_STRING));
4636 }
4637 } else {
4638 text = content;
4639 addTextChild(text.replace(commentPattern, EMPTY_STRING));
4640 }
4641 } else {
4642 {
4643 if (trim(content)) {
4644 fatal$1("Invalid character is found in <" + currentElement.tag + "> attribute level.");
4645 }
4646 }
4647 text = content;
4648 }
4649
4650 return text;
4651 }],
4652 blockParsers = [// {{#each xx:index}}
4653 function (source) {
4654 if (startsWith(source, SYNTAX_EACH)) {
4655 {
4656 if (currentElement) {
4657 fatal$1(currentAttribute ? "The \"each\" block can't be appear in an attribute value." : "The \"each\" block can't be appear in attribute level.");
4658 }
4659 }
4660 source = slicePrefix(source, SYNTAX_EACH);
4661 var terms = source.replace(/\s+/g, EMPTY_STRING).split(':');
4662
4663 if (terms[0]) {
4664 var literal = trim(terms[0]),
4665 index_1 = terms[1] ? trim(terms[1]) : UNDEFINED,
4666 match = literal.match(rangePattern);
4667
4668 if (match) {
4669 var parts = literal.split(rangePattern),
4670 from = compile(parts[0]),
4671 to = compile(parts[2]);
4672
4673 if (from && to) {
4674 return createEach(from, to, trim(match[1]) === '=>', index_1);
4675 }
4676 } else {
4677 var expr = compile(literal);
4678
4679 if (expr) {
4680 return createEach(expr, UNDEFINED, FALSE, index_1);
4681 }
4682 }
4683 }
4684
4685 {
4686 fatal$1("Invalid each");
4687 }
4688 }
4689 }, // {{#import name}}
4690 function (source) {
4691 if (startsWith(source, SYNTAX_IMPORT)) {
4692 source = slicePrefix(source, SYNTAX_IMPORT);
4693
4694 if (source) {
4695 if (!currentElement) {
4696 return createImport(source);
4697 } else {
4698 fatal$1(currentAttribute ? "The \"import\" block can't be appear in an attribute value." : "The \"import\" block can't be appear in attribute level.");
4699 }
4700 }
4701
4702 {
4703 fatal$1("Invalid import");
4704 }
4705 }
4706 }, // {{#partial name}}
4707 function (source) {
4708 if (startsWith(source, SYNTAX_PARTIAL)) {
4709 source = slicePrefix(source, SYNTAX_PARTIAL);
4710
4711 if (source) {
4712 if (!currentElement) {
4713 return createPartial(source);
4714 } else {
4715 fatal$1(currentAttribute ? "The \"partial\" block can't be appear in an attribute value." : "The \"partial\" block can't be appear in attribute level.");
4716 }
4717 }
4718
4719 {
4720 fatal$1("Invalid partial");
4721 }
4722 }
4723 }, // {{#if expr}}
4724 function (source) {
4725 if (startsWith(source, SYNTAX_IF)) {
4726 source = slicePrefix(source, SYNTAX_IF);
4727 var expr = compile(source);
4728
4729 if (expr) {
4730 return createIf(expr);
4731 }
4732
4733 {
4734 fatal$1("Invalid if");
4735 }
4736 }
4737 }, // {{else if expr}}
4738 function (source) {
4739 if (startsWith(source, SYNTAX_ELSE_IF)) {
4740 source = slicePrefix(source, SYNTAX_ELSE_IF);
4741 var expr = compile(source);
4742
4743 if (expr) {
4744 return createElseIf(expr);
4745 }
4746
4747 {
4748 fatal$1("Invalid else if");
4749 }
4750 }
4751 }, // {{else}}
4752 function (source) {
4753 if (startsWith(source, SYNTAX_ELSE)) {
4754 source = slicePrefix(source, SYNTAX_ELSE);
4755
4756 if (!trim(source)) {
4757 return createElse();
4758 }
4759
4760 {
4761 fatal$1("The \"else\" must not be followed by anything.");
4762 }
4763 }
4764 }, // {{...obj}}
4765 function (source) {
4766 if (startsWith(source, SYNTAX_SPREAD)) {
4767 source = slicePrefix(source, SYNTAX_SPREAD);
4768 var expr = compile(source);
4769
4770 if (expr) {
4771 if (currentElement && currentElement.isComponent) {
4772 return createSpread(expr, expr.type === IDENTIFIER);
4773 } else {
4774 fatal$1("The spread can only be used by a component.");
4775 }
4776 }
4777
4778 {
4779 fatal$1("Invalid spread");
4780 }
4781 }
4782 }, // {{expr}}
4783 function (source) {
4784 if (!SYNTAX_COMMENT.test(source)) {
4785 source = trim(source);
4786 var expr = compile(source);
4787
4788 if (expr) {
4789 return createExpression(expr, blockMode === BLOCK_MODE_SAFE);
4790 }
4791
4792 {
4793 fatal$1("Invalid expression");
4794 }
4795 }
4796 }],
4797 parseHtml = function (code) {
4798 while (code) {
4799 each(htmlParsers, function (parse) {
4800 var match = parse(code);
4801
4802 if (match) {
4803 code = slice(code, match.length);
4804 return FALSE;
4805 }
4806 });
4807 }
4808 },
4809 parseBlock = function (code) {
4810 if (charAt(code) === RAW_SLASH) {
4811 /**
4812 * 处理可能存在的自闭合元素,如下
4813 *
4814 * {{#if xx}}
4815 * <input>
4816 * {{/if}}
4817 */
4818 popSelfClosingElementIfNeeded();
4819 var name = slice(code, 1);
4820 var type = name2Type[name],
4821 isCondition = FALSE;
4822
4823 if (type === IF) {
4824 var node_1 = pop(ifStack);
4825
4826 if (node_1) {
4827 type = node_1.type;
4828 isCondition = TRUE;
4829 } else {
4830 fatal$1("The \"if\" block is closing, but it does't opened.");
4831 }
4832 }
4833
4834 var node = popStack(type);
4835
4836 if (node && isCondition) {
4837 checkCondition(node);
4838 }
4839 } else {
4840 // 开始下一个 block 或表达式
4841 each(blockParsers, function (parse) {
4842 var node = parse(code);
4843
4844 if (node) {
4845 addChild(node);
4846 return FALSE;
4847 }
4848 });
4849 }
4850 },
4851 closeBlock = function () {
4852 // 确定开始和结束定界符能否配对成功,即 {{ 对 }},{{{ 对 }}}
4853 // 这里不能动 openBlockIndex 和 closeBlockIndex,因为等下要用他俩 slice
4854 index = closeBlockIndex + 2; // 这里要用 <=,因为很可能到头了
4855
4856 if (index <= length) {
4857 if (index < length && charAt(content, index) === '}') {
4858 if (blockMode === BLOCK_MODE_UNSAFE) {
4859 nextIndex = index + 1;
4860 } else {
4861 fatal$1("{{ and }}} is not a pair.");
4862 }
4863 } else {
4864 if (blockMode === BLOCK_MODE_SAFE) {
4865 nextIndex = index;
4866 } else {
4867 fatal$1("{{{ and }} is not a pair.");
4868 }
4869 }
4870
4871 pop(blockStack); // }} 左侧的位置
4872
4873 addIndex(closeBlockIndex);
4874 openBlockIndex = indexOf$1(content, '{{', nextIndex);
4875 closeBlockIndex = indexOf$1(content, '}}', nextIndex); // 如果碰到连续的结束定界符,继续 close
4876
4877 if (closeBlockIndex >= nextIndex && (openBlockIndex < 0 || closeBlockIndex < openBlockIndex)) {
4878 return closeBlock();
4879 }
4880 } else {
4881 // 到头了
4882 return TRUE;
4883 }
4884 },
4885 addIndex = function (index) {
4886 if (!blockStack.length) {
4887 push(indexList, index);
4888 }
4889 }; // 因为存在 mustache 注释内包含插值的情况
4890 // 这里把流程设计为先标记切片的位置,标记过程中丢弃无效的 block
4891 // 最后处理有效的 block
4892
4893
4894 while (TRUE) {
4895 addIndex(nextIndex);
4896 openBlockIndex = indexOf$1(content, '{{', nextIndex);
4897
4898 if (openBlockIndex >= nextIndex) {
4899 blockMode = BLOCK_MODE_SAFE; // {{ 左侧的位置
4900
4901 addIndex(openBlockIndex); // 跳过 {{
4902
4903 openBlockIndex += 2; // {{ 后面总得有内容吧
4904
4905 if (openBlockIndex < length) {
4906 if (charAt(content, openBlockIndex) === '{') {
4907 blockMode = BLOCK_MODE_UNSAFE;
4908 openBlockIndex++;
4909 } // {{ 右侧的位置
4910
4911
4912 addIndex(openBlockIndex); // block 是否安全
4913
4914 addIndex(blockMode); // 打开一个 block 就入栈一个
4915
4916 push(blockStack, TRUE);
4917
4918 if (openBlockIndex < length) {
4919 closeBlockIndex = indexOf$1(content, '}}', openBlockIndex);
4920
4921 if (closeBlockIndex >= openBlockIndex) {
4922 // 注释可以嵌套,如 {{! {{xx}} {{! {{xx}} }} }}
4923 nextIndex = indexOf$1(content, '{{', openBlockIndex);
4924
4925 if (nextIndex < 0 || closeBlockIndex < nextIndex) {
4926 if (closeBlock()) {
4927 break;
4928 }
4929 }
4930 } else {
4931 fatal$1('The end delimiter is not found.');
4932 }
4933 } else {
4934 // {{{ 后面没字符串了?
4935 fatal$1('Unterminated template literal.');
4936 }
4937 } else {
4938 // {{ 后面没字符串了?
4939 fatal$1('Unterminated template literal.');
4940 }
4941 } else {
4942 break;
4943 }
4944 }
4945
4946 for (var i = 0, length_1 = indexList.length; i < length_1; i += 5) {
4947 index = indexList[i]; // {{ 左侧的位置
4948
4949 openBlockIndex = indexList[i + 1];
4950
4951 if (openBlockIndex) {
4952 parseHtml(slice(content, index, openBlockIndex));
4953 } // {{ 右侧的位置
4954
4955
4956 openBlockIndex = indexList[i + 2];
4957 blockMode = indexList[i + 3];
4958 closeBlockIndex = indexList[i + 4];
4959
4960 if (closeBlockIndex) {
4961 code = trim(slice(content, openBlockIndex, closeBlockIndex)); // 不用处理 {{ }} 和 {{{ }}} 这种空 block
4962
4963 if (code) {
4964 parseBlock(code);
4965 }
4966 } else {
4967 blockMode = BLOCK_MODE_NONE;
4968 parseHtml(slice(content, index));
4969 }
4970 }
4971
4972 if (nodeStack.length) {
4973 /**
4974 * 处理可能存在的自闭合元素,如下
4975 *
4976 * <input>
4977 */
4978 popSelfClosingElementIfNeeded();
4979 {
4980 if (nodeStack.length) {
4981 fatal$1('Some nodes is still in the stack.');
4982 }
4983 }
4984 }
4985
4986 if (nodeList.length > 0) {
4987 removeComment(nodeList);
4988 }
4989
4990 return nodeList;
4991 }
4992
4993 var UNDEFINED$1 = '$0';
4994 var TRUE$1 = '$1';
4995 var FALSE$1 = '$2';
4996 var COMMA = ',';
4997 var COLON = ':';
4998 var PLUS = '+';
4999 var AND = '&&';
5000 var QUESTION = '?';
5001 var NOT = '!';
5002 var EMPTY = '""';
5003 var RETURN = 'return ';
5004 /**
5005 * 目的是 保证调用参数顺序稳定,减少运行时判断
5006 *
5007 * [a, undefined, undefined] => [a]
5008 * [a, undefined, b, undefined] => [a, undefined, b]
5009 */
5010
5011 function trimArgs(list) {
5012 var args = [],
5013 removable = TRUE;
5014 each(list, function (arg) {
5015 if (isDef(arg)) {
5016 removable = FALSE;
5017 unshift(args, arg);
5018 } else if (!removable) {
5019 unshift(args, UNDEFINED$1);
5020 }
5021 }, TRUE);
5022 return args;
5023 }
5024
5025 function toObject$1(fields) {
5026 return "{" + join(fields, COMMA) + "}";
5027 }
5028
5029 function toArray$1(items) {
5030 return "[" + join(items, COMMA) + "]";
5031 }
5032
5033 function toCall(name, args) {
5034 return name + "(" + join(trimArgs(args), COMMA) + ")";
5035 }
5036
5037 function toString$1(value) {
5038 return JSON.stringify(value);
5039 }
5040
5041 function toFunction(args, code) {
5042 return RAW_FUNCTION + "(" + args + "){var " + UNDEFINED$1 + "=void 0," + TRUE$1 + "=!0," + FALSE$1 + "=!1;" + RETURN + code + "}";
5043 }
5044
5045 function generate(node, renderIdentifier, renderMemberKeypath, renderMemberLiteral, renderCall, holder, depIgnore, stack, inner) {
5046 var value,
5047 isSpecialNode = FALSE,
5048 generateChildNode = function (node) {
5049 return generate(node, renderIdentifier, renderMemberKeypath, renderMemberLiteral, renderCall, holder, depIgnore, stack, TRUE);
5050 };
5051
5052 switch (node.type) {
5053 case LITERAL:
5054 value = toString$1(node.value);
5055 break;
5056
5057 case UNARY:
5058 value = node.operator + generateChildNode(node.node);
5059 break;
5060
5061 case BINARY:
5062 value = generateChildNode(node.left) + node.operator + generateChildNode(node.right);
5063 break;
5064
5065 case TERNARY:
5066 value = generateChildNode(node.test) + QUESTION + generateChildNode(node.yes) + COLON + generateChildNode(node.no);
5067 break;
5068
5069 case ARRAY:
5070 var items = node.nodes.map(generateChildNode);
5071 value = toArray$1(items);
5072 break;
5073
5074 case OBJECT:
5075 var fields_1 = [];
5076 each(node.keys, function (key, index) {
5077 push(fields_1, toString$1(key) + COLON + generateChildNode(node.values[index]));
5078 });
5079 value = toObject$1(fields_1);
5080 break;
5081
5082 case IDENTIFIER:
5083 isSpecialNode = TRUE;
5084 var identifier = node;
5085 value = toCall(renderIdentifier, [toString$1(identifier.name), identifier.lookup ? TRUE$1 : UNDEFINED, identifier.offset > 0 ? toString$1(identifier.offset) : UNDEFINED, holder ? TRUE$1 : UNDEFINED, depIgnore ? TRUE$1 : UNDEFINED, stack ? stack : UNDEFINED]);
5086 break;
5087
5088 case MEMBER:
5089 isSpecialNode = TRUE;
5090 var _a = node,
5091 lead = _a.lead,
5092 keypath = _a.keypath,
5093 nodes = _a.nodes,
5094 lookup = _a.lookup,
5095 offset = _a.offset,
5096 stringifyNodes = nodes ? nodes.map(generateChildNode) : [];
5097
5098 if (lead.type === IDENTIFIER) {
5099 // 只能是 a[b] 的形式,因为 a.b 已经在解析时转换成 Identifier 了
5100 value = toCall(renderIdentifier, [toCall(renderMemberKeypath, [toString$1(lead.name), toArray$1(stringifyNodes)]), lookup ? TRUE$1 : UNDEFINED, offset > 0 ? toString$1(offset) : UNDEFINED, holder ? TRUE$1 : UNDEFINED, depIgnore ? TRUE$1 : UNDEFINED, stack ? stack : UNDEFINED]);
5101 } else if (nodes) {
5102 // "xx"[length]
5103 // format()[a][b]
5104 value = toCall(renderMemberLiteral, [generateChildNode(lead), UNDEFINED, toArray$1(stringifyNodes), holder ? TRUE$1 : UNDEFINED]);
5105 } else {
5106 // "xx".length
5107 // format().a.b
5108 value = toCall(renderMemberLiteral, [generateChildNode(lead), toString$1(keypath), UNDEFINED, holder ? TRUE$1 : UNDEFINED]);
5109 }
5110
5111 break;
5112
5113 default:
5114 isSpecialNode = TRUE;
5115 var args = node.args;
5116 value = toCall(renderCall, [generateChildNode(node.name), args.length ? toArray$1(args.map(generateChildNode)) : UNDEFINED, holder ? TRUE$1 : UNDEFINED]);
5117 break;
5118 } // 不需要 value holder
5119
5120
5121 if (!holder) {
5122 return value;
5123 } // 内部的临时值,且 holder 为 true
5124
5125
5126 if (inner) {
5127 return isSpecialNode ? value + RAW_DOT + RAW_VALUE : value;
5128 } // 最外层的值,且 holder 为 true
5129
5130
5131 return isSpecialNode ? value : toObject$1([RAW_VALUE + COLON + value]);
5132 }
5133 /**
5134 * 这里的难点在于处理 Element 的 children,举个例子:
5135 *
5136 * ['1', _x(expr), _l(expr, index, generate), _x(expr) ? ['1', _x(expr), _l(expr, index, generate)] : y]
5137 *
5138 * children 用数组表示,其中表达式求出的值可能是任意类型,比如数组或对象,我们无法控制表达式的值最终会是什么类型
5139 *
5140 * 像 each 或 import 这样的语法,内部其实会产生一个 vnode 数组,这里就出现了两个难点:
5141 *
5142 * 1. 如何区分 each 或其他语法产生的数组和表达式求值的数组
5143 * 2. 如何避免频繁的创建数组
5144 *
5145 * 我能想到的解决方案是,根据当前节点类型,如果是元素,则确保 children 的每一项的值序列化后都是函数调用的形式
5146 *
5147 * 这样能确保是从左到右依次执行,也就便于在内部创建一个公共数组,执行一个函数就收集一个值,而不管那个值到底是什么类型
5148 *
5149 */
5150 // 是否要执行 join 操作
5151
5152
5153 var joinStack = [],
5154 // 是否正在收集子节点
5155 collectStack = [],
5156 nodeGenerator = {},
5157 RENDER_EXPRESSION_IDENTIFIER = 'a',
5158 RENDER_EXPRESSION_MEMBER_KEYPATH = 'b',
5159 RENDER_EXPRESSION_MEMBER_LITERAL = 'c',
5160 RENDER_EXPRESSION_CALL = 'd',
5161 RENDER_TEXT_VNODE = 'e',
5162 RENDER_ATTRIBUTE_VNODE = 'f',
5163 RENDER_PROPERTY_VNODE = 'g',
5164 RENDER_LAZY_VNODE = 'h',
5165 RENDER_TRANSITION_VNODE = 'i',
5166 RENDER_BINDING_VNODE = 'j',
5167 RENDER_MODEL_VNODE = 'k',
5168 RENDER_EVENT_METHOD_VNODE = 'l',
5169 RENDER_EVENT_NAME_VNODE = 'm',
5170 RENDER_DIRECTIVE_VNODE = 'n',
5171 RENDER_SPREAD_VNODE = 'o',
5172 RENDER_ELEMENT_VNODE = 'p',
5173 RENDER_SLOT = 'q',
5174 RENDER_PARTIAL = 'r',
5175 RENDER_IMPORT = 's',
5176 RENDER_EACH = 't',
5177 RENDER_RANGE = 'u',
5178 RENDER_EQUAL_RANGE = 'v',
5179 TO_STRING = 'w',
5180 ARG_STACK = 'x'; // 序列化代码的参数列表
5181
5182 var codeArgs, // 表达式求值是否要求返回字符串类型
5183 isStringRequired;
5184
5185 function renderExpression(expr, holder, depIgnore, stack) {
5186 return generate(expr, RENDER_EXPRESSION_IDENTIFIER, RENDER_EXPRESSION_MEMBER_KEYPATH, RENDER_EXPRESSION_MEMBER_LITERAL, RENDER_EXPRESSION_CALL, holder, depIgnore, stack);
5187 }
5188
5189 function stringifyObject(obj) {
5190 var fields = [];
5191 each$2(obj, function (value, key) {
5192 if (isDef(value)) {
5193 push(fields, toString$1(key) + COLON + value);
5194 }
5195 });
5196 return toObject$1(fields);
5197 }
5198
5199 function stringifyFunction(result, arg) {
5200 return RAW_FUNCTION + "(" + (arg || EMPTY_STRING) + "){" + (result || EMPTY_STRING) + "}";
5201 }
5202
5203 function stringifyGroup(code) {
5204 return "(" + code + ")";
5205 }
5206
5207 function stringifyExpression(expr, toString) {
5208 var value = renderExpression(expr);
5209 return toString ? toCall(TO_STRING, [value]) : value;
5210 }
5211
5212 function stringifyExpressionVnode(expr, toString) {
5213 return toCall(RENDER_TEXT_VNODE, [stringifyExpression(expr, toString)]);
5214 }
5215
5216 function stringifyExpressionArg(expr) {
5217 return renderExpression(expr, FALSE, FALSE, ARG_STACK);
5218 }
5219
5220 function stringifyValue(value, expr, children) {
5221 if (isDef(value)) {
5222 return toString$1(value);
5223 } // 只有一个表达式时,保持原始类型
5224
5225
5226 if (expr) {
5227 return stringifyExpression(expr);
5228 } // 多个值拼接时,要求是字符串
5229
5230
5231 if (children) {
5232 isStringRequired = children.length > 1;
5233 return stringifyChildren(children);
5234 }
5235 }
5236
5237 function stringifyChildren(children, isComplex) {
5238 // 如果是复杂节点的 children,则每个 child 的序列化都是函数调用的形式
5239 // 因此最后可以拼接为 fn1(), fn2(), fn3() 这样依次调用,而不用再多此一举的使用数组,因为在 renderer 里也用不上这个数组
5240 // children 大于一个时,才有 join 的可能,单个值 jion 啥啊...
5241 var isJoin = children.length > 1 && !isComplex;
5242 push(joinStack, isJoin);
5243 var value = join(children.map(function (child) {
5244 return nodeGenerator[child.type](child);
5245 }), isJoin ? PLUS : COMMA);
5246 pop(joinStack);
5247 return value;
5248 }
5249
5250 function stringifyConditionChildren(children, isComplex) {
5251 if (children) {
5252 var result = stringifyChildren(children, isComplex);
5253 return children.length > 1 && isComplex ? stringifyGroup(result) : result;
5254 }
5255 }
5256
5257 function stringifyIf(node, stub) {
5258 var children = node.children,
5259 isComplex = node.isComplex,
5260 next = node.next,
5261 test = stringifyExpression(node.expr),
5262 yes = stringifyConditionChildren(children, isComplex),
5263 no,
5264 result;
5265
5266 if (next) {
5267 no = next.type === ELSE ? stringifyConditionChildren(next.children, next.isComplex) : stringifyIf(next, stub);
5268 } // 到达最后一个条件,发现第一个 if 语句带有 stub,需创建一个注释标签占位
5269 else if (stub) {
5270 no = renderElement(stringifyObject({
5271 isComment: TRUE$1,
5272 text: EMPTY
5273 }));
5274 }
5275
5276 if (isDef(yes) || isDef(no)) {
5277 var isJoin = last(joinStack);
5278
5279 if (isJoin) {
5280 if (isUndef(yes)) {
5281 yes = EMPTY;
5282 }
5283
5284 if (isUndef(no)) {
5285 no = EMPTY;
5286 }
5287 }
5288
5289 if (isUndef(no)) {
5290 result = test + AND + yes;
5291 } else if (isUndef(yes)) {
5292 result = NOT + test + AND + no;
5293 } else {
5294 result = test + QUESTION + yes + COLON + no;
5295 } // 如果是连接操作,因为 ?: 优先级最低,因此要加 ()
5296
5297
5298 return isJoin ? stringifyGroup(result) : result;
5299 }
5300
5301 return EMPTY;
5302 }
5303
5304 function renderElement(data, tag, attrs, childs, slots) {
5305 return toCall(RENDER_ELEMENT_VNODE, [data, tag, attrs, childs, slots]);
5306 }
5307
5308 function getComponentSlots(children) {
5309 var result = {},
5310 slots = {},
5311 addSlot = function (name, nodes) {
5312 if (!falsy(nodes)) {
5313 name = SLOT_DATA_PREFIX + name;
5314 push(slots[name] || (slots[name] = []), nodes);
5315 }
5316 };
5317
5318 each(children, function (child) {
5319 // 找到具名 slot
5320 if (child.type === ELEMENT) {
5321 var element = child;
5322
5323 if (element.slot) {
5324 addSlot(element.slot, element.tag === RAW_TEMPLATE ? element.children : [element]);
5325 return;
5326 }
5327 } // 匿名 slot,名称统一为 children
5328
5329
5330 addSlot(SLOT_NAME_DEFAULT, [child]);
5331 });
5332 each$2(slots, function (children, name) {
5333 // 强制为复杂节点,因为 slot 的子节点不能用字符串拼接的方式来渲染
5334 result[name] = stringifyFunction(stringifyChildren(children, TRUE));
5335 });
5336
5337 if (!falsy$2(result)) {
5338 return stringifyObject(result);
5339 }
5340 }
5341
5342 nodeGenerator[ELEMENT] = function (node) {
5343 var tag = node.tag,
5344 isComponent = node.isComponent,
5345 isSvg = node.isSvg,
5346 isStyle = node.isStyle,
5347 isOption = node.isOption,
5348 isStatic = node.isStatic,
5349 isComplex = node.isComplex,
5350 name = node.name,
5351 ref = node.ref,
5352 key = node.key,
5353 html = node.html,
5354 attrs = node.attrs,
5355 children = node.children,
5356 data = {},
5357 outputTag,
5358 outputAttrs = [],
5359 outputChilds,
5360 outputSlots;
5361
5362 if (tag === RAW_SLOT) {
5363 var args = [toString$1(SLOT_DATA_PREFIX + name)];
5364
5365 if (children) {
5366 push(args, stringifyFunction(stringifyChildren(children, TRUE)));
5367 }
5368
5369 return toCall(RENDER_SLOT, args);
5370 }
5371
5372 push(collectStack, FALSE);
5373
5374 if (attrs) {
5375 each(attrs, function (attr) {
5376 push(outputAttrs, nodeGenerator[attr.type](attr));
5377 });
5378 } // 如果以 $ 开头,表示动态组件
5379
5380
5381 if (codeAt(tag) === 36) {
5382 outputTag = toString$1(slice(tag, 1));
5383 } else {
5384 data.tag = toString$1(tag);
5385 }
5386
5387 if (isSvg) {
5388 data.isSvg = TRUE$1;
5389 }
5390
5391 if (isStyle) {
5392 data.isStyle = TRUE$1;
5393 }
5394
5395 if (isOption) {
5396 data.isOption = TRUE$1;
5397 }
5398
5399 if (isStatic) {
5400 data.isStatic = TRUE$1;
5401 }
5402
5403 if (ref) {
5404 data.ref = stringifyValue(ref.value, ref.expr, ref.children);
5405 }
5406
5407 if (key) {
5408 data.key = stringifyValue(key.value, key.expr, key.children);
5409 }
5410
5411 if (html) {
5412 data.html = string(html) ? toString$1(html) : stringifyExpression(html, TRUE);
5413 }
5414
5415 if (isComponent) {
5416 data.isComponent = TRUE$1;
5417
5418 if (children) {
5419 collectStack[collectStack.length - 1] = TRUE;
5420 outputSlots = getComponentSlots(children);
5421 }
5422 } else if (children) {
5423 isStringRequired = TRUE;
5424 collectStack[collectStack.length - 1] = isComplex;
5425 outputChilds = stringifyChildren(children, isComplex);
5426
5427 if (isComplex) {
5428 outputChilds = stringifyFunction(outputChilds);
5429 } else {
5430 data.text = outputChilds;
5431 outputChilds = UNDEFINED;
5432 }
5433 }
5434
5435 pop(collectStack);
5436 return renderElement(stringifyObject(data), outputTag, falsy(outputAttrs) ? UNDEFINED : stringifyFunction(join(outputAttrs, COMMA)), outputChilds, outputSlots);
5437 };
5438
5439 nodeGenerator[ATTRIBUTE] = function (node) {
5440 var value = node.binding ? toCall(RENDER_BINDING_VNODE, [toString$1(node.name), renderExpression(node.expr, TRUE, TRUE)]) : stringifyValue(node.value, node.expr, node.children);
5441 return toCall(RENDER_ATTRIBUTE_VNODE, [toString$1(node.name), value]);
5442 };
5443
5444 nodeGenerator[PROPERTY] = function (node) {
5445 var value = node.binding ? toCall(RENDER_BINDING_VNODE, [toString$1(node.name), renderExpression(node.expr, TRUE, TRUE), toString$1(node.hint)]) : stringifyValue(node.value, node.expr, node.children);
5446 return toCall(RENDER_PROPERTY_VNODE, [toString$1(node.name), toString$1(node.hint), value]);
5447 };
5448
5449 nodeGenerator[DIRECTIVE] = function (node) {
5450 var ns = node.ns,
5451 name = node.name,
5452 key = node.key,
5453 value = node.value,
5454 expr = node.expr,
5455 modifier = node.modifier;
5456
5457 if (ns === DIRECTIVE_LAZY) {
5458 return toCall(RENDER_LAZY_VNODE, [toString$1(name), toString$1(value)]);
5459 } // <div transition="name">
5460
5461
5462 if (ns === RAW_TRANSITION) {
5463 return toCall(RENDER_TRANSITION_VNODE, [toString$1(value)]);
5464 } // <input model="id">
5465
5466
5467 if (ns === DIRECTIVE_MODEL) {
5468 return toCall(RENDER_MODEL_VNODE, [renderExpression(expr, TRUE, TRUE)]);
5469 }
5470
5471 var renderName = RENDER_DIRECTIVE_VNODE,
5472 args = [toString$1(name), toString$1(key), toString$1(modifier), toString$1(value)]; // 尽可能把表达式编译成函数,这样对外界最友好
5473 //
5474 // 众所周知,事件指令会编译成函数,对于自定义指令来说,也要尽可能编译成函数
5475 //
5476 // 比如 o-tap="method()" 或 o-log="{'id': '11'}"
5477 // 前者会编译成 handler(调用方法),后者会编译成 getter(取值)
5478
5479 if (expr) {
5480 // 如果表达式明确是在调用方法,则序列化成 method + args 的形式
5481 if (expr.type === CALL) {
5482 if (ns === DIRECTIVE_EVENT) {
5483 renderName = RENDER_EVENT_METHOD_VNODE;
5484 } // compiler 保证了函数调用的 name 是标识符
5485
5486
5487 push(args, toString$1(expr.name.name)); // 为了实现运行时动态收集参数,这里序列化成函数
5488
5489 if (!falsy(expr.args)) {
5490 // args 函数在触发事件时调用,调用时会传入它的作用域,因此这里要加一个参数
5491 push(args, stringifyFunction(RETURN + toArray$1(expr.args.map(stringifyExpressionArg)), ARG_STACK));
5492 }
5493 } // 不是调用方法,就是事件转换
5494 else if (ns === DIRECTIVE_EVENT) {
5495 renderName = RENDER_EVENT_NAME_VNODE;
5496 push(args, toString$1(expr.raw));
5497 } else if (ns === DIRECTIVE_CUSTOM) {
5498 // 取值函数
5499 // getter 函数在触发事件时调用,调用时会传入它的作用域,因此这里要加一个参数
5500 if (expr.type !== LITERAL) {
5501 push(args, UNDEFINED); // method
5502
5503 push(args, UNDEFINED); // args
5504
5505 push(args, stringifyFunction(RETURN + stringifyExpressionArg(expr), ARG_STACK));
5506 }
5507 }
5508 }
5509
5510 return toCall(renderName, args);
5511 };
5512
5513 nodeGenerator[SPREAD] = function (node) {
5514 return toCall(RENDER_SPREAD_VNODE, [renderExpression(node.expr, TRUE, node.binding)]);
5515 };
5516
5517 nodeGenerator[TEXT] = function (node) {
5518 var result = toString$1(node.text);
5519
5520 if (last(collectStack) && !last(joinStack)) {
5521 return toCall(RENDER_TEXT_VNODE, [result]);
5522 }
5523
5524 return result;
5525 };
5526
5527 nodeGenerator[EXPRESSION] = function (node) {
5528 // 强制保留 isStringRequired 参数,减少运行时判断参数是否存在
5529 // 因为还有 stack 参数呢,各种判断真的很累
5530 if (last(collectStack) && !last(joinStack)) {
5531 return stringifyExpressionVnode(node.expr, isStringRequired);
5532 }
5533
5534 return stringifyExpression(node.expr, isStringRequired);
5535 };
5536
5537 nodeGenerator[IF] = function (node) {
5538 return stringifyIf(node, node.stub);
5539 };
5540
5541 nodeGenerator[EACH] = function (node) {
5542 // compiler 保证了 children 一定有值
5543 var children = stringifyFunction(stringifyChildren(node.children, node.isComplex)); // 遍历区间
5544
5545 if (node.to) {
5546 if (node.equal) {
5547 return toCall(RENDER_EQUAL_RANGE, [children, renderExpression(node.from), renderExpression(node.to), node.index ? toString$1(node.index) : UNDEFINED]);
5548 }
5549
5550 return toCall(RENDER_RANGE, [children, renderExpression(node.from), renderExpression(node.to), node.index ? toString$1(node.index) : UNDEFINED]);
5551 } // 遍历数组和对象
5552
5553
5554 return toCall(RENDER_EACH, [children, renderExpression(node.from, TRUE), node.index ? toString$1(node.index) : UNDEFINED]);
5555 };
5556
5557 nodeGenerator[PARTIAL] = function (node) {
5558 return toCall(RENDER_PARTIAL, [toString$1(node.name), // compiler 保证了 children 一定有值
5559 stringifyFunction(stringifyChildren(node.children, node.isComplex))]);
5560 };
5561
5562 nodeGenerator[IMPORT] = function (node) {
5563 return toCall(RENDER_IMPORT, [toString$1(node.name)]);
5564 };
5565
5566 function generate$1(node) {
5567 if (!codeArgs) {
5568 codeArgs = join([RENDER_EXPRESSION_IDENTIFIER, RENDER_EXPRESSION_MEMBER_KEYPATH, RENDER_EXPRESSION_MEMBER_LITERAL, RENDER_EXPRESSION_CALL, RENDER_TEXT_VNODE, RENDER_ATTRIBUTE_VNODE, RENDER_PROPERTY_VNODE, RENDER_LAZY_VNODE, RENDER_TRANSITION_VNODE, RENDER_BINDING_VNODE, RENDER_MODEL_VNODE, RENDER_EVENT_METHOD_VNODE, RENDER_EVENT_NAME_VNODE, RENDER_DIRECTIVE_VNODE, RENDER_SPREAD_VNODE, RENDER_ELEMENT_VNODE, RENDER_SLOT, RENDER_PARTIAL, RENDER_IMPORT, RENDER_EACH, RENDER_RANGE, RENDER_EQUAL_RANGE, TO_STRING], COMMA);
5569 }
5570
5571 return toFunction(codeArgs, nodeGenerator[node.type](node));
5572 }
5573
5574 function setPair(target, name, key, value) {
5575 var data = target[name] || (target[name] = {});
5576 data[key] = value;
5577 }
5578
5579 var KEY_DIRECTIVES = 'directives';
5580
5581 function render(context, observer, template, filters, partials, directives, transitions) {
5582 var $scope = {
5583 $keypath: EMPTY_STRING
5584 },
5585 $stack = [$scope],
5586 $vnode,
5587 vnodeStack = [],
5588 localPartials = {},
5589 renderedSlots = {},
5590 findValue = function (stack, index, key, lookup, depIgnore, defaultKeypath) {
5591 var scope = stack[index],
5592 keypath = join$1(scope.$keypath, key),
5593 value = stack,
5594 holder$1 = holder; // 如果最后还是取不到值,用回最初的 keypath
5595
5596 if (isUndef(defaultKeypath)) {
5597 defaultKeypath = keypath;
5598 } // 如果取的是 scope 上直接有的数据,如 $keypath
5599
5600
5601 if (isDef(scope[key])) {
5602 value = scope[key];
5603 } // 如果取的是数组项,则要更进一步
5604 else if (isDef(scope.$item)) {
5605 scope = scope.$item; // 到这里 scope 可能为空
5606 // 比如 new Array(10) 然后遍历这个数组,每一项肯定是空
5607 // 取 this
5608
5609 if (key === EMPTY_STRING) {
5610 value = scope;
5611 } // 取 this.xx
5612 else if (scope != NULL && isDef(scope[key])) {
5613 value = scope[key];
5614 }
5615 }
5616
5617 if (value === stack) {
5618 // 正常取数据
5619 value = observer.get(keypath, stack, depIgnore);
5620
5621 if (value === stack) {
5622 if (lookup && index > 0) {
5623 {
5624 debug("The data \"" + keypath + "\" can't be found in the current context, start looking up.");
5625 }
5626 return findValue(stack, index - 1, key, lookup, depIgnore, defaultKeypath);
5627 } // 到头了,最后尝试过滤器
5628
5629
5630 var result = get(filters, key);
5631
5632 if (result) {
5633 holder$1 = result;
5634 holder$1.keypath = key;
5635 } else {
5636 holder$1.value = UNDEFINED;
5637 holder$1.keypath = defaultKeypath;
5638 }
5639
5640 return holder$1;
5641 }
5642 }
5643
5644 holder$1.value = value;
5645 holder$1.keypath = keypath;
5646 return holder$1;
5647 },
5648 createEventListener = function (type) {
5649 return function (event, data) {
5650 // 事件名称相同的情况,只可能是监听 DOM 事件,比如写一个 Button 组件
5651 // <button on-click="click"> 纯粹的封装了一个原生 click 事件
5652 if (type !== event.type) {
5653 event = new CustomEvent(type, event);
5654 }
5655
5656 context.fire(event, data);
5657 };
5658 },
5659 createMethodListener = function (name, args, stack) {
5660 return function (event, data) {
5661 var method = context[name];
5662
5663 if (event instanceof CustomEvent) {
5664 var result = UNDEFINED;
5665
5666 if (args) {
5667 var scope = last(stack);
5668
5669 if (scope) {
5670 scope.$event = event;
5671 scope.$data = data;
5672 result = execute(method, context, args(stack));
5673 scope.$event = scope.$data = UNDEFINED;
5674 }
5675 } else {
5676 result = execute(method, context, data ? [event, data] : event);
5677 }
5678
5679 return result;
5680 } else {
5681 execute(method, context, args ? args(stack) : UNDEFINED);
5682 }
5683 };
5684 },
5685 createGetter = function (getter, stack) {
5686 return function () {
5687 return getter(stack);
5688 };
5689 },
5690 renderTextVnode = function (text) {
5691 var vnodeList = last(vnodeStack);
5692
5693 if (vnodeList) {
5694 var lastVnode = last(vnodeList);
5695
5696 if (lastVnode && lastVnode.isText) {
5697 lastVnode.text += text;
5698 } else {
5699 var textVnode = {
5700 isText: TRUE,
5701 text: text,
5702 context: context,
5703 keypath: $scope.$keypath
5704 };
5705 push(vnodeList, textVnode);
5706 }
5707 }
5708 },
5709 renderAttributeVnode = function (name, value) {
5710 if ($vnode.isComponent) {
5711 setPair($vnode, 'props', name, value);
5712 } else {
5713 setPair($vnode, 'nativeAttrs', name, {
5714 name: name,
5715 value: value
5716 });
5717 }
5718 },
5719 renderPropertyVnode = function (name, hint, value) {
5720 setPair($vnode, 'nativeProps', name, {
5721 name: name,
5722 value: value,
5723 hint: hint
5724 });
5725 },
5726 renderLazyVnode = function (name, value) {
5727 setPair($vnode, 'lazy', name, value);
5728 },
5729 renderTransitionVnode = function (name) {
5730 $vnode.transition = transitions[name];
5731 {
5732 if (!$vnode.transition) {
5733 fatal("The transition \"" + name + "\" can't be found.");
5734 }
5735 }
5736 },
5737 renderBindingVnode = function (name, holder, hint) {
5738 var key = join$1(DIRECTIVE_BINDING, name);
5739 setPair($vnode, KEY_DIRECTIVES, key, {
5740 ns: DIRECTIVE_BINDING,
5741 name: name,
5742 key: key,
5743 modifier: holder.keypath,
5744 hooks: directives[DIRECTIVE_BINDING],
5745 hint: hint
5746 });
5747 return holder.value;
5748 },
5749 renderModelVnode = function (holder) {
5750 setPair($vnode, KEY_DIRECTIVES, DIRECTIVE_MODEL, {
5751 ns: DIRECTIVE_MODEL,
5752 name: EMPTY_STRING,
5753 key: DIRECTIVE_MODEL,
5754 value: holder.value,
5755 modifier: holder.keypath,
5756 hooks: directives[DIRECTIVE_MODEL]
5757 });
5758 },
5759 renderEventMethodVnode = function (name, key, modifier, value, method, args) {
5760 setPair($vnode, KEY_DIRECTIVES, key, {
5761 ns: DIRECTIVE_EVENT,
5762 name: name,
5763 key: key,
5764 value: value,
5765 modifier: modifier,
5766 hooks: directives[DIRECTIVE_EVENT],
5767 handler: createMethodListener(method, args, $stack)
5768 });
5769 },
5770 renderEventNameVnode = function (name, key, modifier, value, event) {
5771 setPair($vnode, KEY_DIRECTIVES, key, {
5772 ns: DIRECTIVE_EVENT,
5773 name: name,
5774 key: key,
5775 value: value,
5776 modifier: modifier,
5777 hooks: directives[DIRECTIVE_EVENT],
5778 handler: createEventListener(event)
5779 });
5780 },
5781 renderDirectiveVnode = function (name, key, modifier, value, method, args, getter) {
5782 var hooks = directives[name];
5783 {
5784 if (!hooks) {
5785 fatal("The directive " + name + " can't be found.");
5786 }
5787 }
5788 setPair($vnode, KEY_DIRECTIVES, key, {
5789 ns: DIRECTIVE_CUSTOM,
5790 name: name,
5791 key: key,
5792 value: value,
5793 hooks: hooks,
5794 modifier: modifier,
5795 getter: getter ? createGetter(getter, $stack) : UNDEFINED,
5796 handler: method ? createMethodListener(method, args, $stack) : UNDEFINED
5797 });
5798 },
5799 renderSpreadVnode = function (holder) {
5800 var value = holder.value,
5801 keypath = holder.keypath; // 如果为 null 或 undefined,则不需要 warn
5802
5803 if (value != NULL) {
5804 // 数组也算一种对象,要排除掉
5805 if (object(value) && !array(value)) {
5806 each$2(value, function (value, key) {
5807 setPair($vnode, 'props', key, value);
5808 });
5809
5810 if (keypath) {
5811 var key = join$1(DIRECTIVE_BINDING, keypath);
5812 setPair($vnode, KEY_DIRECTIVES, key, {
5813 ns: DIRECTIVE_BINDING,
5814 name: EMPTY_STRING,
5815 key: key,
5816 modifier: join$1(keypath, RAW_WILDCARD),
5817 hooks: directives[DIRECTIVE_BINDING]
5818 });
5819 }
5820 }
5821 }
5822 },
5823 renderElementVnode = function (vnode, tag, attrs, childs, slots) {
5824 if (tag) {
5825 var componentName = observer.get(tag);
5826 {
5827 if (!componentName) {
5828 warn("The dynamic component \"" + tag + "\" can't be found.");
5829 }
5830 }
5831 vnode.tag = componentName;
5832 }
5833
5834 if (attrs) {
5835 $vnode = vnode;
5836 attrs();
5837 $vnode = UNDEFINED;
5838 } // childs 和 slots 不可能同时存在
5839
5840
5841 if (childs) {
5842 vnodeStack.push(vnode.children = []);
5843 childs();
5844 pop(vnodeStack);
5845 } else if (slots) {
5846 var renderSlots_1 = {};
5847 each$2(slots, function (slot, name) {
5848 vnodeStack.push([]);
5849 slot();
5850 var vnodes = pop(vnodeStack);
5851 renderSlots_1[name] = vnodes.length ? vnodes : UNDEFINED;
5852 });
5853 vnode.slots = renderSlots_1;
5854 }
5855
5856 vnode.context = context;
5857 vnode.keypath = $scope.$keypath;
5858 var vnodeList = last(vnodeStack);
5859
5860 if (vnodeList) {
5861 push(vnodeList, vnode);
5862 }
5863
5864 return vnode;
5865 },
5866 renderExpressionIdentifier = function (name, lookup, offset, holder, depIgnore, stack) {
5867 var myStack = stack || $stack,
5868 result = findValue(myStack, myStack.length - ((offset || 0) + 1), name, lookup, depIgnore);
5869 return holder ? result : result.value;
5870 },
5871 renderExpressionMemberKeypath = function (identifier, runtimeKeypath) {
5872 unshift(runtimeKeypath, identifier);
5873 return join(runtimeKeypath, RAW_DOT);
5874 },
5875 renderExpressionMemberLiteral = function (value, staticKeypath, runtimeKeypath, holder$1) {
5876 if (isDef(runtimeKeypath)) {
5877 staticKeypath = join(runtimeKeypath, RAW_DOT);
5878 }
5879
5880 var match = get(value, staticKeypath);
5881 holder.keypath = UNDEFINED;
5882 holder.value = match ? match.value : UNDEFINED;
5883 return holder$1 ? holder : holder.value;
5884 },
5885 renderExpressionCall = function (fn, args, holder$1) {
5886 holder.keypath = UNDEFINED; // 当 holder 为 true, args 为空时,args 会传入 false
5887
5888 holder.value = execute(fn, context, args || UNDEFINED);
5889 return holder$1 ? holder : holder.value;
5890 },
5891 // <slot name="xx"/>
5892 renderSlot = function (name, defaultRender) {
5893 var vnodeList = last(vnodeStack),
5894 vnodes = context.get(name);
5895
5896 if (vnodeList) {
5897 if (vnodes) {
5898 each(vnodes, function (vnode) {
5899 push(vnodeList, vnode);
5900 vnode.slot = name;
5901 vnode.parent = context;
5902 });
5903 } else if (defaultRender) {
5904 defaultRender();
5905 }
5906 } // 不能重复输出相同名称的 slot
5907
5908
5909 {
5910 if (renderedSlots[name]) {
5911 fatal("The slot \"" + slice(name, SLOT_DATA_PREFIX.length) + "\" can't render more than one time.");
5912 }
5913
5914 renderedSlots[name] = TRUE;
5915 }
5916 },
5917 // {{#partial name}}
5918 // xx
5919 // {{/partial}}
5920 renderPartial = function (name, render) {
5921 localPartials[name] = render;
5922 },
5923 // {{> name}}
5924 renderImport = function (name) {
5925 if (localPartials[name]) {
5926 localPartials[name]();
5927 } else {
5928 var partial = partials[name];
5929
5930 if (partial) {
5931 partial(renderExpressionIdentifier, renderExpressionMemberKeypath, renderExpressionMemberLiteral, renderExpressionCall, renderTextVnode, renderAttributeVnode, renderPropertyVnode, renderLazyVnode, renderTransitionVnode, renderBindingVnode, renderModelVnode, renderEventMethodVnode, renderEventNameVnode, renderDirectiveVnode, renderSpreadVnode, renderElementVnode, renderSlot, renderPartial, renderImport, renderEach, renderRange, renderEqualRange, toString);
5932 } else {
5933 fatal("The partial \"" + name + "\" can't be found.");
5934 }
5935 }
5936 },
5937 eachHandler = function (generate, item, key, keypath, index, length) {
5938 var lastScope = $scope,
5939 lastStack = $stack; // each 会改变 keypath
5940
5941 $scope = {
5942 $keypath: keypath
5943 };
5944 $stack = lastStack.concat($scope); // 避免模板里频繁读取 list.length
5945
5946 if (isDef(length)) {
5947 $scope.$length = length;
5948 } // 业务层是否写了 expr:index
5949
5950
5951 if (index) {
5952 $scope[index] = key;
5953 } // 无法通过 context.get($keypath + key) 读取到数据的场景
5954 // 必须把 item 写到 scope
5955
5956
5957 if (!keypath) {
5958 $scope.$item = item;
5959 }
5960
5961 generate();
5962 $scope = lastScope;
5963 $stack = lastStack;
5964 },
5965 renderEach = function (generate, holder, index) {
5966 var keypath = holder.keypath,
5967 value = holder.value;
5968
5969 if (array(value)) {
5970 for (var i = 0, length = value.length; i < length; i++) {
5971 eachHandler(generate, value[i], i, keypath ? join$1(keypath, EMPTY_STRING + i) : EMPTY_STRING, index, length);
5972 }
5973 } else if (object(value)) {
5974 for (var key in value) {
5975 eachHandler(generate, value[key], key, keypath ? join$1(keypath, key) : EMPTY_STRING, index);
5976 }
5977 }
5978 },
5979 renderRange = function (generate, from, to, index) {
5980 var count = 0;
5981
5982 if (from < to) {
5983 for (var i = from; i < to; i++) {
5984 eachHandler(generate, i, count++, EMPTY_STRING, index);
5985 }
5986 } else {
5987 for (var i = from; i > to; i--) {
5988 eachHandler(generate, i, count++, EMPTY_STRING, index);
5989 }
5990 }
5991 },
5992 renderEqualRange = function (generate, from, to, index) {
5993 var count = 0;
5994
5995 if (from < to) {
5996 for (var i = from; i <= to; i++) {
5997 eachHandler(generate, i, count++, EMPTY_STRING, index);
5998 }
5999 } else {
6000 for (var i = from; i >= to; i--) {
6001 eachHandler(generate, i, count++, EMPTY_STRING, index);
6002 }
6003 }
6004 };
6005
6006 return template(renderExpressionIdentifier, renderExpressionMemberKeypath, renderExpressionMemberLiteral, renderExpressionCall, renderTextVnode, renderAttributeVnode, renderPropertyVnode, renderLazyVnode, renderTransitionVnode, renderBindingVnode, renderModelVnode, renderEventMethodVnode, renderEventNameVnode, renderDirectiveVnode, renderSpreadVnode, renderElementVnode, renderSlot, renderPartial, renderImport, renderEach, renderRange, renderEqualRange, toString);
6007 } // 这里先写 IE9 支持的接口
6008
6009
6010 var innerText = 'textContent',
6011 innerHTML = 'innerHTML',
6012 createEvent = function (event, node) {
6013 return event;
6014 },
6015 findElement = function (selector) {
6016 var node = DOCUMENT.querySelector(selector);
6017
6018 if (node) {
6019 return node;
6020 }
6021 },
6022 _addEventListener = function addEventListener(node, type, listener) {
6023 node.addEventListener(type, listener, FALSE);
6024 },
6025 _removeEventListener = function removeEventListener(node, type, listener) {
6026 node.removeEventListener(type, listener, FALSE);
6027 },
6028 // IE9 不支持 classList
6029 addElementClass = function (node, className) {
6030 node.classList.add(className);
6031 },
6032 removeElementClass = function (node, className) {
6033 node.classList.remove(className);
6034 };
6035
6036 {
6037 if (DOCUMENT) {
6038 // 此时 document.body 不一定有值,比如 script 放在 head 里
6039 if (!DOCUMENT.documentElement.classList) {
6040 addElementClass = function (node, className) {
6041 var classes = node.className.split(CHAR_WHITESPACE);
6042
6043 if (!has(classes, className)) {
6044 push(classes, className);
6045 node.className = join(classes, CHAR_WHITESPACE);
6046 }
6047 };
6048
6049 removeElementClass = function (node, className) {
6050 var classes = node.className.split(CHAR_WHITESPACE);
6051
6052 if (remove(classes, className)) {
6053 node.className = join(classes, CHAR_WHITESPACE);
6054 }
6055 };
6056 } // 为 IE9 以下浏览器打补丁
6057
6058
6059 {
6060 if (!DOCUMENT.addEventListener) {
6061 var PROPERTY_CHANGE_1 = 'propertychange',
6062 isBoxElement_1 = function (node) {
6063 return node.tagName === 'INPUT' && (node.type === 'radio' || node.type === 'checkbox');
6064 };
6065
6066 var IEEvent_1 =
6067 /** @class */
6068 function () {
6069 function IEEvent(event, element) {
6070 extend(this, event);
6071 this.currentTarget = element;
6072 this.target = event.srcElement || element;
6073 this.originalEvent = event;
6074 }
6075
6076 IEEvent.prototype.preventDefault = function () {
6077 this.originalEvent.returnValue = FALSE;
6078 };
6079
6080 IEEvent.prototype.stopPropagation = function () {
6081 this.originalEvent.cancelBubble = TRUE;
6082 };
6083
6084 return IEEvent;
6085 }(); // textContent 不兼容 IE 678
6086
6087
6088 innerText = 'innerText';
6089
6090 createEvent = function (event, element) {
6091 return new IEEvent_1(event, element);
6092 };
6093
6094 findElement = function (selector) {
6095 // 去掉 #
6096 if (codeAt(selector, 0) === 35) {
6097 selector = slice(selector, 1);
6098 } else {
6099 fatal("The id selector, such as \"#id\", is the only supported selector for legacy version.");
6100 }
6101
6102 var node = DOCUMENT.getElementById(selector);
6103
6104 if (node) {
6105 return node;
6106 }
6107 };
6108
6109 _addEventListener = function addEventListener(node, type, listener) {
6110 if (type === EVENT_INPUT) {
6111 _addEventListener(node, PROPERTY_CHANGE_1, // 借用 EMITTER,反正只是内部临时用一下...
6112 listener[EMITTER] = function (event) {
6113 if (event.propertyName === RAW_VALUE) {
6114 event = new CustomEvent(event);
6115 event.type = EVENT_INPUT;
6116 execute(listener, this, event);
6117 }
6118 });
6119 } else if (type === EVENT_CHANGE && isBoxElement_1(node)) {
6120 _addEventListener(node, EVENT_CLICK, listener[EMITTER] = function (event) {
6121 event = new CustomEvent(event);
6122 event.type = EVENT_CHANGE;
6123 execute(listener, this, event);
6124 });
6125 } else {
6126 node.attachEvent("on" + type, listener);
6127 }
6128 };
6129
6130 _removeEventListener = function removeEventListener(node, type, listener) {
6131 if (type === EVENT_INPUT) {
6132 _removeEventListener(node, PROPERTY_CHANGE_1, listener[EMITTER]);
6133
6134 delete listener[EMITTER];
6135 } else if (type === EVENT_CHANGE && isBoxElement_1(node)) {
6136 _removeEventListener(node, EVENT_CLICK, listener[EMITTER]);
6137
6138 delete listener[EMITTER];
6139 } else {
6140 node.detachEvent("on" + type, listener);
6141 }
6142 };
6143 }
6144 }
6145 }
6146 }
6147 var CHAR_WHITESPACE = ' ',
6148
6149 /**
6150 * 绑定在 HTML 元素上的事件发射器
6151 */
6152 EMITTER = '$emitter',
6153
6154 /**
6155 * 低版本 IE 上 style 标签的专有属性
6156 */
6157 STYLE_SHEET = 'styleSheet',
6158
6159 /**
6160 * 跟输入事件配套使用的事件
6161 */
6162 COMPOSITION_START = 'compositionstart',
6163
6164 /**
6165 * 跟输入事件配套使用的事件
6166 */
6167 COMPOSITION_END = 'compositionend',
6168 domain = 'http://www.w3.org/',
6169 namespaces = {
6170 svg: domain + '2000/svg'
6171 },
6172 specialEvents = {};
6173 specialEvents[EVENT_MODEL] = {
6174 on: function on(node, listener) {
6175 var locked = FALSE;
6176
6177 _on(node, COMPOSITION_START, listener[COMPOSITION_START] = function () {
6178 locked = TRUE;
6179 });
6180
6181 _on(node, COMPOSITION_END, listener[COMPOSITION_END] = function (event) {
6182 locked = FALSE;
6183 listener(event);
6184 });
6185
6186 _addEventListener(node, EVENT_INPUT, listener[EVENT_INPUT] = function (event) {
6187 if (!locked) {
6188 listener(event);
6189 }
6190 });
6191 },
6192 off: function off(node, listener) {
6193 _off(node, COMPOSITION_START, listener[COMPOSITION_START]);
6194
6195 _off(node, COMPOSITION_END, listener[COMPOSITION_END]);
6196
6197 _removeEventListener(node, EVENT_INPUT, listener[EVENT_INPUT]);
6198
6199 listener[COMPOSITION_START] = listener[COMPOSITION_END] = listener[EVENT_INPUT] = UNDEFINED;
6200 }
6201 };
6202
6203 function createElement$2(tag, isSvg) {
6204 return isSvg ? DOCUMENT.createElementNS(namespaces.svg, tag) : DOCUMENT.createElement(tag);
6205 }
6206
6207 function createText$1(text) {
6208 return DOCUMENT.createTextNode(text);
6209 }
6210
6211 function createComment(text) {
6212 return DOCUMENT.createComment(text);
6213 }
6214
6215 function prop(node, name, value) {
6216 if (isDef(value)) {
6217 set(node, name, value, FALSE);
6218 } else {
6219 var holder = get(node, name);
6220
6221 if (holder) {
6222 return holder.value;
6223 }
6224 }
6225 }
6226
6227 function removeProp(node, name, hint) {
6228 set(node, name, hint === HINT_BOOLEAN ? FALSE : hint === HINT_NUMBER ? 0 : EMPTY_STRING, FALSE);
6229 }
6230
6231 function attr(node, name, value) {
6232 if (isDef(value)) {
6233 node.setAttribute(name, value);
6234 } else {
6235 // value 还可能是 null
6236 var value_1 = node.getAttribute(name);
6237
6238 if (value_1 != NULL) {
6239 return value_1;
6240 }
6241 }
6242 }
6243
6244 function removeAttr(node, name) {
6245 node.removeAttribute(name);
6246 }
6247
6248 function before(parentNode, node, beforeNode) {
6249 parentNode.insertBefore(node, beforeNode);
6250 }
6251
6252 function append(parentNode, node) {
6253 parentNode.appendChild(node);
6254 }
6255
6256 function replace(parentNode, node, oldNode) {
6257 parentNode.replaceChild(node, oldNode);
6258 }
6259
6260 function remove$2(parentNode, node) {
6261 parentNode.removeChild(node);
6262 }
6263
6264 function parent(node) {
6265 var parentNode = node.parentNode;
6266
6267 if (parentNode) {
6268 return parentNode;
6269 }
6270 }
6271
6272 function next(node) {
6273 var nextSibling = node.nextSibling;
6274
6275 if (nextSibling) {
6276 return nextSibling;
6277 }
6278 }
6279
6280 var find = findElement;
6281
6282 function tag(node) {
6283 if (node.nodeType === 1) {
6284 return lower(node.tagName);
6285 }
6286 }
6287
6288 function text(node, text, isStyle, isOption) {
6289 if (isDef(text)) {
6290 {
6291 if (isStyle && has$2(node, STYLE_SHEET)) {
6292 node[STYLE_SHEET].cssText = text;
6293 } else {
6294 if (isOption) {
6295 node.value = text;
6296 }
6297
6298 node[innerText] = text;
6299 }
6300 }
6301 } else {
6302 return node[innerText];
6303 }
6304 }
6305
6306 function html(node, html, isStyle, isOption) {
6307 if (isDef(html)) {
6308 {
6309 if (isStyle && has$2(node, STYLE_SHEET)) {
6310 node[STYLE_SHEET].cssText = html;
6311 } else {
6312 if (isOption) {
6313 node.value = html;
6314 }
6315
6316 node[innerHTML] = html;
6317 }
6318 }
6319 } else {
6320 return node[innerHTML];
6321 }
6322 }
6323
6324 var addClass = addElementClass;
6325 var removeClass = removeElementClass;
6326
6327 function _on(node, type, listener, context) {
6328 var emitter = node[EMITTER] || (node[EMITTER] = new Emitter()),
6329 nativeListeners = emitter.nativeListeners || (emitter.nativeListeners = {}); // 一个元素,相同的事件,只注册一个 native listener
6330
6331 if (!nativeListeners[type]) {
6332 // 特殊事件
6333 var special = specialEvents[type],
6334 // 唯一的原生监听器
6335 nativeListener = function (event) {
6336 var customEvent = event instanceof CustomEvent ? event : new CustomEvent(event.type, createEvent(event, node));
6337
6338 if (customEvent.type !== type) {
6339 customEvent.type = type;
6340 }
6341
6342 emitter.fire(type, [customEvent]);
6343 };
6344
6345 nativeListeners[type] = nativeListener;
6346
6347 if (special) {
6348 special.on(node, nativeListener);
6349 } else {
6350 _addEventListener(node, type, nativeListener);
6351 }
6352 }
6353
6354 emitter.on(type, {
6355 fn: listener,
6356 ctx: context
6357 });
6358 }
6359
6360 function _off(node, type, listener) {
6361 var emitter = node[EMITTER],
6362 listeners = emitter.listeners,
6363 nativeListeners = emitter.nativeListeners; // emitter 会根据 type 和 listener 参数进行适当的删除
6364
6365 emitter.off(type, listener); // 如果注册的 type 事件都解绑了,则去掉原生监听器
6366
6367 if (nativeListeners && !emitter.has(type)) {
6368 var special = specialEvents[type],
6369 nativeListener = nativeListeners[type];
6370
6371 if (special) {
6372 special.off(node, nativeListener);
6373 } else {
6374 _removeEventListener(node, type, nativeListener);
6375 }
6376
6377 delete nativeListeners[type];
6378 }
6379
6380 if (falsy$2(listeners)) {
6381 node[EMITTER] = UNDEFINED;
6382 }
6383 }
6384
6385 function addSpecialEvent(type, hooks) {
6386 {
6387 if (specialEvents[type]) {
6388 fatal("The special event \"" + type + "\" already exists.");
6389 }
6390
6391 info("The special event \"" + type + "\" is added successfully.");
6392 }
6393 specialEvents[type] = hooks;
6394 }
6395
6396 var domApi =
6397 /*#__PURE__*/
6398 {
6399 createElement: createElement$2,
6400 createText: createText$1,
6401 createComment: createComment,
6402 prop: prop,
6403 removeProp: removeProp,
6404 attr: attr,
6405 removeAttr: removeAttr,
6406 before: before,
6407 append: append,
6408 replace: replace,
6409 remove: remove$2,
6410 parent: parent,
6411 next: next,
6412 find: find,
6413 tag: tag,
6414 text: text,
6415 html: html,
6416 addClass: addClass,
6417 removeClass: removeClass,
6418 on: _on,
6419 off: _off,
6420 addSpecialEvent: addSpecialEvent
6421 };
6422 /**
6423 * 计算属性
6424 *
6425 * 可配置 cache、deps、get、set 等
6426 */
6427
6428 var Computed =
6429 /** @class */
6430 function () {
6431 function Computed(keypath, sync, cache, deps, observer, getter, setter) {
6432 var instance = this;
6433 instance.keypath = keypath;
6434 instance.cache = cache;
6435 instance.deps = deps;
6436 instance.context = observer.context;
6437 instance.observer = observer;
6438 instance.getter = getter;
6439 instance.setter = setter;
6440 instance.unique = {};
6441
6442 instance.watcher = function ($0, $1, $2) {
6443 // 计算属性的依赖变了会走进这里
6444 var oldValue = instance.value,
6445 newValue = instance.get(TRUE);
6446
6447 if (newValue !== oldValue) {
6448 observer.diff(keypath, newValue, oldValue);
6449 }
6450 };
6451
6452 instance.watcherOptions = {
6453 sync: sync,
6454 watcher: instance.watcher
6455 };
6456
6457 if (instance.fixed = !falsy(deps)) {
6458 each(deps, function (dep) {
6459 observer.watch(dep, instance.watcherOptions);
6460 });
6461 }
6462 }
6463 /**
6464 * 读取计算属性的值
6465 *
6466 * @param force 是否强制刷新缓存
6467 */
6468
6469
6470 Computed.prototype.get = function (force) {
6471 var instance = this,
6472 getter = instance.getter,
6473 context = instance.context; // 禁用缓存
6474
6475 if (!instance.cache) {
6476 instance.value = execute(getter, context);
6477 } // 减少取值频率,尤其是处理复杂的计算规则
6478 else if (force || !has$2(instance, RAW_VALUE)) {
6479 // 如果写死了依赖,则不需要收集依赖
6480 if (instance.fixed) {
6481 instance.value = execute(getter, context);
6482 } else {
6483 // 清空上次收集的依赖
6484 instance.unbind(); // 开始收集新的依赖
6485
6486 var lastComputed = Computed.current;
6487 Computed.current = instance;
6488 instance.value = execute(getter, context); // 绑定新的依赖
6489
6490 instance.bind();
6491 Computed.current = lastComputed;
6492 }
6493 }
6494
6495 return instance.value;
6496 };
6497
6498 Computed.prototype.set = function (value) {
6499 var _a = this,
6500 setter = _a.setter,
6501 context = _a.context;
6502
6503 if (setter) {
6504 setter.call(context, value);
6505 }
6506 };
6507 /**
6508 * 添加依赖
6509 *
6510 * 这里只是为了保证依赖唯一,最后由 bind() 实现绑定
6511 *
6512 * @param dep
6513 */
6514
6515
6516 Computed.prototype.add = function (dep) {
6517 this.unique[dep] = TRUE;
6518 };
6519 /**
6520 * 绑定依赖
6521 */
6522
6523
6524 Computed.prototype.bind = function () {
6525 var _a = this,
6526 unique = _a.unique,
6527 deps = _a.deps,
6528 observer = _a.observer,
6529 watcherOptions = _a.watcherOptions;
6530
6531 each$2(unique, function (_, dep) {
6532 push(deps, dep);
6533 observer.watch(dep, watcherOptions);
6534 }); // 用完重置
6535 // 方便下次收集依赖
6536
6537 this.unique = {};
6538 };
6539 /**
6540 * 解绑依赖
6541 */
6542
6543
6544 Computed.prototype.unbind = function () {
6545 var _a = this,
6546 deps = _a.deps,
6547 observer = _a.observer,
6548 watcher = _a.watcher;
6549
6550 each(deps, function (dep) {
6551 observer.unwatch(dep, watcher);
6552 }, TRUE);
6553 deps.length = 0;
6554 };
6555
6556 return Computed;
6557 }();
6558 /**
6559 * 从 keypath 数组中选择和 keypath 最匹配的那一个
6560 *
6561 * @param sorted 经过排序的 keypath 数组
6562 * @param keypath
6563 */
6564
6565
6566 function matchBest(sorted, keypath) {
6567 var result;
6568 each(sorted, function (prefix) {
6569 var length = match(keypath, prefix);
6570
6571 if (length >= 0) {
6572 result = {
6573 name: prefix,
6574 prop: slice(keypath, length)
6575 };
6576 return FALSE;
6577 }
6578 });
6579 return result;
6580 }
6581
6582 function readValue(source, keypath) {
6583 if (source == NULL || keypath === EMPTY_STRING) {
6584 return source;
6585 }
6586
6587 var result = get(source, keypath);
6588
6589 if (result) {
6590 return result.value;
6591 }
6592 }
6593 /**
6594 * 对比新旧数组
6595 *
6596 * @param newValue
6597 * @param oldValue
6598 * @param callback
6599 */
6600
6601
6602 function diffString(newValue, oldValue, callback) {
6603 var newIsString = string(newValue),
6604 oldIsString = string(oldValue);
6605
6606 if (newIsString || oldIsString) {
6607 callback(RAW_LENGTH, newIsString ? newValue.length : UNDEFINED, oldIsString ? oldValue.length : UNDEFINED);
6608 return TRUE;
6609 }
6610 }
6611 /**
6612 * 对比新旧数组
6613 *
6614 * @param newValue
6615 * @param oldValue
6616 * @param callback
6617 */
6618
6619
6620 function diffArray(newValue, oldValue, callback) {
6621 var newIsArray = array(newValue),
6622 oldIsArray = array(oldValue);
6623
6624 if (newIsArray || oldIsArray) {
6625 var newLength = newIsArray ? newValue.length : UNDEFINED,
6626 oldLength = oldIsArray ? oldValue.length : UNDEFINED;
6627 callback(RAW_LENGTH, newLength, oldLength);
6628
6629 for (var i = 0, length = Math.max(newLength || 0, oldLength || 0); i < length; i++) {
6630 callback('' + i, newValue ? newValue[i] : UNDEFINED, oldValue ? oldValue[i] : UNDEFINED);
6631 }
6632
6633 return TRUE;
6634 }
6635 }
6636 /**
6637 * 对比新旧对象
6638 *
6639 * @param newValue
6640 * @param oldValue
6641 * @param callback
6642 */
6643
6644
6645 function diffObject(newValue, oldValue, callback) {
6646 var newIsObject = object(newValue),
6647 oldIsObject = object(oldValue);
6648
6649 if (newIsObject || oldIsObject) {
6650 newValue = newIsObject ? newValue : EMPTY_OBJECT;
6651 oldValue = oldIsObject ? oldValue : EMPTY_OBJECT;
6652
6653 if (newIsObject) {
6654 each$2(newValue, function (value, key) {
6655 if (value !== oldValue[key]) {
6656 callback(key, value, oldValue[key]);
6657 }
6658 });
6659 }
6660
6661 if (oldIsObject) {
6662 each$2(oldValue, function (value, key) {
6663 if (value !== newValue[key]) {
6664 callback(key, newValue[key], value);
6665 }
6666 });
6667 }
6668 }
6669 }
6670
6671 function diffRecursion(keypath, newValue, oldValue, watchFuzzyKeypaths, callback) {
6672 var diff = function (subKeypath, subNewValue, subOldValue) {
6673 if (subNewValue !== subOldValue) {
6674 var newKeypath_1 = join$1(keypath, subKeypath);
6675 each(watchFuzzyKeypaths, function (fuzzyKeypath) {
6676 if (isDef(matchFuzzy(newKeypath_1, fuzzyKeypath))) {
6677 callback(fuzzyKeypath, newKeypath_1, subNewValue, subOldValue);
6678 }
6679 });
6680 diffRecursion(newKeypath_1, subNewValue, subOldValue, watchFuzzyKeypaths, callback);
6681 }
6682 };
6683
6684 diffString(newValue, oldValue, diff) || diffArray(newValue, oldValue, diff) || diffObject(newValue, oldValue, diff);
6685 }
6686
6687 function diffWatcher(keypath, newValue, oldValue, watcher, isRecursive, callback) {
6688 var fuzzyKeypaths; // 遍历监听的 keypath,如果未被监听,则无需触发任何事件
6689
6690 each$2(watcher, function (_, watchKeypath) {
6691 // 模糊监听,如 users.*.name
6692 if (isFuzzy(watchKeypath)) {
6693 // 如果当前修改的是 users.0 整个对象
6694 // users.0 和 users.*.name 无法匹配
6695 // 此时要知道设置 users.0 到底会不会改变 users.*.name 需要靠递归了
6696 // 如果匹配,则无需递归
6697 if (isDef(matchFuzzy(keypath, watchKeypath))) {
6698 callback(watchKeypath, keypath, newValue, oldValue);
6699 } else if (isRecursive) {
6700 if (fuzzyKeypaths) {
6701 push(fuzzyKeypaths, watchKeypath);
6702 } else {
6703 fuzzyKeypaths = [watchKeypath];
6704 }
6705 }
6706
6707 return;
6708 } // 不是模糊匹配,直接靠前缀匹配
6709 // 比如监听的是 users.0.name,此时修改 users.0,则直接读出子属性值,判断是否相等
6710
6711
6712 var length = match(watchKeypath, keypath);
6713
6714 if (length >= 0) {
6715 var subKeypath = slice(watchKeypath, length),
6716 subNewValue = readValue(newValue, subKeypath),
6717 subOldValue = readValue(oldValue, subKeypath);
6718
6719 if (subNewValue !== subOldValue) {
6720 callback(watchKeypath, watchKeypath, subNewValue, subOldValue);
6721 }
6722 }
6723 }); // 存在模糊匹配的需求
6724 // 必须对数据进行递归
6725 // 性能确实会慢一些,但是很好用啊,几乎可以监听所有的数据
6726
6727 if (fuzzyKeypaths) {
6728 diffRecursion(keypath, newValue, oldValue, fuzzyKeypaths, callback);
6729 }
6730 }
6731 /**
6732 * 触发异步变化时,用此函数过滤下,哪些 listener 应该执行
6733 *
6734 * @param item
6735 * @param data
6736 */
6737
6738
6739 function filterWatcher(_, args, options) {
6740 if (options.count && args) {
6741 // 采用计数器的原因是,同一个 options 可能执行多次
6742 // 比如监听 user.*,如果同批次修改了 user.name 和 user.age
6743 // 这个监听器会调用多次,如果第一次执行就把 count 干掉了,第二次就无法执行了
6744 options.count--; // 新旧值不相等
6745
6746 return args[0] !== args[1];
6747 }
6748 } // 避免频繁创建对象
6749
6750
6751 var optionsHolder = {
6752 watcher: EMPTY_FUNCTION
6753 };
6754 /**
6755 * 格式化 watch options
6756 *
6757 * @param options
6758 */
6759
6760 function formatWatcherOptions(options, immediate) {
6761 if (func(options)) {
6762 optionsHolder.watcher = options;
6763 optionsHolder.immediate = immediate === TRUE;
6764 return optionsHolder;
6765 }
6766
6767 if (options && options.watcher) {
6768 return options;
6769 }
6770
6771 {
6772 fatal("watcher should be a function or object.");
6773 }
6774 }
6775 /**
6776 * 观察者有两种观察模式:
6777 *
6778 * 1. 同步监听
6779 * 2. 异步监听
6780 *
6781 * 对于`计算属性`这种需要实时变化的对象,即它的依赖变了,它需要立即跟着变,否则会出现不一致的问题
6782 * 这种属于同步监听
6783 *
6784 * 对于外部调用 observer.watch('keypath', listener),属于异步监听,它只关心是否变了,而不关心是否是立即触发的
6785 */
6786
6787
6788 var Observer =
6789 /** @class */
6790 function () {
6791 function Observer(data, context) {
6792 var instance = this;
6793 instance.data = data || {};
6794 instance.context = context || instance;
6795 instance.nextTask = new NextTask();
6796 instance.syncEmitter = new Emitter();
6797 instance.asyncEmitter = new Emitter();
6798 instance.asyncChanges = {};
6799 }
6800 /**
6801 * 获取数据
6802 *
6803 * @param keypath
6804 * @param defaultValue
6805 * @param depIgnore
6806 * @return
6807 */
6808
6809
6810 Observer.prototype.get = function (keypath, defaultValue, depIgnore) {
6811 var instance = this,
6812 currentComputed = Computed.current,
6813 data = instance.data,
6814 computed = instance.computed,
6815 reversedComputedKeys = instance.reversedComputedKeys; // 传入 '' 获取整个 data
6816
6817 if (keypath === EMPTY_STRING) {
6818 return data;
6819 } // 调用 get 时,外面想要获取依赖必须设置是谁在收集依赖
6820 // 如果没设置,则跳过依赖收集
6821
6822
6823 if (currentComputed && !depIgnore) {
6824 currentComputed.add(keypath);
6825 }
6826
6827 var result, target;
6828
6829 if (computed) {
6830 target = computed[keypath];
6831
6832 if (target) {
6833 return target.get();
6834 }
6835
6836 if (reversedComputedKeys) {
6837 var match = matchBest(reversedComputedKeys, keypath);
6838
6839 if (match && match.prop) {
6840 result = get(computed[match.name].get(), match.prop);
6841 }
6842 }
6843 }
6844
6845 if (!result) {
6846 result = get(data, keypath);
6847 }
6848
6849 return result ? result.value : defaultValue;
6850 };
6851 /**
6852 * 更新数据
6853 *
6854 * @param keypath
6855 * @param value
6856 */
6857
6858
6859 Observer.prototype.set = function (keypath, value) {
6860 var instance = this,
6861 data = instance.data,
6862 computed = instance.computed,
6863 reversedComputedKeys = instance.reversedComputedKeys,
6864 setValue = function (newValue, keypath) {
6865 var oldValue = instance.get(keypath);
6866
6867 if (newValue === oldValue) {
6868 return;
6869 }
6870
6871 var target;
6872
6873 if (computed) {
6874 target = computed[keypath];
6875
6876 if (target) {
6877 target.set(newValue);
6878 }
6879
6880 if (reversedComputedKeys) {
6881 var match = matchBest(reversedComputedKeys, keypath);
6882
6883 if (match && match.prop) {
6884 target = computed[match.name];
6885
6886 if (target) {
6887 var targetValue = target.get();
6888
6889 if (object(targetValue)) {
6890 set(targetValue, match.prop, newValue);
6891 }
6892 }
6893 }
6894 }
6895 }
6896
6897 if (!target) {
6898 set(data, keypath, newValue);
6899 }
6900
6901 instance.diff(keypath, newValue, oldValue);
6902 };
6903
6904 if (string(keypath)) {
6905 setValue(value, keypath);
6906 } else if (object(keypath)) {
6907 each$2(keypath, setValue);
6908 }
6909 };
6910 /**
6911 * 同步调用的 diff,用于触发 syncEmitter,以及唤醒 asyncEmitter
6912 *
6913 * @param keypath
6914 * @param newValue
6915 * @param oldValue
6916 */
6917
6918
6919 Observer.prototype.diff = function (keypath, newValue, oldValue) {
6920 var instance = this,
6921 syncEmitter = instance.syncEmitter,
6922 asyncEmitter = instance.asyncEmitter,
6923 asyncChanges = instance.asyncChanges,
6924
6925 /**
6926 * 我们认为 $ 开头的变量是不可递归的
6927 * 比如浏览器中常见的 $0 表示当前选中元素
6928 * DOM 元素是不能递归的
6929 */
6930 isRecursive = codeAt(keypath) !== 36;
6931 diffWatcher(keypath, newValue, oldValue, syncEmitter.listeners, isRecursive, function (watchKeypath, keypath, newValue, oldValue) {
6932 syncEmitter.fire(watchKeypath, [newValue, oldValue, keypath]);
6933 });
6934 /**
6935 * 此处有坑,举个例子
6936 *
6937 * observer.watch('a', function () {})
6938 *
6939 * observer.set('a', 1)
6940 *
6941 * observer.watch('a', function () {})
6942 *
6943 * 这里,第一个 watcher 应该触发,但第二个不应该,因为它绑定监听时,值已经是最新的了
6944 */
6945
6946 diffWatcher(keypath, newValue, oldValue, asyncEmitter.listeners, isRecursive, function (watchKeypath, keypath, newValue, oldValue) {
6947 each(asyncEmitter.listeners[watchKeypath], function (item) {
6948 item.count++;
6949 });
6950 var keypaths = (asyncChanges[keypath] || (asyncChanges[keypath] = {
6951 value: oldValue,
6952 keypaths: []
6953 })).keypaths;
6954
6955 if (!has(keypaths, watchKeypath)) {
6956 push(keypaths, watchKeypath);
6957 }
6958
6959 if (!instance.pending) {
6960 instance.pending = TRUE;
6961 instance.nextTask.append(function () {
6962 if (instance.pending) {
6963 instance.pending = UNDEFINED;
6964 instance.diffAsync();
6965 }
6966 });
6967 }
6968 });
6969 };
6970 /**
6971 * 异步触发的 diff
6972 */
6973
6974
6975 Observer.prototype.diffAsync = function () {
6976 var instance = this,
6977 asyncEmitter = instance.asyncEmitter,
6978 asyncChanges = instance.asyncChanges;
6979 instance.asyncChanges = {};
6980 each$2(asyncChanges, function (change, keypath) {
6981 var args = [instance.get(keypath), change.value, keypath]; // 不能在这判断新旧值是否相同,相同就不 fire
6982 // 因为前面标记了 count,在这中断会导致 count 无法清除
6983
6984 each(change.keypaths, function (watchKeypath) {
6985 asyncEmitter.fire(watchKeypath, args, filterWatcher);
6986 });
6987 });
6988 };
6989 /**
6990 * 添加计算属性
6991 *
6992 * @param keypath
6993 * @param computed
6994 */
6995
6996
6997 Observer.prototype.addComputed = function (keypath, options) {
6998 var cache = TRUE,
6999 sync = TRUE,
7000 deps = [],
7001 getter,
7002 setter;
7003
7004 if (func(options)) {
7005 getter = options;
7006 } else if (object(options)) {
7007 var computedOptions = options;
7008
7009 if (boolean(computedOptions.cache)) {
7010 cache = computedOptions.cache;
7011 }
7012
7013 if (boolean(computedOptions.sync)) {
7014 sync = computedOptions.sync;
7015 } // 因为可能会修改 deps,所以这里创建一个新的 deps,避免影响外部传入的 deps
7016
7017
7018 if (array(computedOptions.deps)) {
7019 deps = copy(computedOptions.deps);
7020 }
7021
7022 if (func(computedOptions.get)) {
7023 getter = computedOptions.get;
7024 }
7025
7026 if (func(computedOptions.set)) {
7027 setter = computedOptions.set;
7028 }
7029 }
7030
7031 if (getter) {
7032 var instance = this,
7033 computed = new Computed(keypath, sync, cache, deps, instance, getter, setter);
7034
7035 if (!instance.computed) {
7036 instance.computed = {};
7037 }
7038
7039 instance.computed[keypath] = computed;
7040 instance.reversedComputedKeys = sort(instance.computed, TRUE);
7041 return computed;
7042 }
7043 };
7044 /**
7045 * 移除计算属性
7046 *
7047 * @param keypath
7048 */
7049
7050
7051 Observer.prototype.removeComputed = function (keypath) {
7052 var instance = this,
7053 computed = instance.computed;
7054
7055 if (computed && has$2(computed, keypath)) {
7056 delete computed[keypath];
7057 instance.reversedComputedKeys = sort(computed, TRUE);
7058 }
7059 };
7060 /**
7061 * 监听数据变化
7062 *
7063 * @param keypath
7064 * @param watcher
7065 * @param immediate
7066 */
7067
7068
7069 Observer.prototype.watch = function (keypath, watcher, immediate) {
7070 var instance = this,
7071 context = instance.context,
7072 syncEmitter = instance.syncEmitter,
7073 asyncEmitter = instance.asyncEmitter,
7074 bind = function (keypath, options) {
7075 var emitter = options.sync ? syncEmitter : asyncEmitter,
7076 // formatWatcherOptions 保证了 options.watcher 一定存在
7077 listener = {
7078 fn: options.watcher,
7079 ctx: context,
7080 count: 0
7081 };
7082
7083 if (options.once) {
7084 listener.max = 1;
7085 }
7086
7087 emitter.on(keypath, listener);
7088
7089 if (options.immediate) {
7090 execute(options.watcher, context, [instance.get(keypath), UNDEFINED, keypath]);
7091 }
7092 };
7093
7094 if (string(keypath)) {
7095 bind(keypath, formatWatcherOptions(watcher, immediate));
7096 return;
7097 }
7098
7099 each$2(keypath, function (options, keypath) {
7100 bind(keypath, formatWatcherOptions(options));
7101 });
7102 };
7103 /**
7104 * 取消监听数据变化
7105 *
7106 * @param keypath
7107 * @param watcher
7108 */
7109
7110
7111 Observer.prototype.unwatch = function (keypath, watcher) {
7112 this.syncEmitter.off(keypath, watcher);
7113 this.asyncEmitter.off(keypath, watcher);
7114 };
7115 /**
7116 * 取反 keypath 对应的数据
7117 *
7118 * 不管 keypath 对应的数据是什么类型,操作后都是布尔型
7119 *
7120 * @param keypath
7121 * @return 取反后的布尔值
7122 */
7123
7124
7125 Observer.prototype.toggle = function (keypath) {
7126 var value = !this.get(keypath);
7127 this.set(keypath, value);
7128 return value;
7129 };
7130 /**
7131 * 递增 keypath 对应的数据
7132 *
7133 * 注意,最好是整型的加法,如果涉及浮点型,不保证计算正确
7134 *
7135 * @param keypath 值必须能转型成数字,如果不能,则默认从 0 开始递增
7136 * @param step 步进值,默认是 1
7137 * @param max 可以递增到的最大值,默认不限制
7138 */
7139
7140
7141 Observer.prototype.increase = function (keypath, step, max) {
7142 var value = toNumber(this.get(keypath), 0) + (step || 1);
7143
7144 if (!number(max) || value <= max) {
7145 this.set(keypath, value);
7146 return value;
7147 }
7148 };
7149 /**
7150 * 递减 keypath 对应的数据
7151 *
7152 * 注意,最好是整型的减法,如果涉及浮点型,不保证计算正确
7153 *
7154 * @param keypath 值必须能转型成数字,如果不能,则默认从 0 开始递减
7155 * @param step 步进值,默认是 1
7156 * @param min 可以递减到的最小值,默认不限制
7157 */
7158
7159
7160 Observer.prototype.decrease = function (keypath, step, min) {
7161 var value = toNumber(this.get(keypath), 0) - (step || 1);
7162
7163 if (!number(min) || value >= min) {
7164 this.set(keypath, value);
7165 return value;
7166 }
7167 };
7168 /**
7169 * 在数组指定位置插入元素
7170 *
7171 * @param keypath
7172 * @param item
7173 * @param index
7174 */
7175
7176
7177 Observer.prototype.insert = function (keypath, item, index) {
7178 var list = this.get(keypath);
7179 list = !array(list) ? [] : copy(list);
7180 var length = list.length;
7181
7182 if (index === TRUE || index === length) {
7183 list.push(item);
7184 } else if (index === FALSE || index === 0) {
7185 list.unshift(item);
7186 } else if (index > 0 && index < length) {
7187 list.splice(index, 0, item);
7188 } else {
7189 return;
7190 }
7191
7192 this.set(keypath, list);
7193 return TRUE;
7194 };
7195 /**
7196 * 在数组尾部添加元素
7197 *
7198 * @param keypath
7199 * @param item
7200 */
7201
7202
7203 Observer.prototype.append = function (keypath, item) {
7204 return this.insert(keypath, item, TRUE);
7205 };
7206 /**
7207 * 在数组首部添加元素
7208 *
7209 * @param keypath
7210 * @param item
7211 */
7212
7213
7214 Observer.prototype.prepend = function (keypath, item) {
7215 return this.insert(keypath, item, FALSE);
7216 };
7217 /**
7218 * 通过索引移除数组中的元素
7219 *
7220 * @param keypath
7221 * @param index
7222 */
7223
7224
7225 Observer.prototype.removeAt = function (keypath, index) {
7226 var list = this.get(keypath);
7227
7228 if (array(list) && index >= 0 && index < list.length) {
7229 list = copy(list);
7230 list.splice(index, 1);
7231 this.set(keypath, list);
7232 return TRUE;
7233 }
7234 };
7235 /**
7236 * 直接移除数组中的元素
7237 *
7238 * @param keypath
7239 * @param item
7240 */
7241
7242
7243 Observer.prototype.remove = function (keypath, item) {
7244 var list = this.get(keypath);
7245
7246 if (array(list)) {
7247 list = copy(list);
7248
7249 if (remove(list, item)) {
7250 this.set(keypath, list);
7251 return TRUE;
7252 }
7253 }
7254 };
7255 /**
7256 * 拷贝任意数据,支持深拷贝
7257 *
7258 * @param data
7259 * @param deep
7260 */
7261
7262
7263 Observer.prototype.copy = function (data, deep) {
7264 return copy(data, deep);
7265 };
7266 /**
7267 * 销毁
7268 */
7269
7270
7271 Observer.prototype.destroy = function () {
7272 var instance = this;
7273 instance.syncEmitter.off();
7274 instance.asyncEmitter.off();
7275 instance.nextTask.clear();
7276 clear(instance);
7277 };
7278
7279 return Observer;
7280 }();
7281 /**
7282 * 节流调用
7283 *
7284 * @param fn 需要节制调用的函数
7285 * @param delay 调用的时间间隔,单位毫秒
7286 * @param immediate 是否立即触发
7287 * @return 节流函数
7288 */
7289
7290
7291 function debounce(fn, delay, immediate) {
7292 var timer;
7293 return function () {
7294 if (!timer) {
7295 var args_1 = toArray(arguments);
7296
7297 if (immediate) {
7298 execute(fn, UNDEFINED, args_1);
7299 }
7300
7301 timer = setTimeout(function () {
7302 timer = UNDEFINED;
7303
7304 if (!immediate) {
7305 execute(fn, UNDEFINED, args_1);
7306 }
7307 }, delay);
7308 }
7309 };
7310 }
7311
7312 function bind(node, directive, vnode) {
7313 var key = directive.key,
7314 name = directive.name,
7315 modifier = directive.modifier,
7316 handler = directive.handler,
7317 lazy = vnode.lazy;
7318
7319 if (!handler) {
7320 return;
7321 }
7322
7323 if (lazy) {
7324 var value = lazy[name] || lazy[EMPTY_STRING];
7325
7326 if (value === TRUE) {
7327 name = EVENT_CHANGE;
7328 } else if (value > 0) {
7329 handler = debounce(handler, value, // 避免连续多次点击,主要用于提交表单场景
7330 // 移动端的 tap 事件可自行在业务层打补丁实现
7331 name === EVENT_CLICK || name === EVENT_TAP);
7332 }
7333 }
7334
7335 var element;
7336
7337 if (vnode.isComponent) {
7338 var component_1 = node;
7339
7340 if (modifier === MODIFER_NATIVE) {
7341 element = component_1.$el;
7342
7343 _on(element, name, handler);
7344
7345 vnode.data[key] = function () {
7346 _off(element, name, handler);
7347 };
7348 } else {
7349 // 还原命名空间
7350 if (modifier) {
7351 name += RAW_DOT + modifier;
7352 }
7353
7354 component_1.on(name, handler);
7355
7356 vnode.data[key] = function () {
7357 component_1.off(name, handler);
7358 };
7359 }
7360 } else {
7361 element = node;
7362
7363 _on(element, name, handler);
7364
7365 vnode.data[key] = function () {
7366 _off(element, name, handler);
7367 };
7368 }
7369 }
7370
7371 function unbind(node, directive, vnode) {
7372 execute(vnode.data[directive.key]);
7373 }
7374
7375 var event =
7376 /*#__PURE__*/
7377 {
7378 bind: bind,
7379 unbind: unbind
7380 };
7381
7382 function debounceIfNeeded(fn, lazy) {
7383 // 应用 lazy
7384 return lazy && lazy !== TRUE ? debounce(fn, lazy) : fn;
7385 }
7386
7387 var inputControl = {
7388 set: function set(node, value) {
7389 node.value = toString(value);
7390 },
7391 sync: function sync(node, keypath, context) {
7392 context.set(keypath, node.value);
7393 },
7394 name: RAW_VALUE
7395 },
7396 radioControl = {
7397 set: function set(node, value) {
7398 node.checked = node.value === toString(value);
7399 },
7400 sync: function sync(node, keypath, context) {
7401 if (node.checked) {
7402 context.set(keypath, node.value);
7403 }
7404 },
7405 name: 'checked'
7406 },
7407 checkboxControl = {
7408 set: function set(node, value) {
7409 node.checked = array(value) ? has(value, node.value, FALSE) : !!value;
7410 },
7411 sync: function sync(node, keypath, context) {
7412 var value = context.get(keypath);
7413
7414 if (array(value)) {
7415 if (node.checked) {
7416 context.append(keypath, node.value);
7417 } else {
7418 context.removeAt(keypath, indexOf(value, node.value, FALSE));
7419 }
7420 } else {
7421 context.set(keypath, node.checked);
7422 }
7423 },
7424 name: 'checked'
7425 },
7426 selectControl = {
7427 set: function set(node, value) {
7428 each(toArray(node.options), node.multiple ? function (option) {
7429 option.selected = has(value, option.value, FALSE);
7430 } : function (option, index) {
7431 if (option.value == value) {
7432 node.selectedIndex = index;
7433 return FALSE;
7434 }
7435 });
7436 },
7437 sync: function sync(node, keypath, context) {
7438 var options = node.options;
7439
7440 if (node.multiple) {
7441 var values_1 = [];
7442 each(toArray(options), function (option) {
7443 if (option.selected) {
7444 push(values_1, option.value);
7445 }
7446 });
7447 context.set(keypath, values_1);
7448 } else {
7449 context.set(keypath, options[node.selectedIndex].value);
7450 }
7451 },
7452 name: RAW_VALUE
7453 };
7454 var once = TRUE;
7455
7456 function bind$1(node, directive, vnode) {
7457 var context = vnode.context,
7458 lazy = vnode.lazy,
7459 isComponent = vnode.isComponent,
7460 dataBinding = directive.modifier,
7461 lazyValue = lazy && (lazy[DIRECTIVE_MODEL] || lazy[EMPTY_STRING]),
7462 _set2,
7463 unbind;
7464
7465 if (isComponent) {
7466 var component_1 = node,
7467 viewBinding_1 = component_1.$model,
7468 viewSyncing_1 = debounceIfNeeded(function (newValue) {
7469 context.set(dataBinding, newValue);
7470 }, lazyValue);
7471
7472 _set2 = function set(newValue) {
7473 if (_set2) {
7474 component_1.set(viewBinding_1, newValue);
7475 }
7476 };
7477
7478 unbind = function () {
7479 component_1.unwatch(viewBinding_1, viewSyncing_1);
7480 };
7481
7482 component_1.watch(viewBinding_1, viewSyncing_1);
7483 } else {
7484 var element_1 = node,
7485 control_1 = vnode.tag === 'select' ? selectControl : inputControl,
7486 // checkbox,radio,select 监听的是 change 事件
7487 eventName_1 = EVENT_CHANGE;
7488
7489 if (control_1 === inputControl) {
7490 var type = node.type;
7491
7492 if (type === 'radio') {
7493 control_1 = radioControl;
7494 } else if (type === 'checkbox') {
7495 control_1 = checkboxControl;
7496 } // 如果是输入框,则切换成 model 事件
7497 // model 事件是个 yox-dom 实现的特殊事件
7498 // 不会在输入法组合文字过程中得到触发事件
7499 else if (lazyValue !== TRUE) {
7500 eventName_1 = EVENT_MODEL;
7501 }
7502 }
7503
7504 _set2 = function _set(newValue) {
7505 if (_set2) {
7506 control_1.set(element_1, newValue);
7507 }
7508 };
7509
7510 var sync_1 = debounceIfNeeded(function () {
7511 control_1.sync(element_1, dataBinding, context);
7512 }, lazyValue);
7513
7514 unbind = function () {
7515 _off(element_1, eventName_1, sync_1);
7516 };
7517
7518 _on(element_1, eventName_1, sync_1);
7519
7520 control_1.set(element_1, directive.value);
7521 } // 监听数据,修改界面
7522
7523
7524 context.watch(dataBinding, _set2);
7525
7526 vnode.data[directive.key] = function () {
7527 context.unwatch(dataBinding, _set2);
7528 _set2 = UNDEFINED;
7529 unbind();
7530 };
7531 }
7532
7533 function unbind$1(node, directive, vnode) {
7534 execute(vnode.data[directive.key]);
7535 }
7536
7537 var model =
7538 /*#__PURE__*/
7539 {
7540 once: once,
7541 bind: bind$1,
7542 unbind: unbind$1
7543 };
7544 var once$1 = TRUE;
7545
7546 function bind$2(node, directive, vnode) {
7547 // binding 可能是模糊匹配
7548 // 比如延展属性 {{...obj}},这里 binding 会是 `obj.*`
7549 var binding = directive.modifier,
7550 // 提前判断好是否是模糊匹配,避免 watcher 频繁执行判断逻辑
7551 isFuzzy$1 = isFuzzy(binding),
7552 _watcher = function watcher(newValue, _, keypath) {
7553 if (_watcher) {
7554 var name = isFuzzy$1 ? matchFuzzy(keypath, binding) : directive.name;
7555
7556 if (vnode.isComponent) {
7557 var component = node;
7558 component.checkProp(name, newValue);
7559 component.set(name, newValue);
7560 } else {
7561 var element = node;
7562
7563 if (isDef(directive.hint)) {
7564 prop(element, name, newValue);
7565 } else {
7566 attr(element, name, newValue);
7567 }
7568 }
7569 }
7570 };
7571
7572 vnode.context.watch(binding, _watcher);
7573
7574 vnode.data[directive.key] = function () {
7575 vnode.context.unwatch(binding, _watcher);
7576 _watcher = UNDEFINED;
7577 };
7578 }
7579
7580 function unbind$2(node, directive, vnode) {
7581 execute(vnode.data[directive.key]);
7582 }
7583
7584 var binding =
7585 /*#__PURE__*/
7586 {
7587 once: once$1,
7588 bind: bind$2,
7589 unbind: unbind$2
7590 };
7591 var globalDirectives = {},
7592 globalTransitions = {},
7593 globalComponents = {},
7594 globalPartials = {},
7595 globalFilters = {},
7596 compileCache = {},
7597 TEMPLATE_COMPUTED = '$$',
7598 selectorPattern = /^[#.][-\w+]+$/;
7599
7600 var Yox =
7601 /** @class */
7602 function () {
7603 function Yox(options) {
7604 var instance = this,
7605 $options = options || EMPTY_OBJECT; // 为了冒泡 HOOK_BEFORE_CREATE 事件,必须第一时间创建 emitter
7606 // 监听各种事件
7607 // 支持命名空间
7608
7609 instance.$emitter = new Emitter(TRUE);
7610
7611 if ($options.events) {
7612 instance.on($options.events);
7613 }
7614
7615 {
7616 // 当前组件的直接父组件
7617 if ($options.parent) {
7618 instance.$parent = $options.parent;
7619 } // 建立好父子连接后,立即触发钩子
7620
7621
7622 execute($options[HOOK_BEFORE_CREATE], instance, $options); // 冒泡 before create 事件
7623
7624 instance.fire(HOOK_BEFORE_CREATE + NAMESPACE_HOOK, $options);
7625 }
7626 var data = $options.data,
7627 props = $options.props,
7628 vnode = $options.vnode,
7629 propTypes = $options.propTypes,
7630 computed = $options.computed,
7631 methods = $options.methods,
7632 watchers = $options.watchers,
7633 extensions = $options.extensions;
7634 instance.$options = $options;
7635
7636 if (extensions) {
7637 extend(instance, extensions);
7638 } // 数据源,默认值仅在创建组件时启用
7639
7640
7641 var source = props ? copy(props) : {};
7642 {
7643 if (propTypes) {
7644 each$2(propTypes, function (rule, key) {
7645 var value = source[key];
7646 {
7647 checkProp(key, value, rule);
7648 }
7649
7650 if (isUndef(value)) {
7651 value = rule.value;
7652
7653 if (isDef(value)) {
7654 source[key] = rule.type === RAW_FUNCTION ? value : func(value) ? value() : value;
7655 }
7656 }
7657 });
7658 }
7659 } // 先放 props
7660 // 当 data 是函数时,可以通过 this.get() 获取到外部数据
7661
7662 var observer = instance.$observer = new Observer(source, instance);
7663
7664 if (computed) {
7665 each$2(computed, function (options, keypath) {
7666 observer.addComputed(keypath, options);
7667 });
7668 } // 后放 data
7669
7670
7671 {
7672 if (vnode && object(data)) {
7673 warn("The \"data\" option of child component should be a function which return an object.");
7674 }
7675 }
7676 var extend$1 = func(data) ? execute(data, instance, options) : data;
7677
7678 if (object(extend$1)) {
7679 each$2(extend$1, function (value, key) {
7680 {
7681 if (has$2(source, key)) {
7682 warn("The data \"" + key + "\" is already used as a prop.");
7683 }
7684 }
7685 source[key] = value;
7686 });
7687 }
7688
7689 if (methods) {
7690 each$2(methods, function (method, name) {
7691 {
7692 if (instance[name]) {
7693 fatal("The method \"" + name + "\" is conflicted with built-in methods.");
7694 }
7695 }
7696 instance[name] = method;
7697 });
7698 }
7699
7700 {
7701 var placeholder = UNDEFINED,
7702 el = $options.el,
7703 root = $options.root,
7704 model_1 = $options.model,
7705 context = $options.context,
7706 replace = $options.replace,
7707 template = $options.template,
7708 transitions = $options.transitions,
7709 components = $options.components,
7710 directives = $options.directives,
7711 partials = $options.partials,
7712 filters = $options.filters,
7713 slots = $options.slots;
7714
7715 if (model_1) {
7716 instance.$model = model_1;
7717 } // 把 slots 放进数据里,方便 get
7718
7719
7720 if (slots) {
7721 extend(source, slots);
7722 } // 检查 template
7723
7724
7725 if (string(template)) {
7726 // 传了选择器,则取对应元素的 html
7727 if (selectorPattern.test(template)) {
7728 placeholder = find(template);
7729
7730 if (placeholder) {
7731 template = html(placeholder);
7732 placeholder = UNDEFINED;
7733 } else {
7734 fatal("The selector \"" + template + "\" can't match an element.");
7735 }
7736 }
7737 } // 检查 el
7738
7739
7740 if (el) {
7741 if (string(el)) {
7742 var selector = el;
7743
7744 if (selectorPattern.test(selector)) {
7745 placeholder = find(selector);
7746 {
7747 if (!placeholder) {
7748 fatal("The selector \"" + selector + "\" can't match an element.");
7749 }
7750 }
7751 } else {
7752 fatal("The \"el\" option should be a selector.");
7753 }
7754 } else {
7755 placeholder = el;
7756 }
7757
7758 if (!replace) {
7759 append(placeholder, placeholder = createComment(EMPTY_STRING));
7760 }
7761 } // 根组件
7762
7763
7764 if (root) {
7765 instance.$root = root;
7766 } // 当前组件是被哪个组件渲染出来的
7767 // 因为有 slot 机制,$context 不一定等于 $parent
7768
7769
7770 if (context) {
7771 instance.$context = context;
7772 }
7773
7774 setFlexibleOptions(instance, RAW_TRANSITION, transitions);
7775 setFlexibleOptions(instance, RAW_COMPONENT, components);
7776 setFlexibleOptions(instance, RAW_DIRECTIVE, directives);
7777 setFlexibleOptions(instance, RAW_PARTIAL, partials);
7778 setFlexibleOptions(instance, RAW_FILTER, filters); // 当存在模板和计算属性时
7779 // 因为这里把模板当做一种特殊的计算属性
7780 // 因此模板这个计算属性的优先级应该最高
7781
7782 if (template) {
7783 // 拷贝一份,避免影响外部定义的 watchers
7784 var newWatchers = watchers ? copy(watchers) : {};
7785 newWatchers[TEMPLATE_COMPUTED] = {
7786 // 模板一旦变化,立即刷新
7787 sync: TRUE,
7788 watcher: function watcher(vnode) {
7789 instance.update(vnode, instance.$vnode);
7790 }
7791 }; // 当模板的依赖变了,则重新创建 virtual dom
7792
7793 observer.addComputed(TEMPLATE_COMPUTED, {
7794 // 当模板依赖变化时,异步通知模板更新
7795 sync: FALSE,
7796 get: function get() {
7797 return instance.render();
7798 }
7799 });
7800 instance.watch(newWatchers);
7801 {
7802 execute(instance.$options[HOOK_AFTER_CREATE], instance);
7803 instance.fire(HOOK_AFTER_CREATE + NAMESPACE_HOOK);
7804 } // 编译模板
7805 // 在开发阶段,template 是原始的 html 模板
7806 // 在产品阶段,template 是编译后的渲染函数
7807 // 当然,具体是什么需要外部自己控制
7808
7809 instance.$template = string(template) ? Yox.compile(template) : template;
7810
7811 if (!vnode) {
7812 {
7813 if (!placeholder) {
7814 fatal('The "el" option is required for root component.');
7815 }
7816 }
7817 vnode = create(domApi, placeholder, instance, EMPTY_STRING);
7818 }
7819
7820 instance.update(instance.get(TEMPLATE_COMPUTED), vnode);
7821 return;
7822 } else {
7823 if (placeholder || vnode) {
7824 fatal('The "template" option is required.');
7825 }
7826 }
7827 }
7828
7829 if (watchers) {
7830 instance.watch(watchers);
7831 }
7832
7833 {
7834 execute(instance.$options[HOOK_AFTER_CREATE], instance);
7835 instance.fire(HOOK_AFTER_CREATE + NAMESPACE_HOOK);
7836 }
7837 }
7838 /**
7839 * 定义组件对象
7840 */
7841
7842
7843 Yox.define = function (options) {
7844 return options;
7845 };
7846 /**
7847 * 安装插件
7848 *
7849 * 插件必须暴露 install 方法
7850 */
7851
7852
7853 Yox.use = function (plugin) {
7854 plugin.install(Yox);
7855 };
7856 /**
7857 * 因为组件采用的是异步更新机制,为了在更新之后进行一些操作,可使用 nextTick
7858 */
7859
7860
7861 Yox.nextTick = function (task, context) {
7862 NextTask.shared().append(task, context);
7863 };
7864 /**
7865 * 编译模板,暴露出来是为了打包阶段的模板预编译
7866 */
7867
7868
7869 Yox.compile = function (template, stringify) {
7870 {
7871 // 需要编译的都是模板源文件,一旦经过预编译,就成了 render 函数,不会再走进 Yox.compile
7872 if (!compileCache[template]) {
7873 var nodes = compile$1(template);
7874 {
7875 if (nodes.length !== 1) {
7876 fatal("The \"template\" option should have just one root element.");
7877 }
7878 }
7879 compileCache[template] = generate$1(nodes[0]);
7880 }
7881
7882 template = compileCache[template];
7883 return stringify ? template : new Function("return " + template)();
7884 }
7885 };
7886 /**
7887 * 注册全局指令
7888 */
7889
7890
7891 Yox.directive = function (name, directive) {
7892 {
7893 if (string(name) && !directive) {
7894 return getResource(globalDirectives, name);
7895 }
7896
7897 setResource(globalDirectives, name, directive);
7898 }
7899 };
7900 /**
7901 * 注册全局过渡动画
7902 */
7903
7904
7905 Yox.transition = function (name, transition) {
7906 {
7907 if (string(name) && !transition) {
7908 return getResource(globalTransitions, name);
7909 }
7910
7911 setResource(globalTransitions, name, transition);
7912 }
7913 };
7914 /**
7915 * 注册全局组件
7916 */
7917
7918
7919 Yox.component = function (name, component) {
7920 {
7921 if (string(name) && !component) {
7922 return getResource(globalComponents, name);
7923 }
7924
7925 setResource(globalComponents, name, component);
7926 }
7927 };
7928 /**
7929 * 注册全局子模板
7930 */
7931
7932
7933 Yox.partial = function (name, partial) {
7934 {
7935 if (string(name) && !partial) {
7936 return getResource(globalPartials, name);
7937 }
7938
7939 setResource(globalPartials, name, partial, Yox.compile);
7940 }
7941 };
7942 /**
7943 * 注册全局过滤器
7944 */
7945
7946
7947 Yox.filter = function (name, filter) {
7948 {
7949 if (string(name) && !filter) {
7950 return getResource(globalFilters, name);
7951 }
7952
7953 setResource(globalFilters, name, filter);
7954 }
7955 };
7956 /**
7957 * 取值
7958 */
7959
7960
7961 Yox.prototype.get = function (keypath, defaultValue) {
7962 return this.$observer.get(keypath, defaultValue);
7963 };
7964 /**
7965 * 设值
7966 */
7967
7968
7969 Yox.prototype.set = function (keypath, value) {
7970 // 组件经常有各种异步改值,为了避免组件销毁后依然调用 set
7971 // 这里判断一下,至于其他方法的异步调用就算了,业务自己控制吧
7972 var $observer = this.$observer;
7973
7974 if ($observer) {
7975 $observer.set(keypath, value);
7976 }
7977 };
7978 /**
7979 * 监听事件,支持链式调用
7980 */
7981
7982
7983 Yox.prototype.on = function (type, listener) {
7984 addEvents(this, type, listener);
7985 return this;
7986 };
7987 /**
7988 * 监听一次事件,支持链式调用
7989 */
7990
7991
7992 Yox.prototype.once = function (type, listener) {
7993 addEvents(this, type, listener, TRUE);
7994 return this;
7995 };
7996 /**
7997 * 取消监听事件,支持链式调用
7998 */
7999
8000
8001 Yox.prototype.off = function (type, listener) {
8002 this.$emitter.off(type, listener);
8003 return this;
8004 };
8005 /**
8006 * 发射事件
8007 */
8008
8009
8010 Yox.prototype.fire = function (type, data, downward) {
8011 // 外部为了使用方便,fire(type) 或 fire(type, data) 就行了
8012 // 内部为了保持格式统一
8013 // 需要转成 Event,这样还能知道 target 是哪个组件
8014 var instance = this,
8015 $emitter = instance.$emitter,
8016 $parent = instance.$parent,
8017 $children = instance.$children,
8018 event = type instanceof CustomEvent ? type : new CustomEvent(type),
8019 args = [event],
8020 isComplete; // 创建完 CustomEvent,如果没有人为操作
8021 // 它的 ns 为 undefined
8022 // 这里先解析出命名空间,避免每次 fire 都要解析
8023
8024 if (isUndef(event.ns)) {
8025 var namespace = $emitter.parse(event.type);
8026 event.type = namespace.type;
8027 event.ns = namespace.ns;
8028 } // 告诉外部是谁发出的事件
8029
8030
8031 if (!event.target) {
8032 event.target = instance;
8033 } // 比如 fire('name', true) 直接向下发事件
8034
8035
8036 if (object(data)) {
8037 push(args, data);
8038 } else if (data === TRUE) {
8039 downward = TRUE;
8040 } // 如果手动 fire 带上了事件命名空间
8041 // 则命名空间不能是 native,因为 native 有特殊用处
8042
8043
8044 {
8045 if (event.ns === MODIFER_NATIVE) {
8046 error("The namespace \"" + MODIFER_NATIVE + "\" is not permitted.");
8047 }
8048 }
8049 isComplete = $emitter.fire(event, args);
8050
8051 if (isComplete) {
8052 if (downward) {
8053 if ($children) {
8054 event.phase = CustomEvent.PHASE_DOWNWARD;
8055 each($children, function (child) {
8056 return isComplete = child.fire(event, data, TRUE);
8057 });
8058 }
8059 } else if ($parent) {
8060 event.phase = CustomEvent.PHASE_UPWARD;
8061 isComplete = $parent.fire(event, data);
8062 }
8063 }
8064
8065 return isComplete;
8066 };
8067 /**
8068 * 监听数据变化,支持链式调用
8069 */
8070
8071
8072 Yox.prototype.watch = function (keypath, watcher, immediate) {
8073 this.$observer.watch(keypath, watcher, immediate);
8074 return this;
8075 };
8076 /**
8077 * 取消监听数据变化,支持链式调用
8078 */
8079
8080
8081 Yox.prototype.unwatch = function (keypath, watcher) {
8082 this.$observer.unwatch(keypath, watcher);
8083 return this;
8084 };
8085 /**
8086 * 加载组件,组件可以是同步或异步,最后会调用 callback
8087 *
8088 * @param name 组件名称
8089 * @param callback 组件加载成功后的回调
8090 */
8091
8092
8093 Yox.prototype.loadComponent = function (name, callback) {
8094 {
8095 if (!loadComponent(this.$components, name, callback)) {
8096 {
8097 if (!loadComponent(globalComponents, name, callback)) {
8098 error("The component \"" + name + "\" is not found.");
8099 }
8100 }
8101 }
8102 }
8103 };
8104 /**
8105 * 创建子组件
8106 *
8107 * @param options 组件配置
8108 * @param vnode 虚拟节点
8109 */
8110
8111
8112 Yox.prototype.createComponent = function (options, vnode) {
8113 {
8114 var instance = this;
8115 options = copy(options);
8116 options.root = instance.$root || instance;
8117 options.parent = instance;
8118 options.context = vnode.context;
8119 options.vnode = vnode;
8120 options.replace = TRUE;
8121 var props = vnode.props,
8122 slots = vnode.slots,
8123 directives = vnode.directives,
8124 model_2 = directives && directives[DIRECTIVE_MODEL];
8125
8126 if (model_2) {
8127 if (!props) {
8128 props = {};
8129 }
8130
8131 var key = options.model || MODEL_PROP_DEFAULT;
8132 props[key] = model_2.value;
8133 options.model = key;
8134 }
8135
8136 if (props) {
8137 options.props = props;
8138 }
8139
8140 if (slots) {
8141 options.slots = slots;
8142 }
8143
8144 var child = new Yox(options);
8145 push(instance.$children || (instance.$children = []), child);
8146 var node = child.$el;
8147
8148 if (node) {
8149 vnode.node = node;
8150 } else {
8151 fatal("The root element of component \"" + vnode.tag + "\" is not found.");
8152 }
8153
8154 return child;
8155 }
8156 };
8157 /**
8158 * 注册当前组件级别的指令
8159 */
8160
8161
8162 Yox.prototype.directive = function (name, directive) {
8163 {
8164 var instance = this,
8165 $directives = instance.$directives;
8166
8167 if (string(name) && !directive) {
8168 return getResource($directives, name, Yox.directive);
8169 }
8170
8171 setResource($directives || (instance.$directives = {}), name, directive);
8172 }
8173 };
8174 /**
8175 * 注册当前组件级别的过渡动画
8176 */
8177
8178
8179 Yox.prototype.transition = function (name, transition) {
8180 {
8181 var instance = this,
8182 $transitions = instance.$transitions;
8183
8184 if (string(name) && !transition) {
8185 return getResource($transitions, name, Yox.transition);
8186 }
8187
8188 setResource($transitions || (instance.$transitions = {}), name, transition);
8189 }
8190 };
8191 /**
8192 * 注册当前组件级别的组件
8193 */
8194
8195
8196 Yox.prototype.component = function (name, component) {
8197 {
8198 var instance = this,
8199 $components = instance.$components;
8200
8201 if (string(name) && !component) {
8202 return getResource($components, name, Yox.component);
8203 }
8204
8205 setResource($components || (instance.$components = {}), name, component);
8206 }
8207 };
8208 /**
8209 * 注册当前组件级别的子模板
8210 */
8211
8212
8213 Yox.prototype.partial = function (name, partial) {
8214 {
8215 var instance = this,
8216 $partials = instance.$partials;
8217
8218 if (string(name) && !partial) {
8219 return getResource($partials, name, Yox.partial);
8220 }
8221
8222 setResource($partials || (instance.$partials = {}), name, partial, Yox.compile);
8223 }
8224 };
8225 /**
8226 * 注册当前组件级别的过滤器
8227 */
8228
8229
8230 Yox.prototype.filter = function (name, filter) {
8231 {
8232 var instance = this,
8233 $filters = instance.$filters;
8234
8235 if (string(name) && !filter) {
8236 return getResource($filters, name, Yox.filter);
8237 }
8238
8239 setResource($filters || (instance.$filters = {}), name, filter);
8240 }
8241 };
8242 /**
8243 * 对于某些特殊场景,修改了数据,但是模板的依赖中并没有这一项
8244 * 而你非常确定需要更新模板,强制刷新正是你需要的
8245 */
8246
8247
8248 Yox.prototype.forceUpdate = function (data) {
8249 {
8250 var instance = this,
8251 $vnode = instance.$vnode,
8252 $observer = instance.$observer,
8253 computed = $observer.computed;
8254
8255 if ($vnode && computed) {
8256 var template = computed[TEMPLATE_COMPUTED],
8257 oldValue = template.get();
8258
8259 if (data) {
8260 instance.set(data);
8261 } // 当前可能正在进行下一轮更新
8262
8263
8264 $observer.nextTask.run(); // 没有更新模板,强制刷新
8265
8266 if (!data && oldValue === template.get()) {
8267 instance.update(template.get(TRUE), $vnode);
8268 }
8269 }
8270 }
8271 };
8272 /**
8273 * 把模板抽象语法树渲染成 virtual dom
8274 */
8275
8276
8277 Yox.prototype.render = function () {
8278 {
8279 var instance = this;
8280 return render(instance, instance.$observer, instance.$template, merge(instance.$filters, globalFilters), merge(instance.$partials, globalPartials), merge(instance.$directives, globalDirectives), merge(instance.$transitions, globalTransitions));
8281 }
8282 };
8283 /**
8284 * 更新 virtual dom
8285 *
8286 * @param vnode
8287 * @param oldVnode
8288 */
8289
8290
8291 Yox.prototype.update = function (vnode, oldVnode) {
8292 {
8293 var instance_1 = this,
8294 $vnode = instance_1.$vnode,
8295 $options_1 = instance_1.$options,
8296 afterHook_1; // 每次渲染重置 refs
8297 // 在渲染过程中收集最新的 ref
8298 // 这样可避免更新时,新的 ref,在前面创建,老的 ref 却在后面删除的情况
8299
8300 instance_1.$refs = {};
8301
8302 if ($vnode) {
8303 execute($options_1[HOOK_BEFORE_UPDATE], instance_1);
8304 instance_1.fire(HOOK_BEFORE_UPDATE + NAMESPACE_HOOK);
8305 patch(domApi, vnode, oldVnode);
8306 afterHook_1 = HOOK_AFTER_UPDATE;
8307 } else {
8308 execute($options_1[HOOK_BEFORE_MOUNT], instance_1);
8309 instance_1.fire(HOOK_BEFORE_MOUNT + NAMESPACE_HOOK);
8310 patch(domApi, vnode, oldVnode);
8311 instance_1.$el = vnode.node;
8312 afterHook_1 = HOOK_AFTER_MOUNT;
8313 }
8314
8315 instance_1.$vnode = vnode; // 跟 nextTask 保持一个节奏
8316 // 这样可以预留一些优化的余地
8317
8318 Yox.nextTick(function () {
8319 if (instance_1.$vnode) {
8320 execute($options_1[afterHook_1], instance_1);
8321 instance_1.fire(afterHook_1 + NAMESPACE_HOOK);
8322 }
8323 });
8324 }
8325 };
8326 /**
8327 * 校验组件参数
8328 *
8329 * @param props
8330 */
8331
8332
8333 Yox.prototype.checkProp = function (key, value) {
8334 {
8335 var propTypes = this.$options.propTypes;
8336
8337 if (propTypes) {
8338 var rule = propTypes[key];
8339
8340 if (rule) {
8341 checkProp(key, value, rule);
8342 }
8343 }
8344 }
8345 };
8346 /**
8347 * 销毁组件
8348 */
8349
8350
8351 Yox.prototype.destroy = function () {
8352 var instance = this,
8353 $parent = instance.$parent,
8354 $options = instance.$options,
8355 $emitter = instance.$emitter,
8356 $observer = instance.$observer;
8357 {
8358 execute($options[HOOK_BEFORE_DESTROY], instance);
8359 instance.fire(HOOK_BEFORE_DESTROY + NAMESPACE_HOOK);
8360 var $vnode = instance.$vnode;
8361
8362 if ($parent && $parent.$children) {
8363 remove($parent.$children, instance);
8364 }
8365
8366 if ($vnode) {
8367 // virtual dom 通过判断 parent.$vnode 知道宿主组件是否正在销毁
8368 instance.$vnode = UNDEFINED;
8369 destroy(domApi, $vnode, !$parent);
8370 }
8371 }
8372 $observer.destroy();
8373 {
8374 execute($options[HOOK_AFTER_DESTROY], instance);
8375 instance.fire(HOOK_AFTER_DESTROY + NAMESPACE_HOOK);
8376 } // 发完 after destroy 事件再解绑所有事件
8377
8378 $emitter.off();
8379 clear(instance);
8380 };
8381 /**
8382 * 因为组件采用的是异步更新机制,为了在更新之后进行一些操作,可使用 nextTick
8383 */
8384
8385
8386 Yox.prototype.nextTick = function (task) {
8387 this.$observer.nextTask.append(task, this);
8388 };
8389 /**
8390 * 取反 keypath 对应的数据
8391 *
8392 * 不管 keypath 对应的数据是什么类型,操作后都是布尔型
8393 */
8394
8395
8396 Yox.prototype.toggle = function (keypath) {
8397 return this.$observer.toggle(keypath);
8398 };
8399 /**
8400 * 递增 keypath 对应的数据
8401 *
8402 * 注意,最好是整型的加法,如果涉及浮点型,不保证计算正确
8403 *
8404 * @param keypath 值必须能转型成数字,如果不能,则默认从 0 开始递增
8405 * @param step 步进值,默认是 1
8406 * @param max 可以递增到的最大值,默认不限制
8407 */
8408
8409
8410 Yox.prototype.increase = function (keypath, step, max) {
8411 return this.$observer.increase(keypath, step, max);
8412 };
8413 /**
8414 * 递减 keypath 对应的数据
8415 *
8416 * 注意,最好是整型的减法,如果涉及浮点型,不保证计算正确
8417 *
8418 * @param keypath 值必须能转型成数字,如果不能,则默认从 0 开始递减
8419 * @param step 步进值,默认是 1
8420 * @param min 可以递减到的最小值,默认不限制
8421 */
8422
8423
8424 Yox.prototype.decrease = function (keypath, step, min) {
8425 return this.$observer.decrease(keypath, step, min);
8426 };
8427 /**
8428 * 在数组指定位置插入元素
8429 *
8430 * @param keypath
8431 * @param item
8432 * @param index
8433 */
8434
8435
8436 Yox.prototype.insert = function (keypath, item, index) {
8437 return this.$observer.insert(keypath, item, index);
8438 };
8439 /**
8440 * 在数组尾部添加元素
8441 *
8442 * @param keypath
8443 * @param item
8444 */
8445
8446
8447 Yox.prototype.append = function (keypath, item) {
8448 return this.$observer.append(keypath, item);
8449 };
8450 /**
8451 * 在数组首部添加元素
8452 *
8453 * @param keypath
8454 * @param item
8455 */
8456
8457
8458 Yox.prototype.prepend = function (keypath, item) {
8459 return this.$observer.prepend(keypath, item);
8460 };
8461 /**
8462 * 通过索引移除数组中的元素
8463 *
8464 * @param keypath
8465 * @param index
8466 */
8467
8468
8469 Yox.prototype.removeAt = function (keypath, index) {
8470 return this.$observer.removeAt(keypath, index);
8471 };
8472 /**
8473 * 直接移除数组中的元素
8474 *
8475 * @param keypath
8476 * @param item
8477 */
8478
8479
8480 Yox.prototype.remove = function (keypath, item) {
8481 return this.$observer.remove(keypath, item);
8482 };
8483 /**
8484 * 拷贝任意数据,支持深拷贝
8485 *
8486 * @param data
8487 * @param deep
8488 */
8489
8490
8491 Yox.prototype.copy = function (data, deep) {
8492 return this.$observer.copy(data, deep);
8493 };
8494 /**
8495 * core 版本
8496 */
8497
8498
8499 Yox.version = "1.0.0-alpha.95";
8500 /**
8501 * 方便外部共用的通用逻辑,特别是写插件,减少重复代码
8502 */
8503
8504 Yox.is = is;
8505 Yox.dom = domApi;
8506 Yox.array = array$1;
8507 Yox.object = object$1;
8508 Yox.string = string$1;
8509 Yox.logger = logger;
8510 Yox.Event = CustomEvent;
8511 Yox.Emitter = Emitter;
8512 return Yox;
8513 }();
8514
8515 var toString$2 = Object.prototype.toString;
8516
8517 function matchType(value, type) {
8518 return type === 'numeric' ? numeric(value) : lower(toString$2.call(value)) === "[object " + type + "]";
8519 }
8520
8521 function checkProp(key, value, rule) {
8522 // 传了数据
8523 if (isDef(value)) {
8524 var type = rule.type; // 如果不写 type 或 type 不是 字符串 或 数组
8525 // 就当做此规则无效,和没写一样
8526
8527 if (type) {
8528 // 自定义函数判断是否匹配类型
8529 // 自己打印警告信息吧
8530 if (func(type)) {
8531 type(key, value);
8532 } else {
8533 var matched_1 = FALSE; // type: 'string'
8534
8535 if (!falsy$1(type)) {
8536 matched_1 = matchType(value, type);
8537 } // type: ['string', 'number']
8538 else if (!falsy(type)) {
8539 each(type, function (item) {
8540 if (matchType(value, item)) {
8541 matched_1 = TRUE;
8542 return FALSE;
8543 }
8544 });
8545 }
8546
8547 if (!matched_1) {
8548 warn("The type of prop \"" + key + "\" expected to be \"" + type + "\", but is \"" + value + "\".");
8549 }
8550 }
8551 } else {
8552 warn("The prop \"" + key + "\" in propTypes has no type.");
8553 }
8554 } // 没传值但此项是必传项
8555 else if (rule.required) {
8556 warn("The prop \"" + key + "\" is marked as required, but its value is not found.");
8557 }
8558 }
8559
8560 function setFlexibleOptions(instance, key, value) {
8561 if (func(value)) {
8562 instance[key](execute(value, instance));
8563 } else if (object(value)) {
8564 instance[key](value);
8565 }
8566 }
8567
8568 function addEvent(instance, type, listener, once) {
8569 var options = {
8570 fn: listener,
8571 ctx: instance
8572 };
8573
8574 if (once) {
8575 options.max = 1;
8576 } // YoxInterface 没有声明 $emitter,因为不想让外部访问,
8577 // 但是这里要用一次,所以加了 as any
8578
8579
8580 instance.$emitter.on(type, options);
8581 }
8582
8583 function addEvents(instance, type, listener, once) {
8584 if (string(type)) {
8585 addEvent(instance, type, listener, once);
8586 } else {
8587 each$2(type, function (value, key) {
8588 addEvent(instance, key, value, once);
8589 });
8590 }
8591 }
8592
8593 function loadComponent(registry, name, callback) {
8594 if (registry && registry[name]) {
8595 var component = registry[name]; // 注册的是异步加载函数
8596
8597 if (func(component)) {
8598 registry[name] = [callback];
8599
8600 var componentCallback = function (result) {
8601 var queue = registry[name],
8602 options = result['default'] || result;
8603 registry[name] = options;
8604 each(queue, function (callback) {
8605 callback(options);
8606 });
8607 },
8608 promise = component(componentCallback);
8609
8610 if (promise) {
8611 promise.then(componentCallback);
8612 }
8613 } // 正在加载中
8614 else if (array(component)) {
8615 push(component, callback);
8616 } // 不是异步加载函数,直接同步返回
8617 else {
8618 callback(component);
8619 }
8620
8621 return TRUE;
8622 }
8623 }
8624
8625 function getResource(registry, name, lookup) {
8626 if (registry && registry[name]) {
8627 return registry[name];
8628 } else if (lookup) {
8629 return lookup(name);
8630 }
8631 }
8632
8633 function setResource(registry, name, value, formatValue) {
8634 if (string(name)) {
8635 registry[name] = formatValue ? formatValue(value) : value;
8636 } else {
8637 each$2(name, function (value, key) {
8638 registry[key] = formatValue ? formatValue(value) : value;
8639 });
8640 }
8641 }
8642
8643 {
8644 // 全局注册内置指令
8645 Yox.directive({
8646 event: event,
8647 model: model,
8648 binding: binding
8649 }); // 全局注册内置过滤器
8650
8651 Yox.filter({
8652 hasSlot: function hasSlot(name) {
8653 // 不鼓励在过滤器使用 this
8654 // 因此过滤器没有 this 的类型声明
8655 // 这个内置过滤器是不得不用 this
8656 return isDef(this.get(SLOT_DATA_PREFIX + name));
8657 }
8658 });
8659 }
8660 return Yox;
8661 });
8662 });
8663
8664 /*
8665 This file 'index' is part of Firebird Integrated Solution 1.0
8666
8667 Copyright (c) 2019 Lincong
8668
8669 Contact:
8670 Email: lincong1987@gmail.com
8671
8672 QQ: 159257119
8673
8674 See Usage at http://www.jplatformx.com/firebird
8675
8676 Create date: 2019-03-06 16:20
8677 */
8678 //require("es5-polyfill")
8679
8680 var test = yox;
8681
8682 exports["default"] = test;
8683
8684 Object.defineProperty(exports, '__esModule', { value: true });
8685
8686}));