1/*! 2 * Vue.js v1.0.28 3 * (c) 2016 Evan You 4 * Released under the MIT License. 5 */ 6(function (global, factory) { 7 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 8 typeof define === 'function' && define.amd ? define(factory) : 9 (global.Vue = factory()); 10}(this, (function () { 'use strict'; 11 12function set(obj, key, val) { 13 if (hasOwn(obj, key)) { 14 obj[key] = val; 15 return; 16 } 17 if (obj._isVue) { 18 set(obj._data, key, val); 19 return; 20 } 21 var ob = obj.__ob__; 22 if (!ob) { 23 obj[key] = val; 24 return; 25 } 26 ob.convert(key, val); 27 ob.dep.notify(); 28 if (ob.vms) { 29 var i = ob.vms.length; 30 while (i--) { 31 var vm = ob.vms[i]; 32 vm._proxy(key); 33 vm._digest(); 34 } 35 } 36 return val; 37} 38 39/** 40 * Delete a property and trigger change if necessary. 41 * 42 * @param {Object} obj 43 * @param {String} key 44 */ 45 46function del(obj, key) { 47 if (!hasOwn(obj, key)) { 48 return; 49 } 50 delete obj[key]; 51 var ob = obj.__ob__; 52 if (!ob) { 53 if (obj._isVue) { 54 delete obj._data[key]; 55 obj._digest(); 56 } 57 return; 58 } 59 ob.dep.notify(); 60 if (ob.vms) { 61 var i = ob.vms.length; 62 while (i--) { 63 var vm = ob.vms[i]; 64 vm._unproxy(key); 65 vm._digest(); 66 } 67 } 68} 69 70var hasOwnProperty = Object.prototype.hasOwnProperty; 71/** 72 * Check whether the object has the property. 73 * 74 * @param {Object} obj 75 * @param {String} key 76 * @return {Boolean} 77 */ 78 79function hasOwn(obj, key) { 80 return hasOwnProperty.call(obj, key); 81} 82 83/** 84 * Check if an expression is a literal value. 85 * 86 * @param {String} exp 87 * @return {Boolean} 88 */ 89 90var literalValueRE = /^\s?(true|false|-?[\d\.]+|'[^']*'|"[^"]*")\s?$/; 91 92function isLiteral(exp) { 93 return literalValueRE.test(exp); 94} 95 96/** 97 * Check if a string starts with $ or _ 98 * 99 * @param {String} str 100 * @return {Boolean} 101 */ 102 103function isReserved(str) { 104 var c = (str + '').charCodeAt(0); 105 return c === 0x24 || c === 0x5F; 106} 107 108/** 109 * Guard text output, make sure undefined outputs 110 * empty string 111 * 112 * @param {*} value 113 * @return {String} 114 */ 115 116function _toString(value) { 117 return value == null ? '' : value.toString(); 118} 119 120/** 121 * Check and convert possible numeric strings to numbers 122 * before setting back to data 123 * 124 * @param {*} value 125 * @return {*|Number} 126 */ 127 128function toNumber(value) { 129 if (typeof value !== 'string') { 130 return value; 131 } else { 132 var parsed = Number(value); 133 return isNaN(parsed) ? value : parsed; 134 } 135} 136 137/** 138 * Convert string boolean literals into real booleans. 139 * 140 * @param {*} value 141 * @return {*|Boolean} 142 */ 143 144function toBoolean(value) { 145 return value === 'true' ? true : value === 'false' ? false : value; 146} 147 148/** 149 * Strip quotes from a string 150 * 151 * @param {String} str 152 * @return {String | false} 153 */ 154 155function stripQuotes(str) { 156 var a = str.charCodeAt(0); 157 var b = str.charCodeAt(str.length - 1); 158 return a === b && (a === 0x22 || a === 0x27) ? str.slice(1, -1) : str; 159} 160 161/** 162 * Camelize a hyphen-delimited string. 163 * 164 * @param {String} str 165 * @return {String} 166 */ 167 168var camelizeRE = /-(\w)/g; 169 170function camelize(str) { 171 return str.replace(camelizeRE, toUpper); 172} 173 174function toUpper(_, c) { 175 return c ? c.toUpperCase() : ''; 176} 177 178/** 179 * Hyphenate a camelCase string. 180 * 181 * @param {String} str 182 * @return {String} 183 */ 184 185var hyphenateRE = /([^-])([A-Z])/g; 186 187function hyphenate(str) { 188 return str.replace(hyphenateRE, '$1-$2').replace(hyphenateRE, '$1-$2').toLowerCase(); 189} 190 191/** 192 * Converts hyphen/underscore/slash delimitered names into 193 * camelized classNames. 194 * 195 * e.g. my-component => MyComponent 196 * some_else => SomeElse 197 * some/comp => SomeComp 198 * 199 * @param {String} str 200 * @return {String} 201 */ 202 203var classifyRE = /(?:^|[-_\/])(\w)/g; 204 205function classify(str) { 206 return str.replace(classifyRE, toUpper); 207} 208 209/** 210 * Simple bind, faster than native 211 * 212 * @param {Function} fn 213 * @param {Object} ctx 214 * @return {Function} 215 */ 216 217function bind(fn, ctx) { 218 return function (a) { 219 var l = arguments.length; 220 return l ? l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a) : fn.call(ctx); 221 }; 222} 223 224/** 225 * Convert an Array-like object to a real Array. 226 * 227 * @param {Array-like} list 228 * @param {Number} [start] - start index 229 * @return {Array} 230 */ 231 232function toArray(list, start) { 233 start = start || 0; 234 var i = list.length - start; 235 var ret = new Array(i); 236 while (i--) { 237 ret[i] = list[i + start]; 238 } 239 return ret; 240} 241 242/** 243 * Mix properties into target object. 244 * 245 * @param {Object} to 246 * @param {Object} from 247 */ 248 249function extend(to, from) { 250 var keys = Object.keys(from); 251 var i = keys.length; 252 while (i--) { 253 to[keys[i]] = from[keys[i]]; 254 } 255 return to; 256} 257 258/** 259 * Quick object check - this is primarily used to tell 260 * Objects from primitive values when we know the value 261 * is a JSON-compliant type. 262 * 263 * @param {*} obj 264 * @return {Boolean} 265 */ 266 267function isObject(obj) { 268 return obj !== null && typeof obj === 'object'; 269} 270 271/** 272 * Strict object type check. Only returns true 273 * for plain JavaScript objects. 274 * 275 * @param {*} obj 276 * @return {Boolean} 277 */ 278 279var toString = Object.prototype.toString; 280var OBJECT_STRING = '[object Object]'; 281 282function isPlainObject(obj) { 283 return toString.call(obj) === OBJECT_STRING; 284} 285 286/** 287 * Array type check. 288 * 289 * @param {*} obj 290 * @return {Boolean} 291 */ 292 293var isArray = Array.isArray; 294 295/** 296 * Define a property. 297 * 298 * @param {Object} obj 299 * @param {String} key 300 * @param {*} val 301 * @param {Boolean} [enumerable] 302 */ 303 304function def(obj, key, val, enumerable) { 305 Object.defineProperty(obj, key, { 306 value: val, 307 enumerable: !!enumerable, 308 writable: true, 309 configurable: true 310 }); 311} 312 313/** 314 * Debounce a function so it only gets called after the 315 * input stops arriving after the given wait period. 316 * 317 * @param {Function} func 318 * @param {Number} wait 319 * @return {Function} - the debounced function 320 */ 321 322function _debounce(func, wait) { 323 var timeout, args, context, timestamp, result; 324 var later = function later() { 325 var last = Date.now() - timestamp; 326 if (last < wait && last >= 0) { 327 timeout = setTimeout(later, wait - last); 328 } else { 329 timeout = null;
330 result = func.apply(context, args); 331 if (!timeout) context = args = null; 332 } 333 }; 334 return function () { 335 context = this; 336 args = arguments; 337 timestamp = Date.now(); 338 if (!timeout) { 339 timeout = setTimeout(later, wait); 340 } 341 return result; 342 }; 343} 344 345/** 346 * Manual indexOf because it's slightly faster than 347 * native. 348 * 349 * @param {Array} arr 350 * @param {*} obj 351 */ 352 353function indexOf(arr, obj) { 354 var i = arr.length; 355 while (i--) { 356 if (arr[i] === obj) return i; 357 } 358 return -1; 359} 360 361/** 362 * Make a cancellable version of an async callback. 363 * 364 * @param {Function} fn 365 * @return {Function} 366 */ 367 368function cancellable(fn) { 369 var cb = function cb() { 370 if (!cb.cancelled) { 371 return fn.apply(this, arguments); 372 } 373 }; 374 cb.cancel = function () { 375 cb.cancelled = true; 376 }; 377 return cb; 378} 379 380/** 381 * Check if two values are loosely equal - that is, 382 * if they are plain objects, do they have the same shape? 383 * 384 * @param {*} a 385 * @param {*} b 386 * @return {Boolean} 387 */ 388 389function looseEqual(a, b) { 390 /* eslint-disable eqeqeq */ 391 return a == b || (isObject(a) && isObject(b) ? JSON.stringify(a) === JSON.stringify(b) : false); 392 /* eslint-enable eqeqeq */ 393} 394 395var hasProto = ('__proto__' in {}); 396 397// Browser environment sniffing 398var inBrowser = typeof window !== 'undefined' && Object.prototype.toString.call(window) !== '[object Object]'; 399 400// detect devtools 401var devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__; 402 403// UA sniffing for working around browser-specific quirks 404var UA = inBrowser && window.navigator.userAgent.toLowerCase(); 405var isIE = UA && UA.indexOf('trident') > 0; 406var isIE9 = UA && UA.indexOf('msie 9.0') > 0; 407var isAndroid = UA && UA.indexOf('android') > 0; 408var isIOS = UA && /iphone|ipad|ipod|ios/.test(UA); 409 410var transitionProp = undefined; 411var transitionEndEvent = undefined; 412var animationProp = undefined; 413var animationEndEvent = undefined; 414 415// Transition property/event sniffing 416if (inBrowser && !isIE9) { 417 var isWebkitTrans = window.ontransitionend === undefined && window.onwebkittransitionend !== undefined; 418 var isWebkitAnim = window.onanimationend === undefined && window.onwebkitanimationend !== undefined; 419 transitionProp = isWebkitTrans ? 'WebkitTransition' : 'transition'; 420 transitionEndEvent = isWebkitTrans ? 'webkitTransitionEnd' : 'transitionend'; 421 animationProp = isWebkitAnim ? 'WebkitAnimation' : 'animation'; 422 animationEndEvent = isWebkitAnim ? 'webkitAnimationEnd' : 'animationend'; 423} 424 425/* istanbul ignore next */ 426function isNative(Ctor) { 427 return (/native code/.test(Ctor.toString()) 428 ); 429} 430 431/** 432 * Defer a task to execute it asynchronously. Ideally this 433 * should be executed as a microtask, so we leverage 434 * MutationObserver if it's available, and fallback to 435 * setTimeout(0). 436 * 437 * @param {Function} cb 438 * @param {Object} ctx 439 */ 440 441var nextTick = (function () { 442 var callbacks = []; 443 var pending = false; 444 var timerFunc = undefined; 445 446 function nextTickHandler() { 447 pending = false; 448 var copies = callbacks.slice(0); 449 callbacks.length = 0; 450 for (var i = 0; i < copies.length; i++) { 451 copies[i](); 452 } 453 } 454 455 // the nextTick behavior leverages the microtask queue, which can be accessed 456 // via either native Promise.then or MutationObserver. 457 // MutationObserver has wider support, however it is seriously bugged in 458 // UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It 459 // completely stops working after triggering a few times... so, if native 460 // Promise is available, we will use it: 461 /* istanbul ignore if */ 462 if (typeof Promise !== 'undefined' && isNative(Promise)) { 463 var p = Promise.resolve(); 464 var noop = function noop() {}; 465 timerFunc = function () { 466 p.then(nextTickHandler); 467 // in problematic UIWebViews, Promise.then doesn't completely break, but 468 // it can get stuck in a weird state where callbacks are pushed into the 469 // microtask queue but the queue isn't being flushed, until the browser 470 // needs to do some other work, e.g. handle a timer. Therefore we can 471 // "force" the microtask queue to be flushed by adding an empty timer. 472 if (isIOS) setTimeout(noop); 473 }; 474 } else if (typeof MutationObserver !== 'undefined') { 475 // use MutationObserver where native Promise is not available, 476 // e.g. IE11, iOS7, Android 4.4 477 var counter = 1; 478 var observer = new MutationObserver(nextTickHandler); 479 var textNode = document.createTextNode(String(counter)); 480 observer.observe(textNode, { 481 characterData: true 482 });
483 timerFunc = function () { 484 counter = (counter + 1) % 2; 485 textNode.data = String(counter); 486 }; 487 } else { 488 // fallback to setTimeout 489 /* istanbul ignore next */ 490 timerFunc = setTimeout; 491 } 492 493 return function (cb, ctx) { 494 var func = ctx ? function () { 495 cb.call(ctx); 496 } : cb; 497 callbacks.push(func); 498 if (pending) return; 499 pending = true; 500 timerFunc(nextTickHandler, 0); 501 }; 502})(); 503 504var _Set = undefined; 505/* istanbul ignore if */ 506if (typeof Set !== 'undefined' && isNative(Set)) { 507 // use native Set when available. 508 _Set = Set; 509} else { 510 // a non-standard Set polyfill that only works with primitive keys. 511 _Set = function () { 512 this.set = Object.create(null); 513 }; 514 _Set.prototype.has = function (key) { 515 return this.set[key] !== undefined; 516 }; 517 _Set.prototype.add = function (key) { 518 this.set[key] = 1; 519 }; 520 _Set.prototype.clear = function () { 521 this.set = Object.create(null); 522 }; 523} 524 525function Cache(limit) { 526 this.size = 0; 527 this.limit = limit; 528 this.head = this.tail = undefined; 529 this._keymap = Object.create(null); 530} 531 532var p = Cache.prototype; 533 534/** 535 * Put <value> into the cache associated with <key>. 536 * Returns the entry which was removed to make room for 537 * the new entry. Otherwise undefined is returned. 538 * (i.e. if there was enough room already). 539 * 540 * @param {String} key 541 * @param {*} value 542 * @return {Entry|undefined} 543 */ 544 545p.put = function (key, value) { 546 var removed; 547 548 var entry = this.get(key, true); 549 if (!entry) { 550 if (this.size === this.limit) { 551 removed = this.shift(); 552 } 553 entry = { 554 key: key 555 }; 556 this._keymap[key] = entry; 557 if (this.tail) { 558 this.tail.newer = entry; 559 entry.older = this.tail; 560 } else { 561 this.head = entry; 562 } 563 this.tail = entry; 564 this.size++; 565 } 566 entry.value = value; 567 568 return removed; 569}; 570 571/** 572 * Purge the least recently used (oldest) entry from the 573 * cache. Returns the removed entry or undefined if the 574 * cache was empty. 575 */ 576 577p.shift = function () { 578 var entry = this.head; 579 if (entry) { 580 this.head = this.head.newer; 581 this.head.older = undefined; 582 entry.newer = entry.older = undefined; 583 this._keymap[entry.key] = undefined; 584 this.size--; 585 } 586 return entry; 587}; 588 589/** 590 * Get and register recent use of <key>. Returns the value 591 * associated with <key> or undefined if not in cache. 592 * 593 * @param {String} key 594 * @param {Boolean} returnEntry 595 * @return {Entry|*} 596 */ 597 598p.get = function (key, returnEntry) { 599 var entry = this._keymap[key]; 600 if (entry === undefined) return; 601 if (entry === this.tail) { 602 return returnEntry ? entry : entry.value; 603 } 604 // HEAD--------------TAIL 605 // <.older .newer> 606 // <--- add direction -- 607 // A B C <D> E 608 if (entry.newer) { 609 if (entry === this.head) { 610 this.head = entry.newer; 611 } 612 entry.newer.older = entry.older; // C <-- E. 613 } 614 if (entry.older) { 615 entry.older.newer = entry.newer; // C. --> E 616 } 617 entry.newer = undefined; // D --x 618 entry.older = this.tail; // D. --> E 619 if (this.tail) { 620 this.tail.newer = entry; // E. <-- D 621 } 622 this.tail = entry; 623 return returnEntry ? entry : entry.value; 624}; 625 626var cache$1 = new Cache(1000); 627var reservedArgRE = /^in$|^-?\d+/; 628 629/** 630 * Parser state 631 */ 632 633var str; 634var dir; 635var len; 636var index; 637var chr; 638var state; 639var startState = 0; 640var filterState = 1; 641var filterNameState = 2; 642var filterArgState = 3; 643 644var doubleChr = 0x22; 645var singleChr = 0x27; 646var pipeChr = 0x7C; 647var escapeChr = 0x5C; 648var spaceChr = 0x20; 649 650var expStartChr = { 0x5B: 1, 0x7B: 1, 0x28: 1 }; 651var expChrPair = { 0x5B: 0x5D, 0x7B: 0x7D, 0x28: 0x29 }; 652 653function peek() { 654 return str.charCodeAt(index + 1); 655} 656 657function next() { 658 return str.charCodeAt(++index); 659} 660 661function eof() { 662 return index >= len; 663} 664 665function eatSpace() { 666 while (peek() === spaceChr) { 667 next(); 668 } 669} 670 671function isStringStart(chr) { 672 return chr === doubleChr || chr === singleChr; 673} 674 675function isExpStart(chr) { 676 return expStartChr[chr]; 677} 678 679function isExpEnd(start, chr) { 680 return expChrPair[start] === chr; 681} 682 683function parseString() { 684 var stringQuote = next(); 685 var chr; 686 while (!eof()) { 687 chr = next(); 688 // escape char 689 if (chr === escapeChr) { 690 next(); 691 } else if (chr === stringQuote) { 692 break; 693 } 694 } 695} 696 697function parseSpecialExp(chr) { 698 var inExp = 0; 699 var startChr = chr; 700 701 while (!eof()) { 702 chr = peek(); 703 if (isStringStart(chr)) { 704 parseString(); 705 continue; 706 } 707 708 if (startChr === chr) { 709 inExp++; 710 } 711 if (isExpEnd(startChr, chr)) { 712 inExp--; 713 } 714 715 next(); 716 717 if (inExp === 0) { 718 break; 719 } 720 } 721} 722 723/** 724 * syntax: 725 * expression | filterName [arg arg [| filterName arg arg]] 726 */ 727 728function parseExpression() { 729 var start = index; 730 while (!eof()) { 731 chr = peek(); 732 if (isStringStart(chr)) { 733 parseString(); 734 } else if (isExpStart(chr)) { 735 parseSpecialExp(chr); 736 } else if (chr === pipeChr) { 737 next(); 738 chr = peek(); 739 if (chr === pipeChr) { 740 next(); 741 } else { 742 if (state === startState || state === filterArgState) { 743 state = filterState; 744 } 745 break; 746 } 747 } else if (chr === spaceChr && (state === filterNameState || state === filterArgState)) { 748 eatSpace(); 749 break; 750 } else { 751 if (state === filterState) { 752 state = filterNameState; 753 } 754 next(); 755 } 756 } 757 758 return str.slice(start + 1, index) || null; 759} 760 761function parseFilterList() { 762 var filters = []; 763 while (!eof()) { 764 filters.push(parseFilter()); 765 } 766 return filters; 767} 768 769function parseFilter() { 770 var filter = {}; 771 var args; 772 773 state = filterState; 774 filter.name = parseExpression().trim(); 775 776 state = filterArgState; 777 args = parseFilterArguments(); 778 779 if (args.length) { 780 filter.args = args; 781 } 782 return filter; 783} 784 785function parseFilterArguments() { 786 var args = []; 787 while (!eof() && state !== filterState) { 788 var arg = parseExpression(); 789 if (!arg) { 790 break; 791 } 792 args.push(processFilterArg(arg)); 793 } 794 795 return args; 796} 797 798/** 799 * Check if an argument is dynamic and strip quotes. 800 * 801 * @param {String} arg 802 * @return {Object} 803 */ 804
805function processFilterArg(arg) { 806 if (reservedArgRE.test(arg)) { 807 return { 808 value: toNumber(arg), 809 dynamic: false 810 }; 811 } else { 812 var stripped = stripQuotes(arg); 813 var dynamic = stripped === arg; 814 return { 815 value: dynamic ? arg : stripped, 816 dynamic: dynamic 817 }; 818 } 819} 820 821/** 822 * Parse a directive value and extract the expression 823 * and its filters into a descriptor. 824 * 825 * Example: 826 * 827 * "a + 1 | uppercase" will yield: 828 * { 829 * expression: 'a + 1', 830 * filters: [ 831 * { name: 'uppercase', args: null } 832 * ] 833 * } 834 * 835 * @param {String} s 836 * @return {Object} 837 */ 838 839function parseDirective(s) { 840 var hit = cache$1.get(s); 841 if (hit) { 842 return hit; 843 } 844 845 // reset parser state 846 str = s; 847 dir = {}; 848 len = str.length; 849 index = -1; 850 chr = ''; 851 state = startState; 852 853 var filters; 854 855 if (str.indexOf('|') < 0) { 856 dir.expression = str.trim(); 857 } else { 858 dir.expression = parseExpression().trim(); 859 filters = parseFilterList(); 860 if (filters.length) { 861 dir.filters = filters; 862 } 863 } 864 865 cache$1.put(s, dir); 866 return dir; 867} 868 869var directive = Object.freeze({ 870 parseDirective: parseDirective 871}); 872 873var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g; 874var cache = undefined; 875var tagRE = undefined; 876var htmlRE = undefined; 877/** 878 * Escape a string so it can be used in a RegExp 879 * constructor. 880 * 881 * @param {String} str 882 */ 883 884function escapeRegex(str) { 885 return str.replace(regexEscapeRE, '\\$&'); 886} 887 888function compileRegex() { 889 var open = escapeRegex(config.delimiters[0]); 890 var close = escapeRegex(config.delimiters[1]); 891 var unsafeOpen = escapeRegex(config.unsafeDelimiters[0]); 892 var unsafeClose = escapeRegex(config.unsafeDelimiters[1]); 893 tagRE = new RegExp(unsafeOpen + '((?:.|\\n)+?)' + unsafeClose + '|' + open + '((?:.|\\n)+?)' + close, 'g'); 894 htmlRE = new RegExp('^' + unsafeOpen + '((?:.|\\n)+?)' + unsafeClose + '$'); 895 // reset cache 896 cache = new Cache(1000); 897} 898 899/** 900 * Parse a template text string into an array of tokens. 901 * 902 * @param {String} text 903 * @return {Array<Object> | null} 904 * - {String} type 905 * - {String} value 906 * - {Boolean} [html] 907 * - {Boolean} [oneTime] 908 */ 909 910function parseText(text) { 911 if (!cache) { 912 compileRegex(); 913 } 914 var hit = cache.get(text); 915 if (hit) { 916 return hit; 917 } 918 if (!tagRE.test(text)) { 919 return null; 920 } 921 var tokens = []; 922 var lastIndex = tagRE.lastIndex = 0; 923 var match, index, html, value, first, oneTime; 924 /* eslint-disable no-cond-assign */ 925 while (match = tagRE.exec(text)) { 926 /* eslint-enable no-cond-assign */ 927 index = match.index; 928 // push text token 929 if (index > lastIndex) { 930 tokens.push({ 931 value: text.slice(lastIndex, index) 932 }); 933 } 934 // tag token 935 html = htmlRE.test(match[0]); 936 value = html ? match[1] : match[2]; 937 first = value.charCodeAt(0); 938 oneTime = first === 42; // * 939 value = oneTime ? value.slice(1) : value; 940 tokens.push({ 941 tag: true, 942 value: value.trim(), 943 html: html, 944 oneTime: oneTime 945 }); 946 lastIndex = index + match[0].length; 947 } 948 if (lastIndex < text.length) { 949 tokens.push({ 950 value: text.slice(lastIndex) 951 }); 952 } 953 cache.put(text, tokens); 954 return tokens; 955} 956 957/** 958 * Format a list of tokens into an expression. 959 * e.g. tokens parsed from 'a {{b}} c' can be serialized 960 * into one single expression as '"a " + b + " c"'. 961 * 962 * @param {Array} tokens 963 * @param {Vue} [vm] 964 * @return {String} 965 */ 966 967function tokensToExp(tokens, vm) { 968 if (tokens.length > 1) { 969 return tokens.map(function (token) { 970 return formatToken(token, vm); 971 }).join('+'); 972 } else { 973 return formatToken(tokens[0], vm, true); 974 } 975} 976 977/** 978 * Format a single token. 979 * 980 * @param {Object} token 981 * @param {Vue} [vm] 982 * @param {Boolean} [single] 983 * @return {String} 984 */ 985 986function formatToken(token, vm, single) { 987 return token.tag ? token.oneTime && vm ? '"' + vm.$eval(token.value) + '"' : inlineFilters(token.value, single) : '"' + token.value + '"'; 988} 989 990/** 991 * For an attribute with multiple interpolation tags, 992 * e.g. attr="some-{{thing | filter}}", in order to combine 993 * the whole thing into a single watchable expression, we 994 * have to inline those filters. This function does exactly 995 * that. This is a bit hacky but it avoids heavy changes 996 * to directive parser and watcher mechanism. 997 * 998 * @param {String} exp 999 * @param {Boolean} single 1000 * @return {String} 1001 */ 1002 1003var filterRE = /[^|]\|[^|]/; 1004function inlineFilters(exp, single) { 1005 if (!filterRE.test(exp)) { 1006 return single ? exp : '(' + exp + ')'; 1007 } else { 1008 var dir = parseDirective(exp); 1009 if (!dir.filters) { 1010 return '(' + exp + ')'; 1011 } else { 1012 return 'this._applyFilters(' + dir.expression + // value 1013 ',null,' + // oldValue (null for read) 1014 JSON.stringify(dir.filters) + // filter descriptors 1015 ',false)'; // write? 1016 } 1017 } 1018} 1019 1020var text = Object.freeze({ 1021 compileRegex: compileRegex, 1022 parseText: parseText, 1023 tokensToExp: tokensToExp 1024}); 1025 1026var delimiters = ['{{', '}}']; 1027var unsafeDelimiters = ['{{{', '}}}']; 1028
1029var config = Object.defineProperties({ 1030 1031 /** 1032 * Whether to print debug messages. 1033 * Also enables stack trace for warnings. 1034 * 1035 * @type {Boolean} 1036 */ 1037 1038 debug: false, 1039 1040 /** 1041 * Whether to suppress warnings. 1042 * 1043 * @type {Boolean} 1044 */ 1045 1046 silent: false, 1047 1048 /** 1049 * Whether to use async rendering. 1050 */ 1051 1052 async: true, 1053 1054 /** 1055 * Whether to warn against errors caught when evaluating 1056 * expressions. 1057 */ 1058 1059 warnExpressionErrors: true, 1060 1061 /** 1062 * Whether to allow devtools inspection. 1063 * Disabled by default in production builds. 1064 */ 1065 1066 devtools: 'development' !== 'production', 1067 1068 /** 1069 * Internal flag to indicate the delimiters have been 1070 * changed. 1071 * 1072 * @type {Boolean} 1073 */ 1074 1075 _delimitersChanged: true, 1076 1077 /** 1078 * List of asset types that a component can own. 1079 * 1080 * @type {Array} 1081 */ 1082 1083 _assetTypes: ['component', 'directive', 'elementDirective', 'filter', 'transition', 'partial'], 1084 1085 /** 1086 * prop binding modes 1087 */ 1088 1089 _propBindingModes: { 1090 ONE_WAY: 0, 1091 TWO_WAY: 1, 1092 ONE_TIME: 2 1093 }, 1094 1095 /** 1096 * Max circular updates allowed in a batcher flush cycle. 1097 */ 1098 1099 _maxUpdateCount: 100 1100 1101}, { 1102 delimiters: { /** 1103 * Interpolation delimiters. Changing these would trigger 1104 * the text parser to re-compile the regular expressions. 1105 * 1106 * @type {Array<String>} 1107 */ 1108 1109 get: function get() { 1110 return delimiters; 1111 }, 1112 set: function set(val) { 1113 delimiters = val; 1114 compileRegex(); 1115 }, 1116 configurable: true, 1117 enumerable: true 1118 }, 1119 unsafeDelimiters: { 1120 get: function get() { 1121 return unsafeDelimiters; 1122 }, 1123 set: function set(val) { 1124 unsafeDelimiters = val; 1125 compileRegex(); 1126 }, 1127 configurable: true, 1128 enumerable: true 1129 } 1130}); 1131 1132var warn = undefined; 1133var formatComponentName = undefined; 1134 1135if ('development' !== 'production') { 1136 (function () { 1137 var hasConsole = typeof console !== 'undefined'; 1138 1139 warn = function (msg, vm) { 1140 if (hasConsole && !config.silent) { 1141 console.error('[Vue warn]: ' + msg + (vm ? formatComponentName(vm) : '')); 1142 } 1143 }; 1144 1145 formatComponentName = function (vm) { 1146 var name = vm._isVue ? vm.$options.name : vm.name; 1147 return name ? ' (found in component: <' + hyphenate(name) + '>)' : ''; 1148 }; 1149 })(); 1150} 1151 1152/** 1153 * Append with transition. 1154 * 1155 * @param {Element} el 1156 * @param {Element} target 1157 * @param {Vue} vm 1158 * @param {Function} [cb] 1159 */ 1160 1161function appendWithTransition(el, target, vm, cb) { 1162 applyTransition(el, 1, function () { 1163 target.appendChild(el); 1164 }, vm, cb); 1165} 1166 1167/** 1168 * InsertBefore with transition. 1169 * 1170 * @param {Element} el 1171 * @param {Element} target 1172 * @param {Vue} vm 1173 * @param {Function} [cb] 1174 */ 1175 1176function beforeWithTransition(el, target, vm, cb) { 1177 applyTransition(el, 1, function () { 1178 before(el, target); 1179 }, vm, cb); 1180} 1181 1182/** 1183 * Remove with transition. 1184 * 1185 * @param {Element} el 1186 * @param {Vue} vm 1187 * @param {Function} [cb] 1188 */ 1189 1190function removeWithTransition(el, vm, cb) { 1191 applyTransition(el, -1, function () { 1192 remove(el); 1193 }, vm, cb); 1194} 1195 1196/** 1197 * Apply transitions with an operation callback. 1198 * 1199 * @param {Element} el 1200 * @param {Number} direction 1201 * 1: enter 1202 * -1: leave 1203 * @param {Function} op - the actual DOM operation 1204 * @param {Vue} vm 1205 * @param {Function} [cb] 1206 */ 1207 1208function applyTransition(el, direction, op, vm, cb) { 1209 var transition = el.__v_trans; 1210 if (!transition || 1211 // skip if there are no js hooks and CSS transition is 1212 // not supported 1213 !transition.hooks && !transitionEndEvent || 1214 // skip transitions for initial compile 1215 !vm._isCompiled || 1216 // if the vm is being manipulated by a parent directive 1217 // during the parent's compilation phase, skip the 1218 // animation. 1219 vm.$parent && !vm.$parent._isCompiled) { 1220 op(); 1221 if (cb) cb(); 1222 return; 1223 } 1224 var action = direction > 0 ? 'enter' : 'leave'; 1225 transition[action](op, cb); 1226} 1227 1228var transition = Object.freeze({ 1229 appendWithTransition: appendWithTransition, 1230 beforeWithTransition: beforeWithTransition, 1231 removeWithTransition: removeWithTransition, 1232 applyTransition: applyTransition 1233}); 1234 1235/** 1236 * Query an element selector if it's not an element already. 1237 * 1238 * @param {String|Element} el 1239 * @return {Element} 1240 */ 1241 1242function query(el) { 1243 if (typeof el === 'string') { 1244 var selector = el; 1245 el = document.querySelector(el); 1246 if (!el) { 1247 'development' !== 'production' && warn('Cannot find element: ' + selector); 1248 } 1249 } 1250 return el; 1251} 1252 1253/** 1254 * Check if a node is in the document. 1255 * Note: document.documentElement.contains should work here 1256 * but always returns false for comment nodes in phantomjs, 1257 * making unit tests difficult. This is fixed by doing the 1258 * contains() check on the node's parentNode instead of 1259 * the node itself. 1260 * 1261 * @param {Node} node 1262 * @return {Boolean} 1263 */ 1264 1265function inDoc(node) { 1266 if (!node) return false;
1267 var doc = node.ownerDocument.documentElement; 1268 var parent = node.parentNode; 1269 return doc === node || doc === parent || !!(parent && parent.nodeType === 1 && doc.contains(parent)); 1270} 1271 1272/** 1273 * Get and remove an attribute from a node. 1274 * 1275 * @param {Node} node 1276 * @param {String} _attr 1277 */ 1278 1279function getAttr(node, _attr) { 1280 var val = node.getAttribute(_attr); 1281 if (val !== null) { 1282 node.removeAttribute(_attr); 1283 } 1284 return val; 1285} 1286 1287/** 1288 * Get an attribute with colon or v-bind: prefix. 1289 * 1290 * @param {Node} node 1291 * @param {String} name 1292 * @return {String|null} 1293 */ 1294 1295function getBindAttr(node, name) { 1296 var val = getAttr(node, ':' + name); 1297 if (val === null) { 1298 val = getAttr(node, 'v-bind:' + name); 1299 } 1300 return val; 1301} 1302 1303/** 1304 * Check the presence of a bind attribute. 1305 * 1306 * @param {Node} node 1307 * @param {String} name 1308 * @return {Boolean} 1309 */ 1310 1311function hasBindAttr(node, name) { 1312 return node.hasAttribute(name) || node.hasAttribute(':' + name) || node.hasAttribute('v-bind:' + name); 1313} 1314 1315/** 1316 * Insert el before target 1317 * 1318 * @param {Element} el 1319 * @param {Element} target 1320 */ 1321 1322function before(el, target) { 1323 target.parentNode.insertBefore(el, target); 1324} 1325 1326/** 1327 * Insert el after target 1328 * 1329 * @param {Element} el 1330 * @param {Element} target 1331 */ 1332 1333function after(el, target) { 1334 if (target.nextSibling) { 1335 before(el, target.nextSibling); 1336 } else { 1337 target.parentNode.appendChild(el); 1338 } 1339} 1340 1341/** 1342 * Remove el from DOM 1343 * 1344 * @param {Element} el 1345 */ 1346 1347function remove(el) { 1348 el.parentNode.removeChild(el); 1349} 1350 1351/** 1352 * Prepend el to target 1353 * 1354 * @param {Element} el 1355 * @param {Element} target 1356 */ 1357 1358function prepend(el, target) { 1359 if (target.firstChild) { 1360 before(el, target.firstChild); 1361 } else { 1362 target.appendChild(el); 1363 } 1364} 1365 1366/** 1367 * Replace target with el 1368 * 1369 * @param {Element} target 1370 * @param {Element} el 1371 */ 1372 1373function replace(target, el) { 1374 var parent = target.parentNode; 1375 if (parent) { 1376 parent.replaceChild(el, target); 1377 } 1378} 1379 1380/** 1381 * Add event listener shorthand. 1382 * 1383 * @param {Element} el 1384 * @param {String} event 1385 * @param {Function} cb 1386 * @param {Boolean} [useCapture] 1387 */ 1388 1389function on(el, event, cb, useCapture) { 1390 el.addEventListener(event, cb, useCapture); 1391} 1392 1393/** 1394 * Remove event listener shorthand. 1395 * 1396 * @param {Element} el 1397 * @param {String} event 1398 * @param {Function} cb 1399 */ 1400 1401function off(el, event, cb) { 1402 el.removeEventListener(event, cb); 1403} 1404 1405/** 1406 * For IE9 compat: when both class and :class are present 1407 * getAttribute('class') returns wrong value... 1408 * 1409 * @param {Element} el 1410 * @return {String} 1411 */ 1412 1413function getClass(el) { 1414 var classname = el.className; 1415 if (typeof classname === 'object') { 1416 classname = classname.baseVal || ''; 1417 } 1418 return classname; 1419} 1420 1421/** 1422 * In IE9, setAttribute('class') will result in empty class 1423 * if the element also has the :class attribute; However in 1424 * PhantomJS, setting `className` does not work on SVG elements... 1425 * So we have to do a conditional check here. 1426 * 1427 * @param {Element} el 1428 * @param {String} cls 1429 */ 1430 1431function setClass(el, cls) { 1432 /* istanbul ignore if */ 1433 if (isIE9 && !/svg$/.test(el.namespaceURI)) { 1434 el.className = cls; 1435 } else { 1436 el.setAttribute('class', cls); 1437 } 1438} 1439 1440/** 1441 * Add class with compatibility for IE & SVG 1442 * 1443 * @param {Element} el 1444 * @param {String} cls 1445 */ 1446 1447function addClass(el, cls) { 1448 if (el.classList) { 1449 el.classList.add(cls); 1450 } else { 1451 var cur = ' ' + getClass(el) + ' '; 1452 if (cur.indexOf(' ' + cls + ' ') < 0) { 1453 setClass(el, (cur + cls).trim()); 1454 } 1455 } 1456} 1457 1458/** 1459 * Remove class with compatibility for IE & SVG 1460 * 1461 * @param {Element} el 1462 * @param {String} cls 1463 */ 1464 1465function removeClass(el, cls) { 1466 if (el.classList) { 1467 el.classList.remove(cls); 1468 } else { 1469 var cur = ' ' + getClass(el) + ' '; 1470 var tar = ' ' + cls + ' '; 1471 while (cur.indexOf(tar) >= 0) { 1472 cur = cur.replace(tar, ' '); 1473 } 1474 setClass(el, cur.trim()); 1475 } 1476 if (!el.className) { 1477 el.removeAttribute('class'); 1478 } 1479} 1480 1481/** 1482 * Extract raw content inside an element into a temporary 1483 * container div 1484 * 1485 * @param {Element} el 1486 * @param {Boolean} asFragment 1487 * @return {Element|DocumentFragment} 1488 */ 1489 1490function extractContent(el, asFragment) { 1491 var child; 1492 var rawContent; 1493 /* istanbul ignore if */ 1494 if (isTemplate(el) && isFragment(el.content)) { 1495 el = el.content; 1496 } 1497 if (el.hasChildNodes()) { 1498 trimNode(el); 1499 rawContent = asFragment ? document.createDocumentFragment() : document.createElement('div'); 1500 /* eslint-disable no-cond-assign */ 1501 while (child = el.firstChild) { 1502 /* eslint-enable no-cond-assign */ 1503 rawContent.appendChild(child); 1504 } 1505 } 1506 return rawContent; 1507} 1508
1509/** 1510 * Trim possible empty head/tail text and comment 1511 * nodes inside a parent. 1512 * 1513 * @param {Node} node 1514 */ 1515 1516function trimNode(node) { 1517 var child; 1518 /* eslint-disable no-sequences */ 1519 while ((child = node.firstChild, isTrimmable(child))) { 1520 node.removeChild(child); 1521 } 1522 while ((child = node.lastChild, isTrimmable(child))) { 1523 node.removeChild(child); 1524 } 1525 /* eslint-enable no-sequences */ 1526} 1527 1528function isTrimmable(node) { 1529 return node && (node.nodeType === 3 && !node.data.trim() || node.nodeType === 8); 1530} 1531 1532/** 1533 * Check if an element is a template tag. 1534 * Note if the template appears inside an SVG its tagName 1535 * will be in lowercase. 1536 * 1537 * @param {Element} el 1538 */ 1539 1540function isTemplate(el) { 1541 return el.tagName && el.tagName.toLowerCase() === 'template'; 1542} 1543 1544/** 1545 * Create an "anchor" for performing dom insertion/removals. 1546 * This is used in a number of scenarios: 1547 * - fragment instance 1548 * - v-html 1549 * - v-if 1550 * - v-for 1551 * - component 1552 * 1553 * @param {String} content 1554 * @param {Boolean} persist - IE trashes empty textNodes on 1555 * cloneNode(true), so in certain 1556 * cases the anchor needs to be 1557 * non-empty to be persisted in 1558 * templates. 1559 * @return {Comment|Text} 1560 */ 1561 1562function createAnchor(content, persist) { 1563 var anchor = config.debug ? document.createComment(content) : document.createTextNode(persist ? ' ' : ''); 1564 anchor.__v_anchor = true; 1565 return anchor; 1566} 1567 1568/** 1569 * Find a component ref attribute that starts with $. 1570 * 1571 * @param {Element} node 1572 * @return {String|undefined} 1573 */ 1574 1575var refRE = /^v-ref:/; 1576 1577function findRef(node) { 1578 if (node.hasAttributes()) { 1579 var attrs = node.attributes; 1580 for (var i = 0, l = attrs.length; i < l; i++) { 1581 var name = attrs[i].name; 1582 if (refRE.test(name)) { 1583 return camelize(name.replace(refRE, '')); 1584 } 1585 } 1586 } 1587} 1588 1589/** 1590 * Map a function to a range of nodes . 1591 * 1592 * @param {Node} node 1593 * @param {Node} end 1594 * @param {Function} op 1595 */ 1596 1597function mapNodeRange(node, end, op) { 1598 var next; 1599 while (node !== end) { 1600 next = node.nextSibling; 1601 op(node); 1602 node = next; 1603 } 1604 op(end); 1605} 1606 1607/** 1608 * Remove a range of nodes with transition, store 1609 * the nodes in a fragment with correct ordering, 1610 * and call callback when done. 1611 * 1612 * @param {Node} start 1613 * @param {Node} end 1614 * @param {Vue} vm 1615 * @param {DocumentFragment} frag 1616 * @param {Function} cb 1617 */ 1618 1619function removeNodeRange(start, end, vm, frag, cb) { 1620 var done = false; 1621 var removed = 0; 1622 var nodes = []; 1623 mapNodeRange(start, end, function (node) { 1624 if (node === end) done = true; 1625 nodes.push(node); 1626 removeWithTransition(node, vm, onRemoved); 1627 }); 1628 function onRemoved() { 1629 removed++; 1630 if (done && removed >= nodes.length) { 1631 for (var i = 0; i < nodes.length; i++) { 1632 frag.appendChild(nodes[i]); 1633 } 1634 cb && cb(); 1635 } 1636 } 1637} 1638 1639/** 1640 * Check if a node is a DocumentFragment. 1641 * 1642 * @param {Node} node 1643 * @return {Boolean} 1644 */ 1645 1646function isFragment(node) { 1647 return node && node.nodeType === 11; 1648} 1649 1650/** 1651 * Get outerHTML of elements, taking care 1652 * of SVG elements in IE as well. 1653 * 1654 * @param {Element} el 1655 * @return {String} 1656 */ 1657 1658function getOuterHTML(el) { 1659 if (el.outerHTML) { 1660 return el.outerHTML; 1661 } else { 1662 var container = document.createElement('div'); 1663 container.appendChild(el.cloneNode(true)); 1664 return container.innerHTML; 1665 } 1666} 1667 1668var commonTagRE = /^(div|p|span|img|a|b|i|br|ul|ol|li|h1|h2|h3|h4|h5|h6|code|pre|table|th|td|tr|form|label|input|select|option|nav|article|section|header|footer)$/i; 1669var reservedTagRE = /^(slot|partial|component)$/i; 1670 1671var isUnknownElement = undefined; 1672if ('development' !== 'production') { 1673 isUnknownElement = function (el, tag) { 1674 if (tag.indexOf('-') > -1) { 1675 // http://stackoverflow.com/a/28210364/1070244 1676 return el.constructor === window.HTMLUnknownElement || el.constructor === window.HTMLElement; 1677 } else { 1678 return (/HTMLUnknownElement/.test(el.toString()) && 1679 // Chrome returns unknown for several HTML5 elements. 1680 // https://code.google.com/p/chromium/issues/detail?id=540526 1681 // Firefox returns unknown for some "Interactive elements." 1682 !/^(data|time|rtc|rb|details|dialog|summary)$/.test(tag) 1683 ); 1684 } 1685 }; 1686} 1687 1688/** 1689 * Check if an element is a component, if yes return its 1690 * component id. 1691 * 1692 * @param {Element} el 1693 * @param {Object} options 1694 * @return {Object|undefined} 1695 */ 1696 1697function checkComponentAttr(el, options) { 1698 var tag = el.tagName.toLowerCase(); 1699 var hasAttrs = el.hasAttributes(); 1700 if (!commonTagRE.test(tag) && !reservedTagRE.test(tag)) { 1701 if (resolveAsset(options, 'components', tag)) { 1702 return { id: tag }; 1703 } else { 1704 var is = hasAttrs && getIsBinding(el, options); 1705 if (is) { 1706 return is; 1707 } else if ('development' !== 'production') { 1708 var expectedTag = options._componentNameMap && options._componentNameMap[tag]; 1709 if (expectedTag) { 1710 warn('Unknown custom element: <' + tag + '> - ' + 'did you mean <' + expectedTag + '>? ' + 'HTML is case-insensitive, remember to use kebab-case in templates.'); 1711 } else if (isUnknownElement(el, tag)) { 1712 warn('Unknown custom element: <' + tag + '> - did you ' + 'register the component correctly? For recursive c
1712omponents, ' + 'make sure to provide the "name" option.'); 1713 } 1714 } 1715 } 1716 } else if (hasAttrs) { 1717 return getIsBinding(el, options); 1718 } 1719} 1720 1721/** 1722 * Get "is" binding from an element. 1723 * 1724 * @param {Element} el 1725 * @param {Object} options 1726 * @return {Object|undefined} 1727 */ 1728 1729function getIsBinding(el, options) { 1730 // dynamic syntax 1731 var exp = el.getAttribute('is'); 1732 if (exp != null) { 1733 if (resolveAsset(options, 'components', exp)) { 1734 el.removeAttribute('is'); 1735 return { id: exp }; 1736 } 1737 } else { 1738 exp = getBindAttr(el, 'is'); 1739 if (exp != null) { 1740 return { id: exp, dynamic: true }; 1741 } 1742 } 1743} 1744 1745/** 1746 * Option overwriting strategies are functions that handle 1747 * how to merge a parent option value and a child option 1748 * value into the final value. 1749 * 1750 * All strategy functions follow the same signature: 1751 * 1752 * @param {*} parentVal 1753 * @param {*} childVal 1754 * @param {Vue} [vm] 1755 */ 1756 1757var strats = config.optionMergeStrategies = Object.create(null); 1758 1759/** 1760 * Helper that recursively merges two data objects together. 1761 */ 1762 1763function mergeData(to, from) { 1764 var key, toVal, fromVal; 1765 for (key in from) { 1766 toVal = to[key]; 1767 fromVal = from[key]; 1768 if (!hasOwn(to, key)) { 1769 set(to, key, fromVal); 1770 } else if (isObject(toVal) && isObject(fromVal)) { 1771 mergeData(toVal, fromVal); 1772 } 1773 } 1774 return to; 1775} 1776 1777/** 1778 * Data 1779 */ 1780 1781strats.data = function (parentVal, childVal, vm) { 1782 if (!vm) { 1783 // in a Vue.extend merge, both should be functions 1784 if (!childVal) { 1785 return parentVal; 1786 } 1787 if (typeof childVal !== 'function') { 1788 'development' !== 'production' && warn('The "data" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm); 1789 return parentVal; 1790 } 1791 if (!parentVal) { 1792 return childVal; 1793 } 1794 // when parentVal & childVal are both present, 1795 // we need to return a function that returns the 1796 // merged result of both functions... no need to 1797 // check if parentVal is a function here because 1798 // it has to be a function to pass previous merges. 1799 return function mergedDataFn() { 1800 return mergeData(childVal.call(this), parentVal.call(this)); 1801 }; 1802 } else if (parentVal || childVal) { 1803 return function mergedInstanceDataFn() { 1804 // instance merge 1805 var instanceData = typeof childVal === 'function' ? childVal.call(vm) : childVal; 1806 var defaultData = typeof parentVal === 'function' ? parentVal.call(vm) : undefined; 1807 if (instanceData) { 1808 return mergeData(instanceData, defaultData); 1809 } else { 1810 return defaultData; 1811 } 1812 }; 1813 } 1814}; 1815 1816/** 1817 * El 1818 */ 1819 1820strats.el = function (parentVal, childVal, vm) { 1821 if (!vm && childVal && typeof childVal !== 'function') { 1822 'development' !== 'production' && warn('The "el" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.', vm); 1823 return; 1824 } 1825 var ret = childVal || parentVal; 1826 // invoke the element factory if this is instance merge 1827 return vm && typeof ret === 'function' ? ret.call(vm) : ret; 1828}; 1829 1830/** 1831 * Hooks and param attributes are merged as arrays. 1832 */ 1833 1834strats.init = strats.created = strats.ready = strats.attached = strats.detached = strats.beforeCompile = strats.compiled = strats.beforeDestroy = strats.destroyed = strats.activate = function (parentVal, childVal) { 1835 return childVal ? parentVal ? parentVal.concat(childVal) : isArray(childVal) ? childVal : [childVal] : parentVal; 1836}; 1837 1838/** 1839 * Assets 1840 * 1841 * When a vm is present (instance creation), we need to do 1842 * a three-way merge between constructor options, instance 1843 * options and parent options. 1844 */ 1845 1846function mergeAssets(parentVal, childVal) { 1847 var res = Object.create(parentVal || null); 1848 return childVal ? extend(res, guardArrayAssets(childVal)) : res; 1849} 1850 1851config._assetTypes.forEach(function (type) { 1852 strats[type + 's'] = mergeAssets; 1853}); 1854 1855/** 1856 * Events & Watchers. 1857 * 1858 * Events & watchers hashes should not overwrite one 1859 * another, so we merge them as arrays. 1860 */ 1861 1862strats.watch = strats.events = function (parentVal, childVal) { 1863 if (!childVal) return parentVal;
1864 if (!parentVal) return childVal; 1865 var ret = {}; 1866 extend(ret, parentVal); 1867 for (var key in childVal) { 1868 var parent = ret[key]; 1869 var child = childVal[key]; 1870 if (parent && !isArray(parent)) { 1871 parent = [parent]; 1872 } 1873 ret[key] = parent ? parent.concat(child) : [child]; 1874 } 1875 return ret; 1876}; 1877 1878/** 1879 * Other object hashes. 1880 */ 1881 1882strats.props = strats.methods = strats.computed = function (parentVal, childVal) { 1883 if (!childVal) return parentVal; 1884 if (!parentVal) return childVal; 1885 var ret = Object.create(null); 1886 extend(ret, parentVal); 1887 extend(ret, childVal); 1888 return ret; 1889}; 1890 1891/** 1892 * Default strategy. 1893 */ 1894 1895var defaultStrat = function defaultStrat(parentVal, childVal) { 1896 return childVal === undefined ? parentVal : childVal; 1897}; 1898 1899/** 1900 * Make sure component options get converted to actual 1901 * constructors. 1902 * 1903 * @param {Object} options 1904 */ 1905 1906function guardComponents(options) { 1907 if (options.components) { 1908 var components = options.components = guardArrayAssets(options.components); 1909 var ids = Object.keys(components); 1910 var def; 1911 if ('development' !== 'production') { 1912 var map = options._componentNameMap = {}; 1913 } 1914 for (var i = 0, l = ids.length; i < l; i++) { 1915 var key = ids[i]; 1916 if (commonTagRE.test(key) || reservedTagRE.test(key)) { 1917 'development' !== 'production' && warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + key); 1918 continue; 1919 } 1920 // record a all lowercase <-> kebab-case mapping for 1921 // possible custom element case error warning 1922 if ('development' !== 'production') { 1923 map[key.replace(/-/g, '').toLowerCase()] = hyphenate(key); 1924 } 1925 def = components[key]; 1926 if (isPlainObject(def)) { 1927 components[key] = Vue.extend(def); 1928 } 1929 } 1930 } 1931} 1932 1933/** 1934 * Ensure all props option syntax are normalized into the 1935 * Object-based format. 1936 * 1937 * @param {Object} options 1938 */ 1939 1940function guardProps(options) { 1941 var props = options.props; 1942 var i, val; 1943 if (isArray(props)) { 1944 options.props = {}; 1945 i = props.length; 1946 while (i--) { 1947 val = props[i]; 1948 if (typeof val === 'string') { 1949 options.props[val] = null; 1950 } else if (val.name) { 1951 options.props[val.name] = val; 1952 } 1953 } 1954 } else if (isPlainObject(props)) { 1955 var keys = Object.keys(props); 1956 i = keys.length; 1957 while (i--) { 1958 val = props[keys[i]]; 1959 if (typeof val === 'function') { 1960 props[keys[i]] = { type: val }; 1961 } 1962 } 1963 } 1964} 1965 1966/** 1967 * Guard an Array-format assets option and converted it 1968 * into the key-value Object format. 1969 * 1970 * @param {Object|Array} assets 1971 * @return {Object} 1972 */ 1973 1974function guardArrayAssets(assets) { 1975 if (isArray(assets)) { 1976 var res = {}; 1977 var i = assets.length; 1978 var asset; 1979 while (i--) { 1980 asset = assets[i]; 1981 var id = typeof asset === 'function' ? asset.options && asset.options.name || asset.id : asset.name || asset.id; 1982 if (!id) { 1983 'development' !== 'production' && warn('Array-syntax assets must provide a "name" or "id" field.'); 1984 } else { 1985 res[id] = asset; 1986 } 1987 } 1988 return res; 1989 } 1990 return assets; 1991} 1992 1993/** 1994 * Merge two option objects into a new one. 1995 * Core utility used in both instantiation and inheritance. 1996 * 1997 * @param {Object} parent 1998 * @param {Object} child 1999 * @param {Vue} [vm] - if vm is present, indicates this is 2000 * an instantiation merge. 2001 */ 2002 2003function mergeOptions(parent, child, vm) { 2004 guardComponents(child); 2005 guardProps(child); 2006 if ('development' !== 'production') { 2007 if (child.propsData && !vm) { 2008 warn('propsData can only be used as an instantiation option.'); 2009 } 2010 } 2011 var options = {}; 2012 var key; 2013 if (child['extends']) { 2014 parent = typeof child['extends'] === 'function' ? mergeOptions(parent, child['extends'].options, vm) : mergeOptions(parent, child['extends'], vm); 2015 } 2016 if (child.mixins) { 2017 for (var i = 0, l = child.mixins.length; i < l; i++) { 2018 var mixin = child.mixins[i]; 2019 var mixinOptions = mixin.prototype instanceof Vue ? mixin.options : mixin; 2020 parent = mergeOptions(parent, mixinOptions, vm); 2021 } 2022 } 2023 for (key in parent) { 2024 mergeField(key); 2025 } 2026 for (key in child) { 2027 if (!hasOwn(parent, key)) { 2028 mergeField(key); 2029 } 2030 } 2031 function mergeField(key) { 2032 var strat = strats[key] || defaultStrat; 2033 options[key] = strat(parent[key], child[key], vm, key); 2034 } 2035 return options; 2036} 2037 2038/** 2039 * Resolve an asset. 2040 * This function is used because child instances need access 2041 * to assets defined in its ancestor chain. 2042 * 2043 * @param {Object} options 2044 * @param {String} type 2045 * @param {String} id 2046 * @param {Boolean} warnMissing 2047 * @return {Object|Function} 2048 */ 2049 2050function resolveAsset(options, type, id, warnMissing) { 2051 /* istanbul ignore if */ 2052 if (typeof id !== 'string') { 2053 return; 2054 } 2055 var assets = options[type]; 2056 var camelizedId; 2057 var res = assets[id] || 2058 // camelCase ID 2059 assets[camelizedId = camelize(id)] || 2060 // Pascal Case ID 2061 assets[camelizedId.charAt(0).toUpperCase() + camelizedId.slice(1)]; 2062 if ('development' !== 'production' && warnMissing && !res) { 2063 warn('Failed to resolve ' + type.slice(0, -1) + ': ' + id, options); 2064 } 2065 return res; 2066} 2067 2068var uid$1 = 0; 2069 2070/** 2071 * A dep is an observable that can have multiple 2072 * directives subscribing to it. 2073 * 2074 * @constructor 2075 */ 2076function Dep() { 2077 this.id = uid$1++; 2078 this.subs = []; 2079} 2080 2081// the current target watcher being evaluated. 2082// this is globally unique because there could be only one 2083// watcher being evaluated at any time. 2084Dep.target = null; 2085 2086/** 2087 * Add a directive subscriber. 2088 * 2089 * @param {Directive} sub 2090 */ 2091 2092Dep.prototype.addSub = function (sub) { 2093 this.subs.push(sub); 2094}; 2095 2096/** 2097 * Remove a directive subscriber. 2098 * 2099 * @param {Directive} sub 2100 */ 2101 2102Dep.prototype.removeSub = function (sub) { 2103 this.subs.$remove(sub); 2104}; 2105 2106/** 2107 * Add self as a dependency to the target watcher. 2108 */ 2109
2110Dep.prototype.depend = function () { 2111 Dep.target.addDep(this); 2112}; 2113 2114/** 2115 * Notify all subscribers of a new value. 2116 */ 2117 2118Dep.prototype.notify = function () { 2119 // stablize the subscriber list first 2120 var subs = toArray(this.subs); 2121 for (var i = 0, l = subs.length; i < l; i++) { 2122 subs[i].update(); 2123 } 2124}; 2125 2126var arrayProto = Array.prototype; 2127var arrayMethods = Object.create(arrayProto) 2128 2129/** 2130 * Intercept mutating methods and emit events 2131 */ 2132 2133;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) { 2134 // cache original method 2135 var original = arrayProto[method]; 2136 def(arrayMethods, method, function mutator() { 2137 // avoid leaking arguments: 2138 // http://jsperf.com/closure-with-arguments 2139 var i = arguments.length; 2140 var args = new Array(i); 2141 while (i--) { 2142 args[i] = arguments[i]; 2143 } 2144 var result = original.apply(this, args); 2145 var ob = this.__ob__; 2146 var inserted; 2147 switch (method) { 2148 case 'push': 2149 inserted = args; 2150 break; 2151 case 'unshift': 2152 inserted = args; 2153 break; 2154 case 'splice': 2155 inserted = args.slice(2); 2156 break; 2157 } 2158 if (inserted) ob.observeArray(inserted); 2159 // notify change 2160 ob.dep.notify(); 2161 return result; 2162 }); 2163}); 2164 2165/** 2166 * Swap the element at the given index with a new value 2167 * and emits corresponding event. 2168 * 2169 * @param {Number} index 2170 * @param {*} val 2171 * @return {*} - replaced element 2172 */ 2173 2174def(arrayProto, '$set', function $set(index, val) { 2175 if (index >= this.length) { 2176 this.length = Number(index) + 1; 2177 } 2178 return this.splice(index, 1, val)[0]; 2179}); 2180 2181/** 2182 * Convenience method to remove the element at given index or target element reference. 2183 * 2184 * @param {*} item 2185 */ 2186 2187def(arrayProto, '$remove', function $remove(item) { 2188 /* istanbul ignore if */ 2189 if (!this.length) return; 2190 var index = indexOf(this, item); 2191 if (index > -1) { 2192 return this.splice(index, 1); 2193 } 2194}); 2195 2196var arrayKeys = Object.getOwnPropertyNames(arrayMethods); 2197 2198/** 2199 * By default, when a reactive property is set, the new value is 2200 * also converted to become reactive. However in certain cases, e.g. 2201 * v-for scope alias and props, we don't want to force conversion 2202 * because the value may be a nested value under a frozen data structure. 2203 * 2204 * So whenever we want to set a reactive property without forcing 2205 * conversion on the new value, we wrap that call inside this function. 2206 */ 2207 2208var shouldConvert = true; 2209 2210function withoutConversion(fn) { 2211 shouldConvert = false; 2212 fn(); 2213 shouldConvert = true; 2214} 2215 2216/** 2217 * Observer class that are attached to each observed 2218 * object. Once attached, the observer converts target 2219 * object's property keys into getter/setters that 2220 * collect dependencies and dispatches updates. 2221 * 2222 * @param {Array|Object} value 2223 * @constructor 2224 */ 2225 2226function Observer(value) { 2227 this.value = value; 2228 this.dep = new Dep(); 2229 def(value, '__ob__', this); 2230 if (isArray(value)) { 2231 var augment = hasProto ? protoAugment : copyAugment; 2232 augment(value, arrayMethods, arrayKeys); 2233 this.observeArray(value); 2234 } else { 2235 this.walk(value); 2236 } 2237} 2238 2239// Instance methods 2240 2241/** 2242 * Walk through each property and convert them into 2243 * getter/setters. This method should only be called when 2244 * value type is Object. 2245 * 2246 * @param {Object} obj 2247 */ 2248 2249Observer.prototype.walk = function (obj) { 2250 var keys = Object.keys(obj); 2251 for (var i = 0, l = keys.length; i < l; i++) { 2252 this.convert(keys[i], obj[keys[i]]); 2253 } 2254}; 2255 2256/** 2257 * Observe a list of Array items. 2258 * 2259 * @param {Array} items 2260 */ 2261 2262Observer.prototype.observeArray = function (items) { 2263 for (var i = 0, l = items.length; i < l; i++) { 2264 observe(items[i]); 2265 } 2266}; 2267 2268/** 2269 * Convert a property into getter/setter so we can emit 2270 * the events when the property is accessed/changed. 2271 * 2272 * @param {String} key 2273 * @param {*} val 2274 */ 2275 2276Observer.prototype.convert = function (key, val) { 2277 defineReactive(this.value, key, val); 2278}; 2279 2280/** 2281 * Add an owner vm, so that when $set/$delete mutations 2282 * happen we can notify owner vms to proxy the keys and 2283 * digest the watchers. This is only called when the object 2284 * is observed as an instance's root $data. 2285 * 2286 * @param {Vue} vm 2287 */ 2288 2289Observer.prototype.addVm = function (vm) { 2290 (this.vms || (this.vms = [])).push(vm); 2291}; 2292 2293/** 2294 * Remove an owner vm. This is called when the object is 2295 * swapped out as an instance's $data object. 2296 * 2297 * @param {Vue} vm 2298 */ 2299 2300Observer.prototype.removeVm = function (vm) { 2301 this.vms.$remove(vm); 2302}; 2303 2304// helpers 2305 2306/** 2307 * Augment an target Object or Array by intercepting 2308 * the prototype chain using __proto__ 2309 * 2310 * @param {Object|Array} target 2311 * @param {Object} src 2312 */ 2313
2314function protoAugment(target, src) { 2315 /* eslint-disable no-proto */ 2316 target.__proto__ = src; 2317 /* eslint-enable no-proto */ 2318} 2319 2320/** 2321 * Augment an target Object or Array by defining 2322 * hidden properties. 2323 * 2324 * @param {Object|Array} target 2325 * @param {Object} proto 2326 */ 2327 2328function copyAugment(target, src, keys) { 2329 for (var i = 0, l = keys.length; i < l; i++) { 2330 var key = keys[i]; 2331 def(target, key, src[key]); 2332 } 2333} 2334 2335/** 2336 * Attempt to create an observer instance for a value, 2337 * returns the new observer if successfully observed, 2338 * or the existing observer if the value already has one. 2339 * 2340 * @param {*} value 2341 * @param {Vue} [vm] 2342 * @return {Observer|undefined} 2343 * @static 2344 */ 2345 2346function observe(value, vm) { 2347 if (!value || typeof value !== 'object') { 2348 return; 2349 } 2350 var ob; 2351 if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { 2352 ob = value.__ob__; 2353 } else if (shouldConvert && (isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue) { 2354 ob = new Observer(value); 2355 } 2356 if (ob && vm) { 2357 ob.addVm(vm); 2358 } 2359 return ob; 2360} 2361 2362/** 2363 * Define a reactive property on an Object. 2364 * 2365 * @param {Object} obj 2366 * @param {String} key 2367 * @param {*} val 2368 */ 2369 2370function defineReactive(obj, key, val) { 2371 var dep = new Dep(); 2372 2373 var property = Object.getOwnPropertyDescriptor(obj, key); 2374 if (property && property.configurable === false) { 2375 return; 2376 } 2377 2378 // cater for pre-defined getter/setters 2379 var getter = property && property.get; 2380 var setter = property && property.set; 2381 2382 var childOb = observe(val); 2383 Object.defineProperty(obj, key, { 2384 enumerable: true, 2385 configurable: true, 2386 get: function reactiveGetter() { 2387 var value = getter ? getter.call(obj) : val; 2388 if (Dep.target) { 2389 dep.depend(); 2390 if (childOb) { 2391 childOb.dep.depend(); 2392 } 2393 if (isArray(value)) { 2394 for (var e, i = 0, l = value.length; i < l; i++) { 2395 e = value[i]; 2396 e && e.__ob__ && e.__ob__.dep.depend(); 2397 } 2398 } 2399 } 2400 return value; 2401 }, 2402 set: function reactiveSetter(newVal) { 2403 var value = getter ? getter.call(obj) : val; 2404 if (newVal === value) { 2405 return; 2406 } 2407 if (setter) { 2408 setter.call(obj, newVal); 2409 } else { 2410 val = newVal; 2411 } 2412 childOb = observe(newVal); 2413 dep.notify(); 2414 } 2415 }); 2416} 2417 2418 2419 2420var util = Object.freeze({ 2421 defineReactive: defineReactive, 2422 set: set, 2423 del: del, 2424 hasOwn: hasOwn, 2425 isLiteral: isLiteral, 2426 isReserved: isReserved, 2427 _toString: _toString, 2428 toNumber: toNumber, 2429 toBoolean: toBoolean, 2430 stripQuotes: stripQuotes, 2431 camelize: camelize, 2432 hyphenate: hyphenate, 2433 classify: classify, 2434 bind: bind, 2435 toArray: toArray, 2436 extend: extend, 2437 isObject: isObject, 2438 isPlainObject: isPlainObject, 2439 def: def, 2440 debounce: _debounce, 2441 indexOf: indexOf, 2442 cancellable: cancellable, 2443 looseEqual: looseEqual, 2444 isArray: isArray, 2445 hasProto: hasProto, 2446 inBrowser: inBrowser, 2447 devtools: devtools, 2448 isIE: isIE, 2449 isIE9: isIE9, 2450 isAndroid: isAndroid, 2451 isIOS: isIOS, 2452 get transitionProp () { return transitionProp; }, 2453 get transitionEndEvent () { return transitionEndEvent; }, 2454 get animationProp () { return animationProp; }, 2455 get animationEndEvent () { return animationEndEvent; }, 2456 nextTick: nextTick, 2457 get _Set () { return _Set; }, 2458 query: query, 2459 inDoc: inDoc, 2460 getAttr: getAttr, 2461 getBindAttr: getBindAttr, 2462 hasBindAttr: hasBindAttr, 2463 before: before, 2464 after: after, 2465 remove: remove, 2466 prepend: prepend, 2467 replace: replace, 2468 on: on, 2469 off: off, 2470 setClass: setClass, 2471 addClass: addClass, 2472 removeClass: removeClass, 2473 extractContent: extractContent, 2474 trimNode: trimNode, 2475 isTemplate: isTemplate, 2476 createAnchor: createAnchor, 2477 findRef: findRef, 2478 mapNodeRange: mapNodeRange, 2479 removeNodeRange: removeNodeRange, 2480 isFragment: isFragment, 2481 getOuterHTML: getOuterHTML, 2482 mergeOptions: mergeOptions, 2483 resolveAsset: resolveAsset, 2484 checkComponentAttr: checkComponentAttr, 2485 commonTagRE: commonTagRE, 2486 reservedTagRE: reservedTagRE, 2487 get warn () { return warn; } 2488}); 2489 2490var uid = 0; 2491 2492function initMixin (Vue) { 2493 /** 2494 * The main init sequence. This is called for every 2495 * instance, including ones that are created from extended 2496 * constructors. 2497 * 2498 * @param {Object} options - this options object should be 2499 * the result of merging class 2500 * options and the options passed 2501 * in to the constructor. 2502 */ 2503 2504 Vue.prototype._init = function (options) { 2505 options = options || {}; 2506 2507 this.$el = null; 2508 this.$parent = options.parent; 2509 this.$root = this.$parent ? this.$parent.$root : this; 2510 this.$children = []; 2511 this.$refs = {}; // child vm references 2512 this.$els = {}; // element references 2513 this._watchers = []; // all watchers as an array 2514 this._directives = []; // all directives 2515 2516 // a uid 2517 this._uid = uid++; 2518 2519 // a flag to avoid this being observed 2520 this._isVue = true; 2521 2522 // events bookkeeping 2523 this._events = {}; // registered callbacks 2524 this._eventsCount = {}; // for $broadcast optimization 2525 2526 // fragment instance properties 2527 this._isFragment = false;
2528 this._fragment = // @type {DocumentFragment} 2529 this._fragmentStart = // @type {Text|Comment} 2530 this._fragmentEnd = null; // @type {Text|Comment} 2531 2532 // lifecycle state 2533 this._isCompiled = this._isDestroyed = this._isReady = this._isAttached = this._isBeingDestroyed = this._vForRemoving = false; 2534 this._unlinkFn = null; 2535 2536 // context: 2537 // if this is a transcluded component, context 2538 // will be the common parent vm of this instance 2539 // and its host. 2540 this._context = options._context || this.$parent; 2541 2542 // scope: 2543 // if this is inside an inline v-for, the scope 2544 // will be the intermediate scope created for this 2545 // repeat fragment. this is used for linking props 2546 // and container directives. 2547 this._scope = options._scope; 2548 2549 // fragment: 2550 // if this instance is compiled inside a Fragment, it 2551 // needs to register itself as a child of that fragment 2552 // for attach/detach to work properly. 2553 this._frag = options._frag; 2554 if (this._frag) { 2555 this._frag.children.push(this); 2556 } 2557 2558 // push self into parent / transclusion host 2559 if (this.$parent) { 2560 this.$parent.$children.push(this); 2561 } 2562 2563 // merge options. 2564 options = this.$options = mergeOptions(this.constructor.options, options, this); 2565 2566 // set ref 2567 this._updateRef(); 2568 2569 // initialize data as empty object. 2570 // it will be filled up in _initData(). 2571 this._data = {}; 2572 2573 // call init hook 2574 this._callHook('init'); 2575 2576 // initialize data observation and scope inheritance. 2577 this._initState(); 2578 2579 // setup event system and option events. 2580 this._initEvents(); 2581 2582 // call created hook 2583 this._callHook('created'); 2584 2585 // if `el` option is passed, start compilation. 2586 if (options.el) { 2587 this.$mount(options.el); 2588 } 2589 }; 2590} 2591 2592var pathCache = new Cache(1000); 2593 2594// actions 2595var APPEND = 0; 2596var PUSH = 1; 2597var INC_SUB_PATH_DEPTH = 2; 2598var PUSH_SUB_PATH = 3; 2599 2600// states 2601var BEFORE_PATH = 0; 2602var IN_PATH = 1; 2603var BEFORE_IDENT = 2; 2604var IN_IDENT = 3; 2605var IN_SUB_PATH = 4; 2606var IN_SINGLE_QUOTE = 5; 2607var IN_DOUBLE_QUOTE = 6; 2608var AFTER_PATH = 7; 2609var ERROR = 8; 2610 2611var pathStateMachine = []; 2612 2613pathStateMachine[BEFORE_PATH] = { 2614 'ws': [BEFORE_PATH], 2615 'ident': [IN_IDENT, APPEND], 2616 '[': [IN_SUB_PATH], 2617 'eof': [AFTER_PATH] 2618}; 2619 2620pathStateMachine[IN_PATH] = { 2621 'ws': [IN_PATH], 2622 '.': [BEFORE_IDENT], 2623 '[': [IN_SUB_PATH], 2624 'eof': [AFTER_PATH] 2625}; 2626 2627pathStateMachine[BEFORE_IDENT] = { 2628 'ws': [BEFORE_IDENT], 2629 'ident': [IN_IDENT, APPEND] 2630}; 2631 2632pathStateMachine[IN_IDENT] = { 2633 'ident': [IN_IDENT, APPEND], 2634 '0': [IN_IDENT, APPEND], 2635 'number': [IN_IDENT, APPEND], 2636 'ws': [IN_PATH, PUSH], 2637 '.': [BEFORE_IDENT, PUSH], 2638 '[': [IN_SUB_PATH, PUSH], 2639 'eof': [AFTER_PATH, PUSH] 2640}; 2641 2642pathStateMachine[IN_SUB_PATH] = { 2643 "'": [IN_SINGLE_QUOTE, APPEND], 2644 '"': [IN_DOUBLE_QUOTE, APPEND], 2645 '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH], 2646 ']': [IN_PATH, PUSH_SUB_PATH], 2647 'eof': ERROR, 2648 'else': [IN_SUB_PATH, APPEND] 2649}; 2650 2651pathStateMachine[IN_SINGLE_QUOTE] = { 2652 "'": [IN_SUB_PATH, APPEND], 2653 'eof': ERROR, 2654 'else': [IN_SINGLE_QUOTE, APPEND] 2655}; 2656 2657pathStateMachine[IN_DOUBLE_QUOTE] = { 2658 '"': [IN_SUB_PATH, APPEND], 2659 'eof': ERROR, 2660 'else': [IN_DOUBLE_QUOTE, APPEND] 2661}; 2662 2663/** 2664 * Determine the type of a character in a keypath. 2665 * 2666 * @param {Char} ch 2667 * @return {String} type 2668 */ 2669 2670function getPathCharType(ch) { 2671 if (ch === undefined) { 2672 return 'eof'; 2673 } 2674 2675 var code = ch.charCodeAt(0); 2676 2677 switch (code) { 2678 case 0x5B: // [ 2679 case 0x5D: // ] 2680 case 0x2E: // . 2681 case 0x22: // " 2682 case 0x27: // ' 2683 case 0x30: 2684 // 0 2685 return ch; 2686 2687 case 0x5F: // _ 2688 case 0x24: 2689 // $ 2690 return 'ident'; 2691 2692 case 0x20: // Space 2693 case 0x09: // Tab 2694 case 0x0A: // Newline 2695 case 0x0D: // Return 2696 case 0xA0: // No-break space 2697 case 0xFEFF: // Byte Order Mark 2698 case 0x2028: // Line Separator 2699 case 0x2029: 2700 // Paragraph Separator 2701 return 'ws'; 2702 } 2703 2704 // a-z, A-Z 2705 if (code >= 0x61 && code <= 0x7A || code >= 0x41 && code <= 0x5A) { 2706 return 'ident'; 2707 } 2708 2709 // 1-9 2710 if (code >= 0x31 && code <= 0x39) { 2711 return 'number'; 2712 } 2713 2714 return 'else'; 2715} 2716 2717/** 2718 * Format a subPath, return its plain form if it is 2719 * a literal string or number. Otherwise prepend the 2720 * dynamic indicator (*). 2721 * 2722 * @param {String} path 2723 * @return {String} 2724 */ 2725 2726function formatSubPath(path) { 2727 var trimmed = path.trim(); 2728 // invalid leading 0 2729 if (path.charAt(0) === '0' && isNaN(path)) { 2730 return false;
2731 } 2732 return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed; 2733} 2734 2735/** 2736 * Parse a string path into an array of segments 2737 * 2738 * @param {String} path 2739 * @return {Array|undefined} 2740 */ 2741 2742function parse(path) { 2743 var keys = []; 2744 var index = -1; 2745 var mode = BEFORE_PATH; 2746 var subPathDepth = 0; 2747 var c, newChar, key, type, transition, action, typeMap; 2748 2749 var actions = []; 2750 2751 actions[PUSH] = function () { 2752 if (key !== undefined) { 2753 keys.push(key); 2754 key = undefined; 2755 } 2756 }; 2757 2758 actions[APPEND] = function () { 2759 if (key === undefined) { 2760 key = newChar; 2761 } else { 2762 key += newChar; 2763 } 2764 }; 2765 2766 actions[INC_SUB_PATH_DEPTH] = function () { 2767 actions[APPEND](); 2768 subPathDepth++; 2769 }; 2770 2771 actions[PUSH_SUB_PATH] = function () { 2772 if (subPathDepth > 0) { 2773 subPathDepth--; 2774 mode = IN_SUB_PATH; 2775 actions[APPEND](); 2776 } else { 2777 subPathDepth = 0; 2778 key = formatSubPath(key); 2779 if (key === false) { 2780 return false; 2781 } else { 2782 actions[PUSH](); 2783 } 2784 } 2785 }; 2786 2787 function maybeUnescapeQuote() { 2788 var nextChar = path[index + 1]; 2789 if (mode === IN_SINGLE_QUOTE && nextChar === "'" || mode === IN_DOUBLE_QUOTE && nextChar === '"') { 2790 index++; 2791 newChar = '\\' + nextChar; 2792 actions[APPEND](); 2793 return true; 2794 } 2795 } 2796 2797 while (mode != null) { 2798 index++; 2799 c = path[index]; 2800 2801 if (c === '\\' && maybeUnescapeQuote()) { 2802 continue; 2803 } 2804 2805 type = getPathCharType(c); 2806 typeMap = pathStateMachine[mode]; 2807 transition = typeMap[type] || typeMap['else'] || ERROR; 2808 2809 if (transition === ERROR) { 2810 return; // parse error 2811 } 2812 2813 mode = transition[0]; 2814 action = actions[transition[1]]; 2815 if (action) { 2816 newChar = transition[2]; 2817 newChar = newChar === undefined ? c : newChar; 2818 if (action() === false) { 2819 return; 2820 } 2821 } 2822 2823 if (mode === AFTER_PATH) { 2824 keys.raw = path; 2825 return keys; 2826 } 2827 } 2828} 2829 2830/** 2831 * External parse that check for a cache hit first 2832 * 2833 * @param {String} path 2834 * @return {Array|undefined} 2835 */ 2836 2837function parsePath(path) { 2838 var hit = pathCache.get(path); 2839 if (!hit) { 2840 hit = parse(path); 2841 if (hit) { 2842 pathCache.put(path, hit); 2843 } 2844 } 2845 return hit; 2846} 2847 2848/** 2849 * Get from an object from a path string 2850 * 2851 * @param {Object} obj 2852 * @param {String} path 2853 */ 2854 2855function getPath(obj, path) { 2856 return parseExpression$1(path).get(obj); 2857} 2858 2859/** 2860 * Warn against setting non-existent root path on a vm. 2861 */ 2862 2863var warnNonExistent; 2864if ('development' !== 'production') { 2865 warnNonExistent = function (path, vm) { 2866 warn('You are setting a non-existent path "' + path.raw + '" ' + 'on a vm instance. Consider pre-initializing the property ' + 'with the "data" option for more reliable reactivity ' + 'and better performance.', vm); 2867 }; 2868} 2869 2870/** 2871 * Set on an object from a path 2872 * 2873 * @param {Object} obj 2874 * @param {String | Array} path 2875 * @param {*} val 2876 */ 2877 2878function setPath(obj, path, val) { 2879 var original = obj; 2880 if (typeof path === 'string') { 2881 path = parse(path); 2882 } 2883 if (!path || !isObject(obj)) { 2884 return false; 2885 } 2886 var last, key; 2887 for (var i = 0, l = path.length; i < l; i++) { 2888 last = obj; 2889 key = path[i]; 2890 if (key.charAt(0) === '*') { 2891 key = parseExpression$1(key.slice(1)).get.call(original, original); 2892 } 2893 if (i < l - 1) { 2894 obj = obj[key]; 2895 if (!isObject(obj)) { 2896 obj = {}; 2897 if ('development' !== 'production' && last._isVue) { 2898 warnNonExistent(path, last); 2899 } 2900 set(last, key, obj); 2901 } 2902 } else { 2903 if (isArray(obj)) { 2904 obj.$set(key, val); 2905 } else if (key in obj) { 2906 obj[key] = val; 2907 } else { 2908 if ('development' !== 'production' && obj._isVue) { 2909 warnNonExistent(path, obj); 2910 } 2911 set(obj, key, val); 2912 } 2913 } 2914 } 2915 return true; 2916} 2917 2918var path = Object.freeze({ 2919 parsePath: parsePath, 2920 getPath: getPath, 2921 setPath: setPath 2922}); 2923 2924var expressionCache = new Cache(1000); 2925 2926var allowedKeywords = 'Math,Date,this,true,false,null,undefined,Infinity,NaN,' + 'isNaN,isFinite,decode
2926URI,decodeURIComponent,encodeURI,' + 'encodeURIComponent,parseInt,parseFloat'; 2927var allowedKeywordsRE = new RegExp('^(' + allowedKeywords.replace(/,/g, '\\b|') + '\\b)'); 2928 2929// keywords that don't make sense inside expressions 2930var improperKeywords = 'break,case,class,catch,const,continue,debugger,default,' + 'delete,do,else,export,extends,finally,for,function,if,' + 'import,in,instanceof,let,return,super,switch,throw,try,' + 'var,while,with,yield,enum,await,implements,package,' + 'protected,static,interface,private,public'; 2931var improperKeywordsRE = new RegExp('^(' + improperKeywords.replace(/,/g, '\\b|') + '\\b)'); 2932 2933var wsRE = /\s/g; 2934var newlineRE = /\n/g; 2935var saveRE = /[\{,]\s*[\w\$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"|`(?:[^`\\]|\\.)*\$\{|\}(?:[^`\\"']|\\.)*`|`(?:[^`\\]|\\.)*`)|new |typeof |void /g; 2936var restoreRE = /"(\d+)"/g; 2937var pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/; 2938var identRE = /[^\w$\.](?:[A-Za-z_$][\w$]*)/g; 2939var literalValueRE$1 = /^(?:true|false|null|undefined|Infinity|NaN)$/; 2940 2941function noop() {} 2942 2943/** 2944 * Save / Rewrite / Restore 2945 * 2946 * When rewriting paths found in an expression, it is 2947 * possible for the same letter sequences to be found in 2948 * strings and Object literal property keys. Therefore we 2949 * remove and store these parts in a temporary array, and 2950 * restore them after the path rewrite. 2951 */ 2952 2953var saved = []; 2954 2955/** 2956 * Save replacer 2957 * 2958 * The save regex can match two possible cases: 2959 * 1. An opening object literal 2960 * 2. A string 2961 * If matched as a plain string, we need to escape its 2962 * newlines, since the string needs to be preserved when 2963 * generating the function body. 2964 * 2965 * @param {String} str 2966 * @param {String} isString - str if matched as a string 2967 * @return {String} - placeholder with index 2968 */ 2969 2970function save(str, isString) { 2971 var i = saved.length; 2972 saved[i] = isString ? str.replace(newlineRE, '\\n') : str; 2973 return '"' + i + '"'; 2974} 2975 2976/** 2977 * Path rewrite replacer 2978 * 2979 * @param {String} raw 2980 * @return {String} 2981 */ 2982 2983function rewrite(raw) { 2984 var c = raw.charAt(0); 2985 var path = raw.slice(1); 2986 if (allowedKeywordsRE.test(path)) { 2987 return raw; 2988 } else { 2989 path = path.indexOf('"') > -1 ? path.replace(restoreRE, restore) : path; 2990 return c + 'scope.' + path; 2991 } 2992} 2993 2994/** 2995 * Restore replacer 2996 * 2997 * @param {String} str 2998 * @param {String} i - matched save index 2999 * @return {String} 3000 */ 3001 3002function restore(str, i) { 3003 return saved[i]; 3004} 3005 3006/** 3007 * Rewrite an expression, prefixing all path accessors with 3008 * `scope.` and generate getter/setter functions. 3009 * 3010 * @param {String} exp 3011 * @return {Function} 3012 */ 3013 3014function compileGetter(exp) { 3015 if (improperKeywordsRE.test(exp)) { 3016 'development' !== 'production' && warn('Avoid using reserved keywords in expression: ' + exp); 3017 } 3018 // reset state 3019 saved.length = 0; 3020 // save strings and object literal keys 3021 var body = exp.replace(saveRE, save).replace(wsRE, ''); 3022 // rewrite all paths 3023 // pad 1 space here because the regex matches 1 extra char 3024 body = (' ' + body).replace(identRE, rewrite).replace(restoreRE, restore); 3025 return makeGetterFn(body); 3026} 3027 3028/** 3029 * Build a getter function. Requires eval. 3030 * 3031 * We isolate the try/catch so it doesn't affect the 3032 * optimization of the parse function when it is not called. 3033 * 3034 * @param {String} body 3035 * @return {Function|undefined} 3036 */ 3037 3038function makeGetterFn(body) { 3039 try { 3040 /* eslint-disable no-new-func */ 3041 return new Function('scope', 'return ' + body + ';'); 3042 /* eslint-enable no-new-func */ 3043 } catch (e) { 3044 if ('development' !== 'production') { 3045 /* istanbul ignore if */ 3046 if (e.toString().match(/unsafe-eval|CSP/)) { 3047 warn('It seems you are using the default build of Vue.js in an environment ' + 'with Content Security Policy that prohibits unsafe-eval. ' + 'Use the CSP-compliant build instead: ' + 'http://vuejs.org/guide/installation.html#CSP-compliant-build'); 3048 } else { 3049 warn('Invalid expression. ' + 'Generated function body: ' + body); 3050 } 3051 } 3052 return noop; 3053 } 3054} 3055 3056/** 3057 * Compile a setter function for the expression. 3058 * 3059 * @param {String} exp 3060 * @return {Function|undefined} 3061 */ 3062 3063function compileSetter(exp) { 3064 var path = parsePath(exp); 3065 if (path) { 3066 return function (scope, val) { 3067 setPath(scope, path, val); 3068 }; 3069 } else { 3070 'development' !== 'production' && warn('Invalid setter expression: ' + exp); 3071 } 3072} 3073 3074/** 3075 * Parse an expression into re-written getter/setters. 3076 * 3077 * @param {String} exp 3078 * @param {Boolean} needSet 3079 * @return {Function} 3080 */ 3081 3082function parseExpression$1(exp, needSet) { 3083 exp = exp.trim(); 3084 // try cache 3085 var hit = expressionCache.get(exp); 3086 if (hit) { 3087 if (needSet && !hit.set) { 3088 hit.set = compileSetter(hit.exp); 3089 } 3090 return hit; 3091 } 3092 var res = { exp: exp }; 3093 res.get = isSimplePath(exp) && exp.indexOf('[') < 0 3094 // optimized super simple getter 3095 ? makeGetterFn('scope.' + exp) 3096 // dynamic getter
3097 : compileGetter(exp); 3098 if (needSet) { 3099 res.set = compileSetter(exp); 3100 } 3101 expressionCache.put(exp, res); 3102 return res; 3103} 3104 3105/** 3106 * Check if an expression is a simple path. 3107 * 3108 * @param {String} exp 3109 * @return {Boolean} 3110 */ 3111 3112function isSimplePath(exp) { 3113 return pathTestRE.test(exp) && 3114 // don't treat literal values as paths 3115 !literalValueRE$1.test(exp) && 3116 // Math constants e.g. Math.PI, Math.E etc. 3117 exp.slice(0, 5) !== 'Math.'; 3118} 3119 3120var expression = Object.freeze({ 3121 parseExpression: parseExpression$1, 3122 isSimplePath: isSimplePath 3123}); 3124 3125// we have two separate queues: one for directive updates 3126// and one for user watcher registered via $watch(). 3127// we want to guarantee directive updates to be called 3128// before user watchers so that when user watchers are 3129// triggered, the DOM would have already been in updated 3130// state. 3131 3132var queue = []; 3133var userQueue = []; 3134var has = {}; 3135var circular = {}; 3136var waiting = false; 3137 3138/** 3139 * Reset the batcher's state. 3140 */ 3141 3142function resetBatcherState() { 3143 queue.length = 0; 3144 userQueue.length = 0; 3145 has = {}; 3146 circular = {}; 3147 waiting = false; 3148} 3149 3150/** 3151 * Flush both queues and run the watchers. 3152 */ 3153 3154function flushBatcherQueue() { 3155 var _again = true; 3156 3157 _function: while (_again) { 3158 _again = false; 3159 3160 runBatcherQueue(queue); 3161 runBatcherQueue(userQueue); 3162 // user watchers triggered more watchers, 3163 // keep flushing until it depletes 3164 if (queue.length) { 3165 _again = true; 3166 continue _function; 3167 } 3168 // dev tool hook 3169 /* istanbul ignore if */ 3170 if (devtools && config.devtools) { 3171 devtools.emit('flush'); 3172 } 3173 resetBatcherState(); 3174 } 3175} 3176 3177/** 3178 * Run the watchers in a single queue. 3179 * 3180 * @param {Array} queue 3181 */ 3182 3183function runBatcherQueue(queue) { 3184 // do not cache length because more watchers might be pushed 3185 // as we run existing watchers 3186 for (var i = 0; i < queue.length; i++) { 3187 var watcher = queue[i]; 3188 var id = watcher.id; 3189 has[id] = null; 3190 watcher.run(); 3191 // in dev build, check and stop circular updates. 3192 if ('development' !== 'production' && has[id] != null) { 3193 circular[id] = (circular[id] || 0) + 1; 3194 if (circular[id] > config._maxUpdateCount) { 3195 warn('You may have an infinite update loop for watcher ' + 'with expression "' + watcher.expression + '"', watcher.vm); 3196 break; 3197 } 3198 } 3199 } 3200 queue.length = 0; 3201} 3202 3203/** 3204 * Push a watcher into the watcher queue. 3205 * Jobs with duplicate IDs will be skipped unless it's 3206 * pushed when the queue is being flushed. 3207 * 3208 * @param {Watcher} watcher 3209 * properties: 3210 * - {Number} id 3211 * - {Function} run 3212 */ 3213 3214function pushWatcher(watcher) { 3215 var id = watcher.id; 3216 if (has[id] == null) { 3217 // push watcher into appropriate queue 3218 var q = watcher.user ? userQueue : queue; 3219 has[id] = q.length; 3220 q.push(watcher); 3221 // queue the flush 3222 if (!waiting) { 3223 waiting = true; 3224 nextTick(flushBatcherQueue); 3225 } 3226 } 3227} 3228 3229var uid$2 = 0; 3230 3231/** 3232 * A watcher parses an expression, collects dependencies, 3233 * and fires callback when the expression value changes. 3234 * This is used for both the $watch() api and directives. 3235 * 3236 * @param {Vue} vm 3237 * @param {String|Function} expOrFn 3238 * @param {Function} cb 3239 * @param {Object} options 3240 * - {Array} filters 3241 * - {Boolean} twoWay 3242 * - {Boolean} deep 3243 * - {Boolean} user 3244 * - {Boolean} sync 3245 * - {Boolean} lazy 3246 * - {Function} [preProcess] 3247 * - {Function} [postProcess] 3248 * @constructor 3249 */ 3250function Watcher(vm, expOrFn, cb, options) { 3251 // mix in options 3252 if (options) { 3253 extend(this, options); 3254 } 3255 var isFn = typeof expOrFn === 'function'; 3256 this.vm = vm; 3257 vm._watchers.push(this); 3258 this.expression = expOrFn; 3259 this.cb = cb; 3260 this.id = ++uid$2; // uid for batching 3261 this.active = true; 3262 this.dirty = this.lazy; // for lazy watchers 3263 this.deps = []; 3264 this.newDeps = []; 3265 this.depIds = new _Set(); 3266 this.newDepIds = new _Set(); 3267 this.prevError = null; // for async error stacks 3268 // parse expression for getter/setter 3269 if (isFn) { 3270 this.getter = expOrFn; 3271 this.setter = undefined; 3272 } else { 3273 var res = parseExpression$1(expOrFn, this.twoWay); 3274 this.getter = res.get; 3275 this.setter = res.set; 3276 } 3277 this.value = this.lazy ? undefined : this.get(); 3278 // state for avoiding false triggers for deep and Array
3279 // watchers during vm._digest() 3280 this.queued = this.shallow = false; 3281} 3282 3283/** 3284 * Evaluate the getter, and re-collect dependencies. 3285 */ 3286 3287Watcher.prototype.get = function () { 3288 this.beforeGet(); 3289 var scope = this.scope || this.vm; 3290 var value; 3291 try { 3292 value = this.getter.call(scope, scope); 3293 } catch (e) { 3294 if ('development' !== 'production' && config.warnExpressionErrors) { 3295 warn('Error when evaluating expression ' + '"' + this.expression + '": ' + e.toString(), this.vm); 3296 } 3297 } 3298 // "touch" every property so they are all tracked as 3299 // dependencies for deep watching 3300 if (this.deep) { 3301 traverse(value); 3302 } 3303 if (this.preProcess) { 3304 value = this.preProcess(value); 3305 } 3306 if (this.filters) { 3307 value = scope._applyFilters(value, null, this.filters, false); 3308 } 3309 if (this.postProcess) { 3310 value = this.postProcess(value); 3311 } 3312 this.afterGet(); 3313 return value; 3314}; 3315 3316/** 3317 * Set the corresponding value with the setter. 3318 * 3319 * @param {*} value 3320 */ 3321 3322Watcher.prototype.set = function (value) { 3323 var scope = this.scope || this.vm; 3324 if (this.filters) { 3325 value = scope._applyFilters(value, this.value, this.filters, true); 3326 } 3327 try { 3328 this.setter.call(scope, scope, value); 3329 } catch (e) { 3330 if ('development' !== 'production' && config.warnExpressionErrors) { 3331 warn('Error when evaluating setter ' + '"' + this.expression + '": ' + e.toString(), this.vm); 3332 } 3333 } 3334 // two-way sync for v-for alias 3335 var forContext = scope.$forContext; 3336 if (forContext && forContext.alias === this.expression) { 3337 if (forContext.filters) { 3338 'development' !== 'production' && warn('It seems you are using two-way binding on ' + 'a v-for alias (' + this.expression + '), and the ' + 'v-for has filters. This will not work properly. ' + 'Either remove the filters or use an array of ' + 'objects and bind to object properties instead.', this.vm); 3339 return; 3340 } 3341 forContext._withLock(function () { 3342 if (scope.$key) { 3343 // original is an object 3344 forContext.rawValue[scope.$key] = value; 3345 } else { 3346 forContext.rawValue.$set(scope.$index, value); 3347 } 3348 }); 3349 } 3350}; 3351 3352/** 3353 * Prepare for dependency collection. 3354 */ 3355 3356Watcher.prototype.beforeGet = function () { 3357 Dep.target = this; 3358}; 3359 3360/** 3361 * Add a dependency to this directive. 3362 * 3363 * @param {Dep} dep 3364 */ 3365 3366Watcher.prototype.addDep = function (dep) { 3367 var id = dep.id; 3368 if (!this.newDepIds.has(id)) { 3369 this.newDepIds.add(id); 3370 this.newDeps.push(dep); 3371 if (!this.depIds.has(id)) { 3372 dep.addSub(this); 3373 } 3374 } 3375}; 3376 3377/** 3378 * Clean up for dependency collection. 3379 */ 3380 3381Watcher.prototype.afterGet = function () { 3382 Dep.target = null; 3383 var i = this.deps.length; 3384 while (i--) { 3385 var dep = this.deps[i]; 3386 if (!this.newDepIds.has(dep.id)) { 3387 dep.removeSub(this); 3388 } 3389 } 3390 var tmp = this.depIds; 3391 this.depIds = this.newDepIds; 3392 this.newDepIds = tmp; 3393 this.newDepIds.clear(); 3394 tmp = this.deps; 3395 this.deps = this.newDeps; 3396 this.newDeps = tmp; 3397 this.newDeps.length = 0; 3398}; 3399 3400/** 3401 * Subscriber interface. 3402 * Will be called when a dependency changes. 3403 * 3404 * @param {Boolean} shallow 3405 */ 3406 3407Watcher.prototype.update = function (shallow) { 3408 if (this.lazy) { 3409 this.dirty = true; 3410 } else if (this.sync || !config.async) { 3411 this.run(); 3412 } else { 3413 // if queued, only overwrite shallow with non-shallow, 3414 // but not the other way around. 3415 this.shallow = this.queued ? shallow ? this.shallow : false : !!shallow; 3416 this.queued = true; 3417 // record before-push error stack in debug mode 3418 /* istanbul ignore if */ 3419 if ('development' !== 'production' && config.debug) { 3420 this.prevError = new Error('[vue] async stack trace'); 3421 } 3422 pushWatcher(this); 3423 } 3424}; 3425 3426/** 3427 * Batcher job interface. 3428 * Will be called by the batcher. 3429 */ 3430 3431Watcher.prototype.run = function () { 3432 if (this.active) { 3433 var value = this.get(); 3434 if (value !== this.value || 3435 // Deep watchers and watchers on Object/Arrays should fire even 3436 // when the value is the same, because the value may 3437 // have mutated; but only do so if this is a 3438 // non-shallow update (caused by a vm digest). 3439 (isObject(value) || this.deep) && !this.shallow) { 3440 // set new value 3441 var oldValue = this.value; 3442 this.value = value; 3443 // in debug + async mode, when a watcher callbacks 3444 // throws, we also throw the saved before-push error 3445 // so the full cross-tick stack trace is available. 3446 var prevError = this.prevError; 3447 /* istanbul ignore if */ 3448 if ('development' !== 'production' && config.debug && prevError) { 3449 this.prevError = null; 3450 try { 3451 this.cb.call(this.vm, value, oldValue); 3452 } catch (e) { 3453 nextTick(function () { 3454 throw prevError; 3455 }, 0); 3456 throw e; 3457 } 3458 } else { 3459 this.cb.call(this.vm, value, oldValue); 3460 } 3461 } 3462 this.queued = this.shallow = false;
3463 } 3464}; 3465 3466/** 3467 * Evaluate the value of the watcher. 3468 * This only gets called for lazy watchers. 3469 */ 3470 3471Watcher.prototype.evaluate = function () { 3472 // avoid overwriting another watcher that is being 3473 // collected. 3474 var current = Dep.target; 3475 this.value = this.get(); 3476 this.dirty = false; 3477 Dep.target = current; 3478}; 3479 3480/** 3481 * Depend on all deps collected by this watcher. 3482 */ 3483 3484Watcher.prototype.depend = function () { 3485 var i = this.deps.length; 3486 while (i--) { 3487 this.deps[i].depend(); 3488 } 3489}; 3490 3491/** 3492 * Remove self from all dependencies' subcriber list. 3493 */ 3494 3495Watcher.prototype.teardown = function () { 3496 if (this.active) { 3497 // remove self from vm's watcher list 3498 // this is a somewhat expensive operation so we skip it 3499 // if the vm is being destroyed or is performing a v-for 3500 // re-render (the watcher list is then filtered by v-for). 3501 if (!this.vm._isBeingDestroyed && !this.vm._vForRemoving) { 3502 this.vm._watchers.$remove(this); 3503 } 3504 var i = this.deps.length; 3505 while (i--) { 3506 this.deps[i].removeSub(this); 3507 } 3508 this.active = false; 3509 this.vm = this.cb = this.value = null; 3510 } 3511}; 3512 3513/** 3514 * Recrusively traverse an object to evoke all converted 3515 * getters, so that every nested property inside the object 3516 * is collected as a "deep" dependency. 3517 * 3518 * @param {*} val 3519 */ 3520 3521var seenObjects = new _Set(); 3522function traverse(val, seen) { 3523 var i = undefined, 3524 keys = undefined; 3525 if (!seen) { 3526 seen = seenObjects; 3527 seen.clear(); 3528 } 3529 var isA = isArray(val); 3530 var isO = isObject(val); 3531 if ((isA || isO) && Object.isExtensible(val)) { 3532 if (val.__ob__) { 3533 var depId = val.__ob__.dep.id; 3534 if (seen.has(depId)) { 3535 return; 3536 } else { 3537 seen.add(depId); 3538 } 3539 } 3540 if (isA) { 3541 i = val.length; 3542 while (i--) traverse(val[i], seen); 3543 } else if (isO) { 3544 keys = Object.keys(val); 3545 i = keys.length; 3546 while (i--) traverse(val[keys[i]], seen); 3547 } 3548 } 3549} 3550 3551var text$1 = { 3552 3553 bind: function bind() { 3554 this.attr = this.el.nodeType === 3 ? 'data' : 'textContent'; 3555 }, 3556 3557 update: function update(value) { 3558 this.el[this.attr] = _toString(value); 3559 } 3560}; 3561 3562var templateCache = new Cache(1000); 3563var idSelectorCache = new Cache(1000); 3564 3565var map = { 3566 efault: [0, '', ''], 3567 legend: [1, '<fieldset>', '</fieldset>'], 3568 tr: [2, '<table><tbody>', '</tbody></table>'], 3569 col: [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'] 3570}; 3571 3572map.td = map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>']; 3573 3574map.option = map.optgroup = [1, '<select multiple="multiple">', '</select>']; 3575 3576map.thead = map.tbody = map.colgroup = map.caption = map.tfoot = [1, '<table>', '</table>']; 3577 3578map.g = map.defs = map.symbol = map.use = map.image = map.text = map.circle = map.ellipse = map.line = map.path = map.polygon = map.polyline = map.rect = [1, '<svg ' + 'xmlns="http://www.w3.org/2000/svg" ' + 'xmlns:xlink="http://www.w3.org/1999/xlink" ' + 'xmlns:ev="http://www.w3.org/2001/xml-events"' + 'version="1.1">', '</svg>']; 3579 3580/** 3581 * Check if a node is a supported template node with a 3582 * DocumentFragment content. 3583 * 3584 * @param {Node} node 3585 * @return {Boolean} 3586 */ 3587 3588function isRealTemplate(node) { 3589 return isTemplate(node) && isFragment(node.content); 3590} 3591 3592var tagRE$1 = /<([\w:-]+)/; 3593var entityRE = /&#?\w+?;/; 3594var commentRE = /<!--/; 3595 3596/** 3597 * Convert a string template to a DocumentFragment. 3598 * Determines correct wrapping by tag types. Wrapping 3599 * strategy found in jQuery & component/domify. 3600 * 3601 * @param {String} templateString 3602 * @param {Boolean} raw 3603 * @return {DocumentFragment} 3604 */ 3605 3606function stringToFragment(templateString, raw) { 3607 // try a cache hit first 3608 var cacheKey = raw ? templateString : templateString.trim(); 3609 var hit = templateCache.get(cacheKey); 3610 if (hit) { 3611 return hit; 3612 } 3613 3614 var frag = document.createDocumentFragment(); 3615 var tagMatch = templateString.match(tagRE$1); 3616 var entityMatch = entityRE.test(templateString); 3617 var commentMatch = commentRE.test(templateString); 3618 3619 if (!tagMatch && !entityMatch && !commentMatch) { 3620 // text only, return a single text node. 3621 frag.appendChild(document.createTextNode(templateString)); 3622 } else { 3623 var tag = tagMatch && tagMatch[1]; 3624 var wrap = map[tag] || map.efault; 3625 var depth = wrap[0]; 3626 var prefix = wrap[1]; 3627 var suffix = wrap[2]; 3628 var node = document.createElement('div'); 3629 3630 node.innerHTML = prefix + templateString + suffix; 3631 while (depth--) { 3632 node = node.lastChild; 3633 } 3634 3635 var child; 3636 /* eslint-disable no-cond-assign */ 3637 while (child = node.firstChild) { 3638 /* eslint-enable no-cond-assign */ 3639 frag.appendChild(child); 3640 } 3641 } 3642 if (!raw) { 3643 trimNode(frag); 3644 } 3645 templateCache.put(cacheKey, frag); 3646 return frag; 3647} 3648 3649/** 3650 * Convert a template node to a DocumentFragment. 3651 * 3652 * @param {Node} node 3653 * @return {DocumentFragment} 3654 */ 3655 3656function nodeToFragment(node) { 3657 // if its a template tag and the browser supports it, 3658 // its content is already a document fragment. However, iOS Safari has 3659 // bug when using directly cloned template content with touch 3660 // events and can cause crashes when the nodes are removed from DOM, so we 3661 // have to treat template elements as string templates. (#2805) 3662 /* istanbul ignore if */ 3663 if (isRealTemplate(node)) { 3664 return stringToFragment(node.innerHTML); 3665 } 3666 // script template 3667 if (node.tagName === 'SCRIPT') { 3668 return stringToFragment(node.textContent); 3669 } 3670 // normal node, clone it to avoid mutating the original 3671 var clonedNode = cloneNode(node); 3672 var frag = document.createDocumentFragment(); 3673 var child; 3674 /* eslint-disable no-cond-assign */ 3675 while (child = clonedNode.firstChild) { 3676 /* eslint-enable no-cond-assign */ 3677 frag.appendChild(child); 3678 } 3679 trimNode(frag); 3680 return frag; 3681} 3682 3683// Test for the presence of the Safari template cloning bug 3684// https://bugs.webkit.org/showug.cgi?id=137755 3685var hasBrokenTemplate = (function () { 3686 /* istanbul ignore else */ 3687 if (inBrowser) { 3688 var a = document.createElement('div'); 3689 a.innerHTML = '<template>1</template>'; 3690 return !a.cloneNode(true).firstChild.innerHTML; 3691 } else { 3692 return false;
3693 } 3694})(); 3695 3696// Test for IE10/11 textarea placeholder clone bug 3697var hasTextareaCloneBug = (function () { 3698 /* istanbul ignore else */ 3699 if (inBrowser) { 3700 var t = document.createElement('textarea'); 3701 t.placeholder = 't'; 3702 return t.cloneNode(true).value === 't'; 3703 } else { 3704 return false; 3705 } 3706})(); 3707 3708/** 3709 * 1. Deal with Safari cloning nested <template> bug by 3710 * manually cloning all template instances. 3711 * 2. Deal with IE10/11 textarea placeholder bug by setting 3712 * the correct value after cloning. 3713 * 3714 * @param {Element|DocumentFragment} node 3715 * @return {Element|DocumentFragment} 3716 */ 3717 3718function cloneNode(node) { 3719 /* istanbul ignore if */ 3720 if (!node.querySelectorAll) { 3721 return node.cloneNode(); 3722 } 3723 var res = node.cloneNode(true); 3724 var i, original, cloned; 3725 /* istanbul ignore if */ 3726 if (hasBrokenTemplate) { 3727 var tempClone = res; 3728 if (isRealTemplate(node)) { 3729 node = node.content; 3730 tempClone = res.content; 3731 } 3732 original = node.querySelectorAll('template'); 3733 if (original.length) { 3734 cloned = tempClone.querySelectorAll('template'); 3735 i = cloned.length; 3736 while (i--) { 3737 cloned[i].parentNode.replaceChild(cloneNode(original[i]), cloned[i]); 3738 } 3739 } 3740 } 3741 /* istanbul ignore if */ 3742 if (hasTextareaCloneBug) { 3743 if (node.tagName === 'TEXTAREA') { 3744 res.value = node.value; 3745 } else { 3746 original = node.querySelectorAll('textarea'); 3747 if (original.length) { 3748 cloned = res.querySelectorAll('textarea'); 3749 i = cloned.length; 3750 while (i--) { 3751 cloned[i].value = original[i].value; 3752 } 3753 } 3754 } 3755 } 3756 return res; 3757} 3758 3759/** 3760 * Process the template option and normalizes it into a 3761 * a DocumentFragment that can be used as a partial or a 3762 * instance template. 3763 * 3764 * @param {*} template 3765 * Possible values include: 3766 * - DocumentFragment object 3767 * - Node object of type Template 3768 * - id selector: '#some-template-id' 3769 * - template string: '<div><span>{{msg}}</span></div>' 3770 * @param {Boolean} shouldClone 3771 * @param {Boolean} raw 3772 * inline HTML interpolation. Do not check for id 3773 * selector and keep whitespace in the string. 3774 * @return {DocumentFragment|undefined} 3775 */ 3776 3777function parseTemplate(template, shouldClone, raw) { 3778 var node, frag; 3779 3780 // if the template is already a document fragment, 3781 // do nothing 3782 if (isFragment(template)) { 3783 trimNode(template); 3784 return shouldClone ? cloneNode(template) : template; 3785 } 3786 3787 if (typeof template === 'string') { 3788 // id selector 3789 if (!raw && template.charAt(0) === '#') { 3790 // id selector can be cached too 3791 frag = idSelectorCache.get(template); 3792 if (!frag) { 3793 node = document.getElementById(template.slice(1)); 3794 if (node) { 3795 frag = nodeToFragment(node); 3796 // save selector to cache 3797 idSelectorCache.put(template, frag); 3798 } 3799 } 3800 } else { 3801 // normal string template 3802 frag = stringToFragment(template, raw); 3803 } 3804 } else if (template.nodeType) { 3805 // a direct node 3806 frag = nodeToFragment(template); 3807 } 3808 3809 return frag && shouldClone ? cloneNode(frag) : frag; 3810} 3811 3812var template = Object.freeze({ 3813 cloneNode: cloneNode, 3814 parseTemplate: parseTemplate 3815}); 3816 3817var html = { 3818 3819 bind: function bind() { 3820 // a comment node means this is a binding for 3821 // {{{ inline unescaped html }}} 3822 if (this.el.nodeType === 8) { 3823 // hold nodes 3824 this.nodes = []; 3825 // replace the placeholder with proper anchor 3826 this.anchor = createAnchor('v-html'); 3827 replace(this.el, this.anchor); 3828 } 3829 }, 3830 3831 update: function update(value) { 3832 value = _toString(value); 3833 if (this.nodes) { 3834 this.swap(value); 3835 } else { 3836 this.el.innerHTML = value; 3837 } 3838 }, 3839 3840 swap: function swap(value) { 3841 // remove old nodes 3842 var i = this.nodes.length; 3843 while (i--) { 3844 remove(this.nodes[i]); 3845 } 3846 // convert new value to a fragment 3847 // do not attempt to retrieve from id selector 3848 var frag = parseTemplate(value, true, true); 3849 // save a reference to these nodes so we can remove later 3850 this.nodes = toArray(frag.childNodes); 3851 before(frag, this.anchor); 3852 } 3853}; 3854 3855/** 3856 * Abstraction for a partially-compiled fragment. 3857 * Can optionally compile content with a child scope. 3858 * 3859 * @param {Function} linker 3860 * @param {Vue} vm 3861 * @param {DocumentFragment} frag 3862 * @param {Vue} [host] 3863 * @param {Object} [scope] 3864 * @param {Fragment} [parentFrag] 3865 */ 3866function Fragment(linker, vm, frag, host, scope, parentFrag) { 3867 this.children = []; 3868 this.childFrags = []; 3869 this.vm = vm; 3870 this.scope = scope; 3871 this.inserted = false;
3872 this.parentFrag = parentFrag; 3873 if (parentFrag) { 3874 parentFrag.childFrags.push(this); 3875 } 3876 this.unlink = linker(vm, frag, host, scope, this); 3877 var single = this.single = frag.childNodes.length === 1 && 3878 // do not go single mode if the only node is an anchor 3879 !frag.childNodes[0].__v_anchor; 3880 if (single) { 3881 this.node = frag.childNodes[0]; 3882 this.before = singleBefore; 3883 this.remove = singleRemove; 3884 } else { 3885 this.node = createAnchor('fragment-start'); 3886 this.end = createAnchor('fragment-end'); 3887 this.frag = frag; 3888 prepend(this.node, frag); 3889 frag.appendChild(this.end); 3890 this.before = multiBefore; 3891 this.remove = multiRemove; 3892 } 3893 this.node.__v_frag = this; 3894} 3895 3896/** 3897 * Call attach/detach for all components contained within 3898 * this fragment. Also do so recursively for all child 3899 * fragments. 3900 * 3901 * @param {Function} hook 3902 */ 3903 3904Fragment.prototype.callHook = function (hook) { 3905 var i, l; 3906 for (i = 0, l = this.childFrags.length; i < l; i++) { 3907 this.childFrags[i].callHook(hook); 3908 } 3909 for (i = 0, l = this.children.length; i < l; i++) { 3910 hook(this.children[i]); 3911 } 3912}; 3913 3914/** 3915 * Insert fragment before target, single node version 3916 * 3917 * @param {Node} target 3918 * @param {Boolean} withTransition 3919 */ 3920 3921function singleBefore(target, withTransition) { 3922 this.inserted = true; 3923 var method = withTransition !== false ? beforeWithTransition : before; 3924 method(this.node, target, this.vm); 3925 if (inDoc(this.node)) { 3926 this.callHook(attach); 3927 } 3928} 3929 3930/** 3931 * Remove fragment, single node version 3932 */ 3933 3934function singleRemove() { 3935 this.inserted = false; 3936 var shouldCallRemove = inDoc(this.node); 3937 var self = this; 3938 this.beforeRemove(); 3939 removeWithTransition(this.node, this.vm, function () { 3940 if (shouldCallRemove) { 3941 self.callHook(detach); 3942 } 3943 self.destroy(); 3944 }); 3945} 3946 3947/** 3948 * Insert fragment before target, multi-nodes version 3949 * 3950 * @param {Node} target 3951 * @param {Boolean} withTransition 3952 */ 3953 3954function multiBefore(target, withTransition) { 3955 this.inserted = true; 3956 var vm = this.vm; 3957 var method = withTransition !== false ? beforeWithTransition : before; 3958 mapNodeRange(this.node, this.end, function (node) { 3959 method(node, target, vm); 3960 }); 3961 if (inDoc(this.node)) { 3962 this.callHook(attach); 3963 } 3964} 3965 3966/** 3967 * Remove fragment, multi-nodes version 3968 */ 3969 3970function multiRemove() { 3971 this.inserted = false; 3972 var self = this; 3973 var shouldCallRemove = inDoc(this.node); 3974 this.beforeRemove(); 3975 removeNodeRange(this.node, this.end, this.vm, this.frag, function () { 3976 if (shouldCallRemove) { 3977 self.callHook(detach); 3978 } 3979 self.destroy(); 3980 }); 3981} 3982 3983/** 3984 * Prepare the fragment for removal. 3985 */ 3986 3987Fragment.prototype.beforeRemove = function () { 3988 var i, l; 3989 for (i = 0, l = this.childFrags.length; i < l; i++) { 3990 // call the same method recursively on child 3991 // fragments, depth-first 3992 this.childFrags[i].beforeRemove(false); 3993 } 3994 for (i = 0, l = this.children.length; i < l; i++) { 3995 // Call destroy for all contained instances, 3996 // with remove:false and defer:true. 3997 // Defer is necessary because we need to 3998 // keep the children to call detach hooks 3999 // on them. 4000 this.children[i].$destroy(false, true); 4001 } 4002 var dirs = this.unlink.dirs; 4003 for (i = 0, l = dirs.length; i < l; i++) { 4004 // disable the watchers on all the directives 4005 // so that the rendered content stays the same 4006 // during removal. 4007 dirs[i]._watcher && dirs[i]._watcher.teardown(); 4008 } 4009}; 4010 4011/** 4012 * Destroy the fragment. 4013 */ 4014 4015Fragment.prototype.destroy = function () { 4016 if (this.parentFrag) { 4017 this.parentFrag.childFrags.$remove(this); 4018 } 4019 this.node.__v_frag = null; 4020 this.unlink(); 4021}; 4022 4023/** 4024 * Call attach hook for a Vue instance. 4025 * 4026 * @param {Vue} child 4027 */ 4028 4029function attach(child) { 4030 if (!child._isAttached && inDoc(child.$el)) { 4031 child._callHook('attached'); 4032 } 4033} 4034 4035/** 4036 * Call detach hook for a Vue instance. 4037 * 4038 * @param {Vue} child 4039 */ 4040 4041function detach(child) { 4042 if (child._isAttached && !inDoc(child.$el)) { 4043 child._callHook('detached'); 4044 } 4045} 4046 4047var linkerCache = new Cache(5000); 4048 4049/** 4050 * A factory that can be used to create instances of a 4051 * fragment. Caches the compiled linker if possible. 4052 * 4053 * @param {Vue} vm 4054 * @param {Element|String} el 4055 */ 4056function FragmentFactory(vm, el) { 4057 this.vm = vm; 4058 var template; 4059 var isString = typeof el === 'string'; 4060 if (isString || isTemplate(el) && !el.hasAttribute('v-if')) { 4061 template = parseTemplate(el, true); 4062 } else { 4063 template = document.createDocumentFragment(); 4064 template.appendChild(el); 4065 } 4066 this.template = template; 4067 // linker can be cached, but only for components 4068 var linker; 4069 var cid = vm.constructor.cid; 4070 if (cid > 0) { 4071 var cacheId = cid + (isString ? el : getOuterHTML(el)); 4072 linker = linkerCache.get(cacheId); 4073 if (!linker) { 4074 linker = compile(template, vm.$options, true); 4075 linkerCache.put(cacheId, linker); 4076 } 4077 } else { 4078 linker = compile(template, vm.$options, true); 4079 } 4080 this.linker = linker; 4081} 4082 4083/** 4084 * Create a fragment instance with given host and scope. 4085 * 4086 * @param {Vue} host 4087 * @param {Object} scope 4088 * @param {Fragment} parentFrag 4089 */ 4090 4091FragmentFactory.prototype.create = function (host, scope, parentFrag) { 4092 var frag = cloneNode(this.template); 4093 return new Fragment(this.linker, this.vm, frag, host, scope, parentFrag); 4094}; 4095 4096var ON = 700; 4097var MODEL = 800; 4098var BIND = 850; 4099var TRANSITION = 1100; 4100var EL = 1500; 4101var COMPONENT = 1500; 4102var PARTIAL = 1750; 4103var IF = 2100; 4104var FOR = 2200; 4105var SLOT = 2300; 4106 4107var uid$3 = 0; 4108 4109var vFor = { 4110 4111 priority: FOR, 4112 terminal: true, 4113 4114 params: ['track-by', 'stagger', 'enter-stagger', 'leave-stagger'], 4115 4116 bind: function bind() { 4117 if ('development' !== 'production' && this.el.hasAttribute('v-if')) { 4118 warn('<' + this.el.tagName.toLowerCase() + ' v-for="' + this.expression + '" v-if="' + this.el.getAttribute('v-if') + '">: ' + 'Using v-if and v-for on the same element is not recommended - ' + 'consider filtering the source Array instead.', this.vm); 4119 } 4120 4121 // support "item in/of items" syntax 4122 var inMatch = this.expression.match(/(.*) (?:in|of) (.*)/); 4123 if (inMatch) { 4124 var itMatch = inMatch[1].match(/\((.*),(.*)\)/); 4125 if (itMatch) { 4126 this.iterator = itMatch[1].trim(); 4127 this.alias = itMatch[2].trim(); 4128 } else { 4129 this.alias = inMatch[1].trim(); 4130 } 4131 this.expression = inMatch[2]; 4132 } 4133 4134 if (!this.alias) { 4135 'development' !== 'production' && warn('Invalid v-for expression "' + this.descriptor.raw + '": ' + 'alias is required.', this.vm); 4136 return; 4137 } 4138 4139 // uid as a cache identifier 4140 this.id = '__v-for__' + ++uid$3; 4141 4142 // check if this is an option list, 4143 // so that we know if we need to update the <select>'s 4144 // v-model when the option list has changed. 4145 // because v-model has a lower priority than v-for, 4146 // the v-model is not bound here yet, so we have to 4147 // retrive it in the actual updateModel() function. 4148 var tag = this.el.tagName; 4149 this.isOption = (tag === 'OPTION' || tag === 'OPTGROUP') && this.el.parentNode.tagName === 'SELECT'; 4150 4151 // setup anchor nodes 4152 this.start = createAnchor('v-for-start'); 4153 this.end = createAnchor('v-for-end'); 4154 replace(this.el, this.end); 4155 before(this.start, this.end); 4156 4157 // cache 4158 this.cache = Object.create(null); 4159 4160 // fragment factory 4161 this.factory = new FragmentFactory(this.vm, this.el); 4162 }, 4163 4164 update: function update(data) { 4165 this.diff(data); 4166 this.updateRef(); 4167 this.updateModel(); 4168 }, 4169 4170 /** 4171 * Diff, based on new data and old data, determine the 4172 * minimum amount of DOM manipulations needed to make the 4173 * DOM reflect the new data Array. 4174 * 4175 * The algorithm diffs the new data Array by storing a 4176 * hidden reference to an owner vm instance on previously 4177 * seen data. This allows us to achieve O(n) which is 4178 * better than a levenshtein distance based algorithm, 4179 * which is O(m * n). 4180 * 4181 * @param {Array} data 4182 */ 4183 4184 diff: function diff(data) { 4185 // check if the Array was converted from an Object 4186 var item = data[0]; 4187 var convertedFromObject = this.fromObject = isObject(item) && hasOwn(item, '$key') && hasOwn(item, '$value'); 4188 4189 var trackByKey = this.params.trackBy; 4190 var oldFrags = this.frags; 4191 var frags = this.frags = new Array(data.length); 4192 var alias = this.alias; 4193 var iterator = this.iterator; 4194 var start = this.start; 4195 var end = this.end; 4196 var inDocument = inDoc(start); 4197 var init = !oldFrags; 4198 var i, l, frag, key, value, primitive; 4199 4200 // First pass, go through the new Array and fill up 4201 // the new frags array. If a piece of data has a cached 4202 // instance for it, we reuse it. Otherwise build a new 4203 // instance. 4204 for (i = 0, l = data.length; i < l; i++) { 4205 item = data[i]; 4206 key = convertedFromObject ? item.$key : null; 4207 value = convertedFromObject ? item.$value : item; 4208 primitive = !isObject(value); 4209 frag = !init && this.getCachedFrag(value, i, key); 4210 if (frag) { 4211 // reusable fragment 4212 frag.reused = true; 4213 // update $index 4214 frag.scope.$index = i; 4215 // update $key 4216 if (key) { 4217 frag.scope.$key = key; 4218 } 4219 // update iterator 4220 if (iterator) { 4221 frag.scope[iterator] = key !== null ? key : i; 4222 } 4223 // update data for track-by, object repeat & 4224 // primitive values. 4225 if (trackByKey || convertedFromObject || primitive) { 4226 withoutConversion(function () { 4227 frag.scope[alias] = value; 4228 }); 4229 } 4230 } else { 4231 // new instance 4232 frag = this.create(value, alias, i, key); 4233 frag.fresh = !init; 4234 } 4235 frags[i] = frag; 4236 if (init) { 4237 frag.before(end); 4238 } 4239 } 4240 4241 // we're done for the initial render. 4242 if (init) { 4243 return; 4244 } 4245 4246 // Second pass, go through the old fragments and 4247 // destroy those who are not reused (and remove them 4248 // from cache) 4249 var removalIndex = 0; 4250 var totalRemoved = oldFrags.length - frags.length; 4251 // when removing a large number of fragments, watcher removal 4252 // turns out to be a perf bottleneck, so we batch the watcher 4253 // removals into a single filter call! 4254 this.vm._vForRemoving = true; 4255 for (i = 0, l = oldFrags.length; i < l; i++) { 4256 frag = oldFrags[i]; 4257 if (!frag.reused) {
4258 this.deleteCachedFrag(frag); 4259 this.remove(frag, removalIndex++, totalRemoved, inDocument); 4260 } 4261 } 4262 this.vm._vForRemoving = false; 4263 if (removalIndex) { 4264 this.vm._watchers = this.vm._watchers.filter(function (w) { 4265 return w.active; 4266 }); 4267 } 4268 4269 // Final pass, move/insert new fragments into the 4270 // right place. 4271 var targetPrev, prevEl, currentPrev; 4272 var insertionIndex = 0; 4273 for (i = 0, l = frags.length; i < l; i++) { 4274 frag = frags[i]; 4275 // this is the frag that we should be after 4276 targetPrev = frags[i - 1]; 4277 prevEl = targetPrev ? targetPrev.staggerCb ? targetPrev.staggerAnchor : targetPrev.end || targetPrev.node : start; 4278 if (frag.reused && !frag.staggerCb) { 4279 currentPrev = findPrevFrag(frag, start, this.id); 4280 if (currentPrev !== targetPrev && (!currentPrev || 4281 // optimization for moving a single item. 4282 // thanks to suggestions by @livoras in #1807 4283 findPrevFrag(currentPrev, start, this.id) !== targetPrev)) { 4284 this.move(frag, prevEl); 4285 } 4286 } else { 4287 // new instance, or still in stagger. 4288 // insert with updated stagger index. 4289 this.insert(frag, insertionIndex++, prevEl, inDocument); 4290 } 4291 frag.reused = frag.fresh = false; 4292 } 4293 }, 4294 4295 /** 4296 * Create a new fragment instance. 4297 * 4298 * @param {*} value 4299 * @param {String} alias 4300 * @param {Number} index 4301 * @param {String} [key] 4302 * @return {Fragment} 4303 */ 4304 4305 create: function create(value, alias, index, key) { 4306 var host = this._host; 4307 // create iteration scope 4308 var parentScope = this._scope || this.vm; 4309 var scope = Object.create(parentScope); 4310 // ref holder for the scope 4311 scope.$refs = Object.create(parentScope.$refs); 4312 scope.$els = Object.create(parentScope.$els); 4313 // make sure point $parent to parent scope 4314 scope.$parent = parentScope; 4315 // for two-way binding on alias 4316 scope.$forContext = this; 4317 // define scope properties 4318 // important: define the scope alias without forced conversion 4319 // so that frozen data structures remain non-reactive. 4320 withoutConversion(function () { 4321 defineReactive(scope, alias, value); 4322 }); 4323 defineReactive(scope, '$index', index); 4324 if (key) { 4325 defineReactive(scope, '$key', key); 4326 } else if (scope.$key) { 4327 // avoid accidental fallback 4328 def(scope, '$key', null); 4329 } 4330 if (this.iterator) { 4331 defineReactive(scope, this.iterator, key !== null ? key : index); 4332 } 4333 var frag = this.factory.create(host, scope, this._frag); 4334 frag.forId = this.id; 4335 this.cacheFrag(value, frag, index, key); 4336 return frag; 4337 }, 4338 4339 /** 4340 * Update the v-ref on owner vm. 4341 */ 4342 4343 updateRef: function updateRef() { 4344 var ref = this.descriptor.ref; 4345 if (!ref) return; 4346 var hash = (this._scope || this.vm).$refs; 4347 var refs; 4348 if (!this.fromObject) { 4349 refs = this.frags.map(findVmFromFrag); 4350 } else { 4351 refs = {};
4352 this.frags.forEach(function (frag) { 4353 refs[frag.scope.$key] = findVmFromFrag(frag); 4354 }); 4355 } 4356 hash[ref] = refs; 4357 }, 4358 4359 /** 4360 * For option lists, update the containing v-model on 4361 * parent <select>. 4362 */ 4363 4364 updateModel: function updateModel() { 4365 if (this.isOption) { 4366 var parent = this.start.parentNode; 4367 var model = parent && parent.__v_model; 4368 if (model) { 4369 model.forceUpdate(); 4370 } 4371 } 4372 }, 4373 4374 /** 4375 * Insert a fragment. Handles staggering. 4376 * 4377 * @param {Fragment} frag 4378 * @param {Number} index 4379 * @param {Node} prevEl 4380 * @param {Boolean} inDocument 4381 */ 4382 4383 insert: function insert(frag, index, prevEl, inDocument) { 4384 if (frag.staggerCb) { 4385 frag.staggerCb.cancel(); 4386 frag.staggerCb = null; 4387 } 4388 var staggerAmount = this.getStagger(frag, index, null, 'enter'); 4389 if (inDocument && staggerAmount) { 4390 // create an anchor and insert it synchronously, 4391 // so that we can resolve the correct order without 4392 // worrying about some elements not inserted yet 4393 var anchor = frag.staggerAnchor; 4394 if (!anchor) { 4395 anchor = frag.staggerAnchor = createAnchor('stagger-anchor'); 4396 anchor.__v_frag = frag; 4397 } 4398 after(anchor, prevEl); 4399 var op = frag.staggerCb = cancellable(function () { 4400 frag.staggerCb = null; 4401 frag.before(anchor); 4402 remove(anchor); 4403 }); 4404 setTimeout(op, staggerAmount); 4405 } else { 4406 var target = prevEl.nextSibling; 4407 /* istanbul ignore if */ 4408 if (!target) { 4409 // reset end anchor position in case the position was messed up 4410 // by an external drag-n-drop library. 4411 after(this.end, prevEl); 4412 target = this.end; 4413 } 4414 frag.before(target); 4415 } 4416 }, 4417 4418 /** 4419 * Remove a fragment. Handles staggering. 4420 * 4421 * @param {Fragment} frag 4422 * @param {Number} index 4423 * @param {Number} total 4424 * @param {Boolean} inDocument 4425 */ 4426 4427 remove: function remove(frag, index, total, inDocument) { 4428 if (frag.staggerCb) { 4429 frag.staggerCb.cancel(); 4430 frag.staggerCb = null; 4431 // it's not possible for the same frag to be removed 4432 // twice, so if we have a pending stagger callback, 4433 // it means this frag is queued for enter but removed 4434 // before its transition started. Since it is already 4435 // destroyed, we can just leave it in detached state. 4436 return; 4437 } 4438 var staggerAmount = this.getStagger(frag, index, total, 'leave'); 4439 if (inDocument && staggerAmount) { 4440 var op = frag.staggerCb = cancellable(function () { 4441 frag.staggerCb = null; 4442 frag.remove(); 4443 }); 4444 setTimeout(op, staggerAmount); 4445 } else { 4446 frag.remove(); 4447 } 4448 }, 4449 4450 /** 4451 * Move a fragment to a new position. 4452 * Force no transition. 4453 * 4454 * @param {Fragment} frag 4455 * @param {Node} prevEl 4456 */ 4457 4458 move: function move(frag, prevEl) { 4459 // fix a common issue with Sortable: 4460 // if prevEl doesn't have nextSibling, this means it's 4461 // been dragged after the end anchor. Just re-position 4462 // the end anchor to the end of the container. 4463 /* istanbul ignore if */ 4464 if (!prevEl.nextSibling) { 4465 this.end.parentNode.appendChild(this.end); 4466 } 4467 frag.before(prevEl.nextSibling, false); 4468 }, 4469 4470 /** 4471 * Cache a fragment using track-by or the object key. 4472 * 4473 * @param {*} value 4474 * @param {Fragment} frag 4475 * @param {Number} index 4476 * @param {String} [key] 4477 */ 4478 4479 cacheFrag: function cacheFrag(value, frag, index, key) { 4480 var trackByKey = this.params.trackBy; 4481 var cache = this.cache; 4482 var primitive = !isObject(value); 4483 var id; 4484 if (key || trackByKey || primitive) { 4485 id = getTrackByKey(index, key, value, trackByKey); 4486 if (!cache[id]) { 4487 cache[id] = frag; 4488 } else if (trackByKey !== '$index') { 4489 'development' !== 'production' && this.warnDuplicate(value); 4490 } 4491 } else { 4492 id = this.id; 4493 if (hasOwn(value, id)) { 4494 if (value[id] === null) { 4495 value[id] = frag; 4496 } else { 4497 'development' !== 'production' && this.warnDuplicate(value); 4498 } 4499 } else if (Object.isExtensible(value)) { 4500 def(value, id, frag); 4501 } else if ('development' !== 'production') { 4502 warn('Frozen v-for objects cannot be automatically tracked, make sure to ' + 'provide a track-by key.'); 4503 } 4504 } 4505 frag.raw = value; 4506 }, 4507 4508 /** 4509 * Get a cached fragment from the value/index/key 4510 * 4511 * @param {*} value 4512 * @param {Number} index 4513 * @param {String} key 4514 * @return {Fragment} 4515 */ 4516 4517 getCachedFrag: function getCachedFrag(value, index, key) { 4518 var trackByKey = this.params.trackBy; 4519 var primitive = !isObject(value); 4520 var frag; 4521 if (key || trackByKey || primitive) { 4522 var id = getTrackByKey(index, key, value, trackByKey); 4523 frag = this.cache[id]; 4524 } else { 4525 frag = value[this.id]; 4526 } 4527 if (frag && (frag.reused || frag.fresh)) { 4528 'development' !== 'production' && this.warnDuplicate(value); 4529 } 4530 return frag; 4531 }, 4532 4533 /** 4534 * Delete a fragment from cache. 4535 * 4536 * @param {Fragment} frag 4537 */ 4538
4539 deleteCachedFrag: function deleteCachedFrag(frag) { 4540 var value = frag.raw; 4541 var trackByKey = this.params.trackBy; 4542 var scope = frag.scope; 4543 var index = scope.$index; 4544 // fix #948: avoid accidentally fall through to 4545 // a parent repeater which happens to have $key. 4546 var key = hasOwn(scope, '$key') && scope.$key; 4547 var primitive = !isObject(value); 4548 if (trackByKey || key || primitive) { 4549 var id = getTrackByKey(index, key, value, trackByKey); 4550 this.cache[id] = null; 4551 } else { 4552 value[this.id] = null; 4553 frag.raw = null; 4554 } 4555 }, 4556 4557 /** 4558 * Get the stagger amount for an insertion/removal. 4559 * 4560 * @param {Fragment} frag 4561 * @param {Number} index 4562 * @param {Number} total 4563 * @param {String} type 4564 */ 4565 4566 getStagger: function getStagger(frag, index, total, type) { 4567 type = type + 'Stagger'; 4568 var trans = frag.node.__v_trans; 4569 var hooks = trans && trans.hooks; 4570 var hook = hooks && (hooks[type] || hooks.stagger); 4571 return hook ? hook.call(frag, index, total) : index * parseInt(this.params[type] || this.params.stagger, 10); 4572 }, 4573 4574 /** 4575 * Pre-process the value before piping it through the 4576 * filters. This is passed to and called by the watcher. 4577 */ 4578 4579 _preProcess: function _preProcess(value) { 4580 // regardless of type, store the un-filtered raw value. 4581 this.rawValue = value; 4582 return value; 4583 }, 4584 4585 /** 4586 * Post-process the value after it has been piped through 4587 * the filters. This is passed to and called by the watcher. 4588 * 4589 * It is necessary for this to be called during the 4590 * watcher's dependency collection phase because we want 4591 * the v-for to update when the source Object is mutated. 4592 */ 4593 4594 _postProcess: function _postProcess(value) { 4595 if (isArray(value)) { 4596 return value; 4597 } else if (isPlainObject(value)) { 4598 // convert plain object to array. 4599 var keys = Object.keys(value); 4600 var i = keys.length; 4601 var res = new Array(i); 4602 var key; 4603 while (i--) { 4604 key = keys[i]; 4605 res[i] = { 4606 $key: key, 4607 $value: value[key] 4608 }; 4609 } 4610 return res; 4611 } else { 4612 if (typeof value === 'number' && !isNaN(value)) { 4613 value = range(value); 4614 } 4615 return value || []; 4616 } 4617 }, 4618 4619 unbind: function unbind() { 4620 if (this.descriptor.ref) { 4621 (this._scope || this.vm).$refs[this.descriptor.ref] = null; 4622 } 4623 if (this.frags) { 4624 var i = this.frags.length; 4625 var frag; 4626 while (i--) { 4627 frag = this.frags[i]; 4628 this.deleteCachedFrag(frag); 4629 frag.destroy(); 4630 } 4631 } 4632 } 4633}; 4634 4635/** 4636 * Helper to find the previous element that is a fragment 4637 * anchor. This is necessary because a destroyed frag's 4638 * element could still be lingering in the DOM before its 4639 * leaving transition finishes, but its inserted flag 4640 * should have been set to false so we can skip them. 4641 * 4642 * If this is a block repeat, we want to make sure we only 4643 * return frag that is bound to this v-for. (see #929) 4644 * 4645 * @param {Fragment} frag 4646 * @param {Comment|Text} anchor 4647 * @param {String} id 4648 * @return {Fragment} 4649 */ 4650 4651function findPrevFrag(frag, anchor, id) { 4652 var el = frag.node.previousSibling; 4653 /* istanbul ignore if */ 4654 if (!el) return; 4655 frag = el.__v_frag; 4656 while ((!frag || frag.forId !== id || !frag.inserted) && el !== anchor) { 4657 el = el.previousSibling; 4658 /* istanbul ignore if */ 4659 if (!el) return; 4660 frag = el.__v_frag; 4661 } 4662 return frag; 4663} 4664 4665/** 4666 * Create a range array from given number. 4667 * 4668 * @param {Number} n 4669 * @return {Array} 4670 */ 4671 4672function range(n) { 4673 var i = -1; 4674 var ret = new Array(Math.floor(n)); 4675 while (++i < n) { 4676 ret[i] = i; 4677 } 4678 return ret; 4679} 4680 4681/** 4682 * Get the track by key for an item. 4683 * 4684 * @param {Number} index 4685 * @param {String} key 4686 * @param {*} value 4687 * @param {String} [trackByKey] 4688 */ 4689 4690function getTrackByKey(index, key, value, trackByKey) { 4691 return trackByKey ? trackByKey === '$index' ? index : trackByKey.charAt(0).match(/\w/) ? getPath(value, trackByKey) : value[trackByKey] : key || value; 4692} 4693 4694if ('development' !== 'production') { 4695 vFor.warnDuplicate = function (value) { 4696 warn('Duplicate value found in v-for="' + this.descriptor.raw + '": ' + JSON.stringify(value) + '. Use track-by="$index" if ' + 'you are expecting duplicate values.', this.vm); 4697 }; 4698} 4699 4700/** 4701 * Find a vm from a fragment. 4702 * 4703 * @param {Fragment} frag 4704 * @return {Vue|undefined} 4705 */ 4706 4707function findVmFromFrag(frag) { 4708 var node = frag.node;
4709 // handle multi-node frag 4710 if (frag.end) { 4711 while (!node.__vue__ && node !== frag.end && node.nextSibling) { 4712 node = node.nextSibling; 4713 } 4714 } 4715 return node.__vue__; 4716} 4717 4718var vIf = { 4719 4720 priority: IF, 4721 terminal: true, 4722 4723 bind: function bind() { 4724 var el = this.el; 4725 if (!el.__vue__) { 4726 // check else block 4727 var next = el.nextElementSibling; 4728 if (next && getAttr(next, 'v-else') !== null) { 4729 remove(next); 4730 this.elseEl = next; 4731 } 4732 // check main block 4733 this.anchor = createAnchor('v-if'); 4734 replace(el, this.anchor); 4735 } else { 4736 'development' !== 'production' && warn('v-if="' + this.expression + '" cannot be ' + 'used on an instance root element.', this.vm); 4737 this.invalid = true; 4738 } 4739 }, 4740 4741 update: function update(value) { 4742 if (this.invalid) return; 4743 if (value) { 4744 if (!this.frag) { 4745 this.insert(); 4746 } 4747 } else { 4748 this.remove(); 4749 } 4750 }, 4751 4752 insert: function insert() { 4753 if (this.elseFrag) { 4754 this.elseFrag.remove(); 4755 this.elseFrag = null; 4756 } 4757 // lazy init factory 4758 if (!this.factory) { 4759 this.factory = new FragmentFactory(this.vm, this.el); 4760 } 4761 this.frag = this.factory.create(this._host, this._scope, this._frag); 4762 this.frag.before(this.anchor); 4763 }, 4764 4765 remove: function remove() { 4766 if (this.frag) { 4767 this.frag.remove(); 4768 this.frag = null; 4769 } 4770 if (this.elseEl && !this.elseFrag) { 4771 if (!this.elseFactory) { 4772 this.elseFactory = new FragmentFactory(this.elseEl._context || this.vm, this.elseEl); 4773 } 4774 this.elseFrag = this.elseFactory.create(this._host, this._scope, this._frag); 4775 this.elseFrag.before(this.anchor); 4776 } 4777 }, 4778 4779 unbind: function unbind() { 4780 if (this.frag) { 4781 this.frag.destroy(); 4782 } 4783 if (this.elseFrag) { 4784 this.elseFrag.destroy(); 4785 } 4786 } 4787}; 4788 4789var show = { 4790 4791 bind: function bind() { 4792 // check else block 4793 var next = this.el.nextElementSibling; 4794 if (next && getAttr(next, 'v-else') !== null) { 4795 this.elseEl = next; 4796 } 4797 }, 4798 4799 update: function update(value) { 4800 this.apply(this.el, value); 4801 if (this.elseEl) { 4802 this.apply(this.elseEl, !value); 4803 } 4804 }, 4805 4806 apply: function apply(el, value) { 4807 if (inDoc(el)) { 4808 applyTransition(el, value ? 1 : -1, toggle, this.vm); 4809 } else { 4810 toggle(); 4811 } 4812 function toggle() { 4813 el.style.display = value ? '' : 'none'; 4814 } 4815 } 4816}; 4817 4818var text$2 = { 4819 4820 bind: function bind() { 4821 var self = this; 4822 var el = this.el; 4823 var isRange = el.type === 'range'; 4824 var lazy = this.params.lazy; 4825 var number = this.params.number; 4826 var debounce = this.params.debounce; 4827 4828 // handle composition events. 4829 // http://blog.evanyou.me/2014/01/03/composition-event/ 4830 // skip this for Android because it handles composition 4831 // events quite differently. Android doesn't trigger 4832 // composition events for language input methods e.g. 4833 // Chinese, but instead triggers them for spelling 4834 // suggestions... (see Discussion/#162) 4835 var composing = false; 4836 if (!isAndroid && !isRange) { 4837 this.on('compositionstart', function () { 4838 composing = true; 4839 }); 4840 this.on('compositionend', function () { 4841 composing = false; 4842 // in IE11 the "compositionend" event fires AFTER 4843 // the "input" event, so the input handler is blocked 4844 // at the end... have to call it here. 4845 // 4846 // #1327: in lazy mode this is unecessary. 4847 if (!lazy) { 4848 self.listener(); 4849 } 4850 }); 4851 } 4852 4853 // prevent messing with the input when user is typing, 4854 // and force update on blur. 4855 this.focused = false; 4856 if (!isRange && !lazy) { 4857 this.on('focus', function () { 4858 self.focused = true; 4859 }); 4860 this.on('blur', function () { 4861 self.focused = false; 4862 // do not sync value after fragment removal (#2017) 4863 if (!self._frag || self._frag.inserted) { 4864 self.rawListener(); 4865 } 4866 }); 4867 } 4868 4869 // Now attach the main listener 4870 this.listener = this.rawListener = function () { 4871 if (composing || !self._bound) { 4872 return; 4873 } 4874 var val = number || isRange ? toNumber(el.value) : el.value; 4875 self.set(val); 4876 // force update on next tick to avoid lock & same value 4877 // also only update when user is not typing 4878 nextTick(function () { 4879 if (self._bound && !self.focused) { 4880 self.update(self._watcher.value); 4881 } 4882 }); 4883 }; 4884 4885 // apply debounce 4886 if (debounce) { 4887 this.listener = _debounce(this.listener, debounce); 4888 } 4889
4890 // Support jQuery events, since jQuery.trigger() doesn't 4891 // trigger native events in some cases and some plugins 4892 // rely on $.trigger() 4893 // 4894 // We want to make sure if a listener is attached using 4895 // jQuery, it is also removed with jQuery, that's why 4896 // we do the check for each directive instance and 4897 // store that check result on itself. This also allows 4898 // easier test coverage control by unsetting the global 4899 // jQuery variable in tests. 4900 this.hasjQuery = typeof jQuery === 'function'; 4901 if (this.hasjQuery) { 4902 var method = jQuery.fn.on ? 'on' : 'bind'; 4903 jQuery(el)[method]('change', this.rawListener); 4904 if (!lazy) { 4905 jQuery(el)[method]('input', this.listener); 4906 } 4907 } else { 4908 this.on('change', this.rawListener); 4909 if (!lazy) { 4910 this.on('input', this.listener); 4911 } 4912 } 4913 4914 // IE9 doesn't fire input event on backspace/del/cut 4915 if (!lazy && isIE9) { 4916 this.on('cut', function () { 4917 nextTick(self.listener); 4918 }); 4919 this.on('keyup', function (e) { 4920 if (e.keyCode === 46 || e.keyCode === 8) { 4921 self.listener(); 4922 } 4923 }); 4924 } 4925 4926 // set initial value if present 4927 if (el.hasAttribute('value') || el.tagName === 'TEXTAREA' && el.value.trim()) { 4928 this.afterBind = this.listener; 4929 } 4930 }, 4931 4932 update: function update(value) { 4933 // #3029 only update when the value changes. This prevent 4934 // browsers from overwriting values like selectionStart 4935 value = _toString(value); 4936 if (value !== this.el.value) this.el.value = value; 4937 }, 4938 4939 unbind: function unbind() { 4940 var el = this.el; 4941 if (this.hasjQuery) { 4942 var method = jQuery.fn.off ? 'off' : 'unbind'; 4943 jQuery(el)[method]('change', this.listener); 4944 jQuery(el)[method]('input', this.listener); 4945 } 4946 } 4947}; 4948 4949var radio = { 4950 4951 bind: function bind() { 4952 var self = this; 4953 var el = this.el; 4954 4955 this.getValue = function () { 4956 // value overwrite via v-bind:value 4957 if (el.hasOwnProperty('_value')) { 4958 return el._value; 4959 } 4960 var val = el.value; 4961 if (self.params.number) { 4962 val = toNumber(val); 4963 } 4964 return val; 4965 }; 4966 4967 this.listener = function () { 4968 self.set(self.getValue()); 4969 }; 4970 this.on('change', this.listener); 4971 4972 if (el.hasAttribute('checked')) { 4973 this.afterBind = this.listener; 4974 } 4975 }, 4976 4977 update: function update(value) { 4978 this.el.checked = looseEqual(value, this.getValue()); 4979 } 4980}; 4981 4982var select = { 4983 4984 bind: function bind() { 4985 var _this = this; 4986 4987 var self = this; 4988 var el = this.el; 4989 4990 // method to force update DOM using latest value. 4991 this.forceUpdate = function () { 4992 if (self._watcher) { 4993 self.update(self._watcher.get()); 4994 } 4995 }; 4996 4997 // check if this is a multiple select 4998 var multiple = this.multiple = el.hasAttribute('multiple'); 4999 5000 // attach listener 5001 this.listener = function () { 5002 var value = getValue(el, multiple); 5003 value = self.params.number ? isArray(value) ? value.map(toNumber) : toNumber(value) : value; 5004 self.set(value); 5005 }; 5006 this.on('change', this.listener); 5007 5008 // if has initial value, set afterBind 5009 var initValue = getValue(el, multiple, true); 5010 if (multiple && initValue.length || !multiple && initValue !== null) { 5011 this.afterBind = this.listener; 5012 } 5013 5014 // All major browsers except Firefox resets 5015 // selectedIndex with value -1 to 0 when the element 5016 // is appended to a new parent, therefore we have to 5017 // force a DOM update whenever that happens... 5018 this.vm.$on('hook:attached', function () { 5019 nextTick(_this.forceUpdate); 5020 }); 5021 if (!inDoc(el)) { 5022 nextTick(this.forceUpdate); 5023 } 5024 }, 5025 5026 update: function update(value) { 5027 var el = this.el; 5028 el.selectedIndex = -1; 5029 var multi = this.multiple && isArray(value); 5030 var options = el.options; 5031 var i = options.length; 5032 var op, val; 5033 while (i--) { 5034 op = options[i]; 5035 val = op.hasOwnProperty('_value') ? op._value : op.value; 5036 /* eslint-disable eqeqeq */ 5037 op.selected = multi ? indexOf$1(value, val) > -1 : looseEqual(value, val); 5038 /* eslint-enable eqeqeq */ 5039 } 5040 }, 5041 5042 unbind: function unbind() { 5043 /* istanbul ignore next */ 5044 this.vm.$off('hook:attached', this.forceUpdate); 5045 } 5046}; 5047 5048/** 5049 * Get select value 5050 * 5051 * @param {SelectElement} el 5052 * @param {Boolean} multi 5053 * @param {Boolean} init 5054 * @return {Array|*} 5055 */ 5056 5057function getValue(el, multi, init) { 5058 var res = multi ? [] : null; 5059 var op, val, selected; 5060 for (var i = 0, l = el.options.length; i < l; i++) { 5061 op = el.options[i]; 5062 selected = init ? op.hasAttribute('selected') : op.selected; 5063 if (selected) { 5064 val = op.hasOwnProperty('_value') ? op._value : op.value; 5065 if (multi) { 5066 res.push(val); 5067 } else { 5068 return val; 5069 } 5070 } 5071 } 5072 return res; 5073} 5074 5075/** 5076 * Native Array.indexOf uses strict equal, but in this 5077 * case we need to match string/numbers with custom equal. 5078 * 5079 * @param {Array} arr 5080 * @param {*} val 5081 */ 5082 5083function indexOf$1(arr, val) { 5084 var i = arr.length; 5085 while (i--) { 5086 if (looseEqual(arr[i], val)) { 5087 return i; 5088 } 5089 } 5090 return -1; 5091} 5092 5093var checkbox = { 5094 5095 bind: function bind() { 5096 var self = this; 5097 var el = this.el; 5098 5099 this.getValue = function () { 5100 return el.hasOwnProperty('_value') ? el._value : self.params.number ? toNumber(el.value) : el.value; 5101 }; 5102 5103 function getBooleanValue() { 5104 var val = el.checked; 5105 if (val && el.hasOwnProperty('_trueValue')) { 5106 return el._trueValue; 5107 } 5108 if (!val && el.hasOwnProperty('_falseValue')) { 5109 return el._falseValue; 5110 } 5111 return val; 5112 } 5113 5114 this.listener = function () { 5115 var model = self._watcher.get(); 5116 if (isArray(model)) { 5117 var val = self.getValue(); 5118 var i = indexOf(model, val); 5119 if (el.checked) { 5120 if (i < 0) { 5121 self.set(model.concat(val)); 5122 } 5123 } else if (i > -1) { 5124 self.set(model.slice(0, i).concat(model.slice(i + 1))); 5125 } 5126 } else { 5127 self.set(getBooleanValue()); 5128 } 5129 }; 5130 5131 this.on('change', this.listener); 5132 if (el.hasAttribute('checked')) { 5133 this.afterBind = this.listener; 5134 } 5135 }, 5136 5137 update: function update(value) { 5138 var el = this.el; 5139 if (isArray(value)) { 5140 el.checked = indexOf(value, this.getValue()) > -1; 5141 } else { 5142 if (el.hasOwnProperty('_trueValue')) { 5143 el.checked = looseEqual(value, el._trueValue); 5144 } else { 5145 el.checked = !!value; 5146 } 5147 } 5148 } 5149}; 5150 5151var handlers = { 5152 text: text$2, 5153 radio: radio, 5154 select: select, 5155 checkbox: checkbox 5156}; 5157 5158var model = { 5159 5160 priority: MODEL, 5161 twoWay: true, 5162 handlers: handlers, 5163 params: ['lazy', 'number', 'debounce'], 5164 5165 /** 5166 * Possible elements: 5167 * <select> 5168 * <textarea> 5169 * <input type="*"> 5170 * - text 5171 * - checkbox 5172 * - radio 5173 * - number 5174 */ 5175 5176 bind: function bind() { 5177 // friendly warning... 5178 this.checkFilters(); 5179 if (this.hasRead && !this.hasWrite) { 5180 'development' !== 'production' && warn('It seems you are using a read-only filter with ' + 'v-model="' + this.descriptor.raw + '". ' + 'You might want to use a two-way filter to ensure correct behavior.', this.vm); 5181 } 5182 var el = this.el; 5183 var tag = el.tagName; 5184 var handler; 5185 if (tag === 'INPUT') { 5186 handler = handlers[el.type] || handlers.text; 5187 } else if (tag === 'SELECT') { 5188 handler = handlers.select; 5189 } else if (tag === 'TEXTAREA') { 5190 handler = handlers.text; 5191 } else { 5192 'development' !== 'production' && warn('v-model does not support element type: ' + tag, this.vm); 5193 return; 5194 } 5195 el.__v_model = this; 5196 handler.bind.call(this); 5197 this.update = handler.update; 5198 this._unbind = handler.unbind; 5199 }, 5200 5201 /** 5202 * Check read/write filter stats. 5203 */ 5204 5205 checkFilters: function checkFilters() { 5206 var filters = this.filters; 5207 if (!filters) return; 5208 var i = filters.length; 5209 while (i--) { 5210 var filter = resolveAsset(this.vm.$options, 'filters', filters[i].name); 5211 if (typeof filter === 'function' || filter.read) { 5212 this.hasRead = true; 5213 } 5214 if (filter.write) { 5215 this.hasWrite = true; 5216 } 5217 } 5218 }, 5219 5220 unbind: function unbind() { 5221 this.el.__v_model = null; 5222 this._unbind && this._unbind(); 5223 } 5224}; 5225 5226// keyCode aliases 5227var keyCodes = { 5228 esc: 27, 5229 tab: 9,
5230 enter: 13, 5231 space: 32, 5232 'delete': [8, 46], 5233 up: 38, 5234 left: 37, 5235 right: 39, 5236 down: 40 5237}; 5238 5239function keyFilter(handler, keys) { 5240 var codes = keys.map(function (key) { 5241 var charCode = key.charCodeAt(0); 5242 if (charCode > 47 && charCode < 58) { 5243 return parseInt(key, 10); 5244 } 5245 if (key.length === 1) { 5246 charCode = key.toUpperCase().charCodeAt(0); 5247 if (charCode > 64 && charCode < 91) { 5248 return charCode; 5249 } 5250 } 5251 return keyCodes[key]; 5252 }); 5253 codes = [].concat.apply([], codes); 5254 return function keyHandler(e) { 5255 if (codes.indexOf(e.keyCode) > -1) { 5256 return handler.call(this, e); 5257 } 5258 }; 5259} 5260 5261function stopFilter(handler) { 5262 return function stopHandler(e) { 5263 e.stopPropagation(); 5264 return handler.call(this, e); 5265 }; 5266} 5267 5268function preventFilter(handler) { 5269 return function preventHandler(e) { 5270 e.preventDefault(); 5271 return handler.call(this, e); 5272 }; 5273} 5274 5275function selfFilter(handler) { 5276 return function selfHandler(e) { 5277 if (e.target === e.currentTarget) { 5278 return handler.call(this, e); 5279 } 5280 }; 5281} 5282 5283var on$1 = { 5284 5285 priority: ON, 5286 acceptStatement: true, 5287 keyCodes: keyCodes, 5288 5289 bind: function bind() { 5290 // deal with iframes 5291 if (this.el.tagName === 'IFRAME' && this.arg !== 'load') { 5292 var self = this; 5293 this.iframeBind = function () { 5294 on(self.el.contentWindow, self.arg, self.handler, self.modifiers.capture); 5295 }; 5296 this.on('load', this.iframeBind); 5297 } 5298 }, 5299 5300 update: function update(handler) { 5301 // stub a noop for v-on with no value, 5302 // e.g. @mousedown.prevent 5303 if (!this.descriptor.raw) { 5304 handler = function () {}; 5305 } 5306 5307 if (typeof handler !== 'function') { 5308 'development' !== 'production' && warn('v-on:' + this.arg + '="' + this.expression + '" expects a function value, ' + 'got ' + handler, this.vm); 5309 return; 5310 } 5311 5312 // apply modifiers 5313 if (this.modifiers.stop) { 5314 handler = stopFilter(handler); 5315 } 5316 if (this.modifiers.prevent) { 5317 handler = preventFilter(handler); 5318 } 5319 if (this.modifiers.self) { 5320 handler = selfFilter(handler); 5321 } 5322 // key filter 5323 var keys = Object.keys(this.modifiers).filter(function (key) { 5324 return key !== 'stop' && key !== 'prevent' && key !== 'self' && key !== 'capture'; 5325 }); 5326 if (keys.length) { 5327 handler = keyFilter(handler, keys); 5328 } 5329 5330 this.reset(); 5331 this.handler = handler; 5332 5333 if (this.iframeBind) { 5334 this.iframeBind(); 5335 } else { 5336 on(this.el, this.arg, this.handler, this.modifiers.capture); 5337 } 5338 }, 5339 5340 reset: function reset() { 5341 var el = this.iframeBind ? this.el.contentWindow : this.el; 5342 if (this.handler) { 5343 off(el, this.arg, this.handler); 5344 } 5345 }, 5346 5347 unbind: function unbind() { 5348 this.reset(); 5349 } 5350}; 5351 5352var prefixes = ['-webkit-', '-moz-', '-ms-']; 5353var camelPrefixes = ['Webkit', 'Moz', 'ms']; 5354var importantRE = /!important;?$/; 5355var propCache = Object.create(null); 5356 5357var testEl = null; 5358 5359var style = { 5360 5361 deep: true, 5362 5363 update: function update(value) { 5364 if (typeof value === 'string') { 5365 this.el.style.cssText = value; 5366 } else if (isArray(value)) { 5367 this.handleObject(value.reduce(extend, {})); 5368 } else { 5369 this.handleObject(value || {}); 5370 } 5371 }, 5372 5373 handleObject: function handleObject(value) { 5374 // cache object styles so that only changed props 5375 // are actually updated. 5376 var cache = this.cache || (this.cache = {}); 5377 var name, val; 5378 for (name in cache) { 5379 if (!(name in value)) { 5380 this.handleSingle(name, null); 5381 delete cache[name]; 5382 } 5383 } 5384 for (name in value) { 5385 val = value[name]; 5386 if (val !== cache[name]) { 5387 cache[name] = val; 5388 this.handleSingle(name, val); 5389 } 5390 } 5391 }, 5392 5393 handleSingle: function handleSingle(prop, value) { 5394 prop = normalize(prop); 5395 if (!prop) return; // unsupported prop 5396 // cast possible numbers/booleans into strings 5397 if (value != null) value += ''; 5398 if (value) { 5399 var isImportant = importantRE.test(value) ? 'important' : ''; 5400 if (isImportant) { 5401 /* istanbul ignore if */ 5402 if ('development' !== 'production') { 5403 warn('It\'s probably a bad idea to use !important with inline rules. ' + 'This feature will be deprecated
5403in a future version of Vue.'); 5404 } 5405 value = value.replace(importantRE, '').trim(); 5406 this.el.style.setProperty(prop.kebab, value, isImportant); 5407 } else { 5408 this.el.style[prop.camel] = value; 5409 } 5410 } else { 5411 this.el.style[prop.camel] = ''; 5412 } 5413 } 5414 5415}; 5416 5417/** 5418 * Normalize a CSS property name. 5419 * - cache result 5420 * - auto prefix 5421 * - camelCase -> dash-case 5422 * 5423 * @param {String} prop 5424 * @return {String} 5425 */ 5426 5427function normalize(prop) { 5428 if (propCache[prop]) { 5429 return propCache[prop]; 5430 } 5431 var res = prefix(prop); 5432 propCache[prop] = propCache[res] = res; 5433 return res; 5434} 5435 5436/** 5437 * Auto detect the appropriate prefix for a CSS property. 5438 * https://gist.github.com/paulirish/523692 5439 * 5440 * @param {String} prop 5441 * @return {String} 5442 */ 5443 5444function prefix(prop) { 5445 prop = hyphenate(prop); 5446 var camel = camelize(prop); 5447 var upper = camel.charAt(0).toUpperCase() + camel.slice(1); 5448 if (!testEl) { 5449 testEl = document.createElement('div'); 5450 } 5451 var i = prefixes.length; 5452 var prefixed; 5453 if (camel !== 'filter' && camel in testEl.style) { 5454 return { 5455 kebab: prop, 5456 camel: camel 5457 }; 5458 } 5459 while (i--) { 5460 prefixed = camelPrefixes[i] + upper; 5461 if (prefixed in testEl.style) { 5462 return { 5463 kebab: prefixes[i] + prop, 5464 camel: prefixed 5465 }; 5466 } 5467 } 5468} 5469 5470// xlink 5471var xlinkNS = 'http://www.w3.org/1999/xlink'; 5472var xlinkRE = /^xlink:/; 5473 5474// check for attributes that prohibit interpolations 5475var disallowedInterpAttrRE = /^v-|^:|^@|^(?:is|transition|transition-mode|debounce|track-by|stagger|enter-stagger|leave-stagger)$/; 5476// these attributes should also set their corresponding properties 5477// because they only affect the initial state of the element 5478var attrWithPropsRE = /^(?:value|checked|selected|muted)$/; 5479// these attributes expect enumrated values of "true" or "false" 5480// but are not boolean attributes 5481var enumeratedAttrRE = /^(?:draggable|contenteditable|spellcheck)$/; 5482 5483// these attributes should set a hidden property for 5484// binding v-model to object values 5485var modelProps = { 5486 value: '_value', 5487 'true-value': '_trueValue', 5488 'false-value': '_falseValue' 5489}; 5490 5491var bind$1 = { 5492 5493 priority: BIND, 5494 5495 bind: function bind() { 5496 var attr = this.arg; 5497 var tag = this.el.tagName; 5498 // should be deep watch on object mode 5499 if (!attr) { 5500 this.deep = true; 5501 } 5502 // handle interpolation bindings 5503 var descriptor = this.descriptor; 5504 var tokens = descriptor.interp; 5505 if (tokens) { 5506 // handle interpolations with one-time tokens 5507 if (descriptor.hasOneTime) { 5508 this.expression = tokensToExp(tokens, this._scope || this.vm); 5509 } 5510 5511 // only allow binding on native attributes 5512 if (disallowedInterpAttrRE.test(attr) || attr === 'name' && (tag === 'PARTIAL' || tag === 'SLOT')) { 5513 'development' !== 'production' && warn(attr + '="' + descriptor.raw + '": ' + 'attribute interpolation is not allowed in Vue.js ' + 'directives and special attributes.', this.vm); 5514 this.el.removeAttribute(attr); 5515 this.invalid = true; 5516 } 5517 5518 /* istanbul ignore if */ 5519 if ('development' !== 'production') { 5520 var raw = attr + '="' + descriptor.raw + '": '; 5521 // warn src 5522 if (attr === 'src') { 5523 warn(raw + 'interpolation in "src" attribute will cause ' + 'a 404 request. Use v-bind:src instead.', this.vm); 5524 } 5525 5526 // warn style 5527 if (attr === 'style') { 5528 warn(raw + 'interpolation in "style" attribute will cause ' + 'the attribute to be discarded in Internet Explorer. ' + 'Use v-bind:style instead.', this.vm); 5529 } 5530 } 5531 } 5532 }, 5533 5534 update: function update(value) { 5535 if (this.invalid) { 5536 return; 5537 } 5538 var attr = this.arg; 5539 if (this.arg) { 5540 this.handleSingle(attr, value); 5541 } else { 5542 this.handleObject(value || {}); 5543 } 5544 }, 5545 5546 // share object handler with v-bind:class 5547 handleObject: style.handleObject, 5548 5549 handleSingle: function handleSingle(attr, value) { 5550 var el = this.el; 5551 var interp = this.descriptor.interp; 5552 if (this.modifiers.camel) { 5553 attr = camelize(attr); 5554 } 5555 if (!interp && attrWithPropsRE.test(attr) && attr in el) { 5556 var attrValue = attr === 'value' ? value == null // IE9 will set input.value to "null" for null... 5557 ? '' : value : value; 5558 5559 if (el[attr] !== attrValue) { 5560 el[attr] = attrValue; 5561 } 5562 } 5563 // set model props 5564 var modelProp = modelProps[attr]; 5565 if (!interp && modelProp) { 5566 el[modelProp] = value; 5567 // update v-model if present 5568 var model = el.__v_model; 5569 if (model) { 5570 model.listener(); 5571 } 5572 } 5573 // do not set value attribute for textarea 5574 if (attr === 'value' && el.tagName === 'TEXTAREA') { 5575 el.removeAttribute(attr); 5576 return; 5577 } 5578 // update attribute 5579 if (enumeratedAttrRE.test(attr)) { 5580 el.setAttribute(attr, value ? 'true' : 'false'); 5581 } else if (value != null && value !== false) { 5582 if (attr === 'class') { 5583 // handle edge case #1960: 5584 // class interpolation should not overwrite Vue transition class 5585 if (el.__v_trans) { 5586 value += ' ' + el.__v_trans.id + '-transition'; 5587 } 5588 setClass(el, value); 5589 } else if (xlinkRE.test(attr)) { 5590 el.setAttributeNS(xlinkNS, attr, value === true ? '' : value); 5591 } else { 5592 el.setAttribute(attr, value === true ? '' : value); 5593 } 5594 } else { 5595 el.removeAttribute(attr); 5596 } 5597 } 5598}; 5599 5600var el = { 5601 5602 priority: EL, 5603 5604 bind: function bind() { 5605 /* istanbul ignore if */ 5606 if (!this.arg) { 5607 return; 5608 } 5609 var id = this.id = camelize(this.arg); 5610 var refs = (this._scope || this.vm).$els; 5611 if (hasOwn(refs, id)) { 5612 refs[id] = this.el; 5613 } else { 5614 defineReactive(refs, id, this.el); 5615 } 5616 }, 5617 5618 unbind: function unbind() { 5619 var refs = (this._scope || this.vm).$els; 5620 if (refs[this.id] === this.el) { 5621 refs[this.id] = null; 5622 } 5623 } 5624}; 5625 5626var ref = { 5627 bind: function bind() { 5628 'development' !== 'production' && warn('v-ref:' + this.arg + ' must be used on a child ' + 'component. Found on <' + this.el.tagName.toLowerCase() + '>.', this.vm); 5629 } 5630}; 5631 5632var cloak = { 5633 bind: function bind() { 5634 var el = this.el; 5635 this.vm.$once('pre-hook:compiled', function () { 5636 el.removeAttribute('v-cloak'); 5637 }); 5638 } 5639}; 5640 5641// logic control 5642// two-way binding 5643// event handling 5644// attributes 5645// ref & el 5646// cloak 5647// must export plain object 5648var directives = { 5649 text: text$1, 5650 html: html, 5651 'for': vFor, 5652 'if': vIf, 5653 show: show, 5654 model: model, 5655 on: on$1, 5656 bind: bind$1, 5657 el: el, 5658 ref: ref, 5659 cloak: cloak 5660}; 5661 5662var vClass = { 5663 5664 deep: true, 5665 5666 update: function update(value) { 5667 if (!value) { 5668 this.cleanup(); 5669 } else if (typeof value === 'string') { 5670 this.setClass(value.trim().split(/\s+/)); 5671 } else { 5672 this.setClass(normalize$1(value)); 5673 } 5674 }, 5675 5676 setClass: function setClass(value) { 5677 this.cleanup(value); 5678 for (var i = 0, l = value.length; i < l; i++) { 5679 var val = value[i]; 5680 if (val) { 5681 apply(this.el, val, addClass); 5682 } 5683 } 5684 this.prevKeys = value; 5685 }, 5686 5687 cleanup: function cleanup(value) { 5688 var prevKeys = this.prevKeys; 5689 if (!prevKeys) return; 5690 var i = prevKeys.length; 5691 while (i--) { 5692 var key = prevKeys[i]; 5693 if (!value || value.indexOf(key) < 0) { 5694 apply(this.el, key, removeClass); 5695 } 5696 } 5697 } 5698}; 5699 5700/** 5701 * Normalize objects and arrays (potentially containing objects)
5702 * into array of strings. 5703 * 5704 * @param {Object|Array<String|Object>} value 5705 * @return {Array<String>} 5706 */ 5707 5708function normalize$1(value) { 5709 var res = []; 5710 if (isArray(value)) { 5711 for (var i = 0, l = value.length; i < l; i++) { 5712 var _key = value[i]; 5713 if (_key) { 5714 if (typeof _key === 'string') { 5715 res.push(_key); 5716 } else { 5717 for (var k in _key) { 5718 if (_key[k]) res.push(k); 5719 } 5720 } 5721 } 5722 } 5723 } else if (isObject(value)) { 5724 for (var key in value) { 5725 if (value[key]) res.push(key); 5726 } 5727 } 5728 return res; 5729} 5730 5731/** 5732 * Add or remove a class/classes on an element 5733 * 5734 * @param {Element} el 5735 * @param {String} key The class name. This may or may not 5736 * contain a space character, in such a 5737 * case we'll deal with multiple class 5738 * names at once. 5739 * @param {Function} fn 5740 */ 5741 5742function apply(el, key, fn) { 5743 key = key.trim(); 5744 if (key.indexOf(' ') === -1) { 5745 fn(el, key); 5746 return; 5747 } 5748 // The key contains one or more space characters. 5749 // Since a class name doesn't accept such characters, we 5750 // treat it as multiple classes. 5751 var keys = key.split(/\s+/); 5752 for (var i = 0, l = keys.length; i < l; i++) { 5753 fn(el, keys[i]); 5754 } 5755} 5756 5757var component = { 5758 5759 priority: COMPONENT, 5760 5761 params: ['keep-alive', 'transition-mode', 'inline-template'], 5762 5763 /** 5764 * Setup. Two possible usages: 5765 * 5766 * - static: 5767 * <comp> or <div v-component="comp"> 5768 * 5769 * - dynamic: 5770 * <component :is="view"> 5771 */ 5772 5773 bind: function bind() { 5774 if (!this.el.__vue__) { 5775 // keep-alive cache 5776 this.keepAlive = this.params.keepAlive; 5777 if (this.keepAlive) { 5778 this.cache = {}; 5779 } 5780 // check inline-template 5781 if (this.params.inlineTemplate) { 5782 // extract inline template as a DocumentFragment 5783 this.inlineTemplate = extractContent(this.el, true); 5784 } 5785 // component resolution related state 5786 this.pendingComponentCb = this.Component = null; 5787 // transition related state 5788 this.pendingRemovals = 0; 5789 this.pendingRemovalCb = null; 5790 // create a ref anchor 5791 this.anchor = createAnchor('v-component'); 5792 replace(this.el, this.anchor); 5793 // remove is attribute. 5794 // this is removed during compilation, but because compilation is 5795 // cached, when the component is used elsewhere this attribute 5796 // will remain at link time. 5797 this.el.removeAttribute('is'); 5798 this.el.removeAttribute(':is'); 5799 // remove ref, same as above 5800 if (this.descriptor.ref) { 5801 this.el.removeAttribute('v-ref:' + hyphenate(this.descriptor.ref)); 5802 } 5803 // if static, build right now. 5804 if (this.literal) { 5805 this.setComponent(this.expression); 5806 } 5807 } else { 5808 'development' !== 'production' && warn('cannot mount component "' + this.expression + '" ' + 'on already mounted element: ' + this.el); 5809 } 5810 }, 5811 5812 /** 5813 * Public update, called by the watcher in the dynamic 5814 * literal scenario, e.g. <component :is="view"> 5815 */ 5816 5817 update: function update(value) { 5818 if (!this.literal) { 5819 this.setComponent(value); 5820 } 5821 }, 5822 5823 /** 5824 * Switch dynamic components. May resolve the component 5825 * asynchronously, and perform transition based on 5826 * specified transition mode. Accepts a few additional 5827 * arguments specifically for vue-router. 5828 * 5829 * The callback is called when the full transition is 5830 * finished. 5831 * 5832 * @param {String} value 5833 * @param {Function} [cb] 5834 */ 5835 5836 setComponent: function setComponent(value, cb) { 5837 this.invalidatePending(); 5838 if (!value) { 5839 // just remove current 5840 this.unbuild(true); 5841 this.remove(this.childVM, cb); 5842 this.childVM = null; 5843 } else { 5844 var self = this; 5845 this.resolveComponent(value, function () { 5846 self.mountComponent(cb); 5847 }); 5848 } 5849 }, 5850 5851 /** 5852 * Resolve the component constructor to use when creating 5853 * the child vm. 5854 * 5855 * @param {String|Function} value 5856 * @param {Function} cb 5857 */ 5858
5859 resolveComponent: function resolveComponent(value, cb) { 5860 var self = this; 5861 this.pendingComponentCb = cancellable(function (Component) { 5862 self.ComponentName = Component.options.name || (typeof value === 'string' ? value : null); 5863 self.Component = Component; 5864 cb(); 5865 }); 5866 this.vm._resolveComponent(value, this.pendingComponentCb); 5867 }, 5868 5869 /** 5870 * Create a new instance using the current constructor and 5871 * replace the existing instance. This method doesn't care 5872 * whether the new component and the old one are actually 5873 * the same. 5874 * 5875 * @param {Function} [cb] 5876 */ 5877 5878 mountComponent: function mountComponent(cb) { 5879 // actual mount 5880 this.unbuild(true); 5881 var self = this; 5882 var activateHooks = this.Component.options.activate; 5883 var cached = this.getCached(); 5884 var newComponent = this.build(); 5885 if (activateHooks && !cached) { 5886 this.waitingFor = newComponent; 5887 callActivateHooks(activateHooks, newComponent, function () { 5888 if (self.waitingFor !== newComponent) { 5889 return; 5890 } 5891 self.waitingFor = null; 5892 self.transition(newComponent, cb); 5893 }); 5894 } else { 5895 // update ref for kept-alive component 5896 if (cached) { 5897 newComponent._updateRef(); 5898 } 5899 this.transition(newComponent, cb); 5900 } 5901 }, 5902 5903 /** 5904 * When the component changes or unbinds before an async 5905 * constructor is resolved, we need to invalidate its 5906 * pending callback. 5907 */ 5908
5909 invalidatePending: function invalidatePending() { 5910 if (this.pendingComponentCb) { 5911 this.pendingComponentCb.cancel(); 5912 this.pendingComponentCb = null; 5913 } 5914 }, 5915 5916 /** 5917 * Instantiate/insert a new child vm. 5918 * If keep alive and has cached instance, insert that 5919 * instance; otherwise build a new one and cache it. 5920 * 5921 * @param {Object} [extraOptions] 5922 * @return {Vue} - the created instance 5923 */ 5924 5925 build: function build(extraOptions) { 5926 var cached = this.getCached(); 5927 if (cached) { 5928 return cached; 5929 } 5930 if (this.Component) { 5931 // default options 5932 var options = { 5933 name: this.ComponentName, 5934 el: cloneNode(this.el), 5935 template: this.inlineTemplate, 5936 // make sure to add the child with correct parent 5937 // if this is a transcluded component, its parent 5938 // should be the transclusion host. 5939 parent: this._host || this.vm, 5940 // if no inline-template, then the compiled 5941 // linker can be cached for better performance. 5942 _linkerCachable: !this.inlineTemplate, 5943 _ref: this.descriptor.ref, 5944 _asComponent: true, 5945 _isRouterView: this._isRouterView, 5946 // if this is a transcluded component, context 5947 // will be the common parent vm of this instance 5948 // and its host. 5949 _context: this.vm, 5950 // if this is inside an inline v-for, the scope 5951 // will be the intermediate scope created for this 5952 // repeat fragment. this is used for linking props 5953 // and container directives. 5954 _scope: this._scope, 5955 // pass in the owner fragment of this component. 5956 // this is necessary so that the fragment can keep 5957 // track of its contained components in order to 5958 // call attach/detach hooks for them. 5959 _frag: this._frag 5960 }; 5961 // extra options 5962 // in 1.0.0 this is used by vue-router only 5963 /* istanbul ignore if */ 5964 if (extraOptions) { 5965 extend(options, extraOptions); 5966 } 5967 var child = new this.Component(options); 5968 if (this.keepAlive) { 5969 this.cache[this.Component.cid] = child; 5970 } 5971 /* istanbul ignore if */ 5972 if ('development' !== 'production' && this.el.hasAttribute('transition') && child._isFragment) { 5973 warn('Transitions will not work on a fragment instance. ' + 'Template: ' + child.$options.template, child); 5974 } 5975 return child; 5976 } 5977 }, 5978 5979 /** 5980 * Try to get a cached instance of the current component. 5981 * 5982 * @return {Vue|undefined} 5983 */ 5984 5985 getCached: function getCached() { 5986 return this.keepAlive && this.cache[this.Component.cid]; 5987 }, 5988 5989 /** 5990 * Teardown the current child, but defers cleanup so 5991 * that we can separate the destroy and removal steps. 5992 * 5993 * @param {Boolean} defer 5994 */ 5995 5996 unbuild: function unbuild(defer) { 5997 if (this.waitingFor) { 5998 if (!this.keepAlive) { 5999 this.waitingFor.$destroy(); 6000 } 6001 this.waitingFor = null; 6002 } 6003 var child = this.childVM; 6004 if (!child || this.keepAlive) { 6005 if (child) { 6006 // remove ref 6007 child._inactive = true; 6008 child._updateRef(true); 6009 } 6010 return; 6011 } 6012 // the sole purpose of `deferCleanup` is so that we can 6013 // "deactivate" the vm right now and perform DOM removal 6014 // later. 6015 child.$destroy(false, defer); 6016 }, 6017 6018 /** 6019 * Remove current destroyed child and manually do 6020 * the cleanup after removal. 6021 * 6022 * @param {Function} cb 6023 */ 6024 6025 remove: function remove(child, cb) { 6026 var keepAlive = this.keepAlive; 6027 if (child) { 6028 // we may have a component switch when a previous 6029 // component is still being transitioned out. 6030 // we want to trigger only one lastest insertion cb 6031 // when the existing transition finishes. (#1119) 6032 this.pendingRemovals++; 6033 this.pendingRemovalCb = cb; 6034 var self = this; 6035 child.$remove(function () { 6036 self.pendingRemovals--; 6037 if (!keepAlive) child._cleanup(); 6038 if (!self.pendingRemovals && self.pendingRemovalCb) { 6039 self.pendingRemovalCb(); 6040 self.pendingRemovalCb = null; 6041 } 6042 }); 6043 } else if (cb) { 6044 cb(); 6045 } 6046 }, 6047 6048 /** 6049 * Actually swap the components, depending on the 6050 * transition mode. Defaults to simultaneous. 6051 * 6052 * @param {Vue} target 6053 * @param {Function} [cb] 6054 */ 6055 6056 transition: function transition(target, cb) { 6057 var self = this; 6058 var current = this.childVM; 6059 // for devtool inspection 6060 if (current) current._inactive = true; 6061 target._inactive = false;
6062 this.childVM = target; 6063 switch (self.params.transitionMode) { 6064 case 'in-out': 6065 target.$before(self.anchor, function () { 6066 self.remove(current, cb); 6067 }); 6068 break; 6069 case 'out-in': 6070 self.remove(current, function () { 6071 target.$before(self.anchor, cb); 6072 }); 6073 break; 6074 default: 6075 self.remove(current); 6076 target.$before(self.anchor, cb); 6077 } 6078 }, 6079 6080 /** 6081 * Unbind. 6082 */ 6083 6084 unbind: function unbind() { 6085 this.invalidatePending(); 6086 // Do not defer cleanup when unbinding 6087 this.unbuild(); 6088 // destroy all keep-alive cached instances 6089 if (this.cache) { 6090 for (var key in this.cache) { 6091 this.cache[key].$destroy(); 6092 } 6093 this.cache = null; 6094 } 6095 } 6096}; 6097 6098/** 6099 * Call activate hooks in order (asynchronous) 6100 * 6101 * @param {Array} hooks 6102 * @param {Vue} vm 6103 * @param {Function} cb 6104 */ 6105 6106function callActivateHooks(hooks, vm, cb) { 6107 var total = hooks.length; 6108 var called = 0; 6109 hooks[0].call(vm, next); 6110 function next() { 6111 if (++called >= total) { 6112 cb(); 6113 } else { 6114 hooks[called].call(vm, next); 6115 } 6116 } 6117} 6118 6119var propBindingModes = config._propBindingModes; 6120var empty = {}; 6121 6122// regexes 6123var identRE$1 = /^[$_a-zA-Z]+[\w$]*$/; 6124var settablePathRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\[[^\[\]]+\])*$/; 6125 6126/** 6127 * Compile props on a root element and return 6128 * a props link function. 6129 * 6130 * @param {Element|DocumentFragment} el 6131 * @param {Array} propOptions 6132 * @param {Vue} vm 6133 * @return {Function} propsLinkFn 6134 */ 6135 6136function compileProps(el, propOptions, vm) { 6137 var props = []; 6138 var propsData = vm.$options.propsData; 6139 var names = Object.keys(propOptions); 6140 var i = names.length; 6141 var options, name, attr, value, path, parsed, prop; 6142 while (i--) { 6143 name = names[i]; 6144 options = propOptions[name] || empty; 6145 6146 if ('development' !== 'production' && name === '$data') { 6147 warn('Do not use $data as prop.', vm); 6148 continue; 6149 } 6150 6151 // props could contain dashes, which will be 6152 // interpreted as minus calculations by the parser 6153 // so we need to camelize the path here 6154 path = camelize(name); 6155 if (!identRE$1.test(path)) { 6156 'development' !== 'production' && warn('Invalid prop key: "' + name + '". Prop keys ' + 'must be valid identifiers.', vm); 6157 continue; 6158 } 6159 6160 prop = { 6161 name: name, 6162 path: path, 6163 options: options, 6164 mode: propBindingModes.ONE_WAY, 6165 raw: null 6166 }; 6167 6168 attr = hyphenate(name); 6169 // first check dynamic version 6170 if ((value = getBindAttr(el, attr)) === null) { 6171 if ((value = getBindAttr(el, attr + '.sync')) !== null) { 6172 prop.mode = propBindingModes.TWO_WAY; 6173 } else if ((value = getBindAttr(el, attr + '.once')) !== null) { 6174 prop.mode = propBindingModes.ONE_TIME; 6175 } 6176 } 6177 if (value !== null) { 6178 // has dynamic binding! 6179 prop.raw = value; 6180 parsed = parseDirective(value); 6181 value = parsed.expression; 6182 prop.filters = parsed.filters; 6183 // check binding type 6184 if (isLiteral(value) && !parsed.filters) { 6185 // for expressions containing literal numbers and 6186 // booleans, there's no need to setup a prop binding, 6187 // so we can optimize them as a one-time set. 6188 prop.optimizedLiteral = true; 6189 } else { 6190 prop.dynamic = true; 6191 // check non-settable path for two-way bindings 6192 if ('development' !== 'production' && prop.mode === propBindingModes.TWO_WAY && !settablePathRE.test(value)) { 6193 prop.mode = propBindingModes.ONE_WAY; 6194 warn('Cannot bind two-way prop with non-settable ' + 'parent path: ' + value, vm); 6195 } 6196 } 6197 prop.parentPath = value; 6198 6199 // warn required two-way 6200 if ('development' !== 'production' && options.twoWay && prop.mode !== propBindingModes.TWO_WAY) { 6201 warn('Prop "' + name + '" expects a two-way binding type.', vm); 6202 } 6203 } else if ((value = getAttr(el, attr)) !== null) { 6204 // has literal binding! 6205 prop.raw = value; 6206 } else if (propsData && (value = propsData[name] || propsData[path]) !== null) { 6207 // has propsData 6208 prop.raw = value; 6209 } else if ('development' !== 'production') { 6210 // check possible camelCase prop usage 6211 var lowerCaseName = path.toLowerCase(); 6212 value = /[A-Z\-]/.test(name) && (el.getAttribute(lowerCaseName) || el.getAttribute(':' + lowerCaseName) || el.getAttribute('v-bind:' + lowerCaseName) || el.getAttribute(':' + lowerCaseName + '.once') || el.getAttribute('v-bind:' + lowerCaseName + '.once') || el.getAttribute(':' + lowerCaseName + '.sync') || el.getAttribute('v-bind:' + lowerCaseName + '.sync')); 6213 if (value) { 6214 warn('Possible usage error for prop `' + lowerCaseName + '` - ' + 'did you mean `' + attr + '`? HTML is case-insensitive, remember to use ' + 'kebab-case for props in templates.', vm); 6215 } else if (options.required && (!propsData || !(name in propsData) && !(path in propsData))) { 6216 // warn missing required
6217 warn('Missing required prop: ' + name, vm); 6218 } 6219 } 6220 // push prop 6221 props.push(prop); 6222 } 6223 return makePropsLinkFn(props); 6224} 6225 6226/** 6227 * Build a function that applies props to a vm. 6228 * 6229 * @param {Array} props 6230 * @return {Function} propsLinkFn 6231 */ 6232 6233function makePropsLinkFn(props) { 6234 return function propsLinkFn(vm, scope) { 6235 // store resolved props info 6236 vm._props = {}; 6237 var inlineProps = vm.$options.propsData; 6238 var i = props.length; 6239 var prop, path, options, value, raw; 6240 while (i--) { 6241 prop = props[i]; 6242 raw = prop.raw; 6243 path = prop.path; 6244 options = prop.options; 6245 vm._props[path] = prop; 6246 if (inlineProps && hasOwn(inlineProps, path)) { 6247 initProp(vm, prop, inlineProps[path]); 6248 }if (raw === null) { 6249 // initialize absent prop 6250 initProp(vm, prop, undefined); 6251 } else if (prop.dynamic) { 6252 // dynamic prop 6253 if (prop.mode === propBindingModes.ONE_TIME) { 6254 // one time binding 6255 value = (scope || vm._context || vm).$get(prop.parentPath); 6256 initProp(vm, prop, value); 6257 } else { 6258 if (vm._context) { 6259 // dynamic binding 6260 vm._bindDir({ 6261 name: 'prop', 6262 def: propDef, 6263 prop: prop 6264 }, null, null, scope); // el, host, scope 6265 } else { 6266 // root instance 6267 initProp(vm, prop, vm.$get(prop.parentPath)); 6268 } 6269 } 6270 } else if (prop.optimizedLiteral) { 6271 // optimized literal, cast it and just set once 6272 var stripped = stripQuotes(raw); 6273 value = stripped === raw ? toBoolean(toNumber(raw)) : stripped; 6274 initProp(vm, prop, value); 6275 } else { 6276 // string literal, but we need to cater for 6277 // Boolean props with no value, or with same 6278 // literal value (e.g. disabled="disabled") 6279 // see https://github.com/vuejs/vue-loader/issues/182 6280 value = options.type === Boolean && (raw === '' || raw === hyphenate(prop.name)) ? true : raw; 6281 initProp(vm, prop, value); 6282 } 6283 } 6284 }; 6285} 6286 6287/** 6288 * Process a prop with a rawValue, applying necessary coersions, 6289 * default values & assertions and call the given callback with 6290 * processed value. 6291 * 6292 * @param {Vue} vm 6293 * @param {Object} prop 6294 * @param {*} rawValue 6295 * @param {Function} fn 6296 */ 6297 6298function processPropValue(vm, prop, rawValue, fn) { 6299 var isSimple = prop.dynamic && isSimplePath(prop.parentPath); 6300 var value = rawValue; 6301 if (value === undefined) { 6302 value = getPropDefaultValue(vm, prop); 6303 } 6304 value = coerceProp(prop, value, vm); 6305 var coerced = value !== rawValue; 6306 if (!assertProp(prop, value, vm)) { 6307 value = undefined; 6308 } 6309 if (isSimple && !coerced) { 6310 withoutConversion(function () { 6311 fn(value); 6312 }); 6313 } else { 6314 fn(value); 6315 } 6316} 6317 6318/** 6319 * Set a prop's initial value on a vm and its data object. 6320 * 6321 * @param {Vue} vm 6322 * @param {Object} prop 6323 * @param {*} value 6324 */ 6325 6326function initProp(vm, prop, value) { 6327 processPropValue(vm, prop, value, function (value) { 6328 defineReactive(vm, prop.path, value); 6329 }); 6330} 6331 6332/** 6333 * Update a prop's value on a vm. 6334 * 6335 * @param {Vue} vm 6336 * @param {Object} prop 6337 * @param {*} value 6338 */ 6339 6340function updateProp(vm, prop, value) { 6341 processPropValue(vm, prop, value, function (value) { 6342 vm[prop.path] = value; 6343 }); 6344} 6345 6346/** 6347 * Get the default value of a prop. 6348 * 6349 * @param {Vue} vm 6350 * @param {Object} prop 6351 * @return {*} 6352 */ 6353 6354function getPropDefaultValue(vm, prop) { 6355 // no default, return undefined 6356 var options = prop.options; 6357 if (!hasOwn(options, 'default')) { 6358 // absent boolean value defaults to false 6359 return options.type === Boolean ? false : undefined; 6360 } 6361 var def = options['default']; 6362 // warn against non-factory defaults for Object & Array 6363 if (isObject(def)) { 6364 'development' !== 'production' && warn('Invalid default value for prop "' + prop.name + '": ' + 'Props with type Object/Array must use a factory function ' + 'to return the default value.', vm); 6365 } 6366 // call factory function for non-Function types 6367 return typeof def === 'function' && options.type !== Function ? def.call(vm) : def; 6368} 6369 6370/** 6371 * Assert whether a prop is valid. 6372 * 6373 * @param {Object} prop 6374 * @param {*} value 6375 * @param {Vue} vm 6376 */ 6377 6378function assertProp(prop, value, vm) { 6379 if (!prop.options.required && ( // non-required 6380 prop.raw === null || // abscent 6381 value == null) // null or undefined 6382 ) { 6383 return true; 6384 } 6385 var options = prop.options; 6386 var type = options.type;
6387 var valid = !type; 6388 var expectedTypes = []; 6389 if (type) { 6390 if (!isArray(type)) { 6391 type = [type]; 6392 } 6393 for (var i = 0; i < type.length && !valid; i++) { 6394 var assertedType = assertType(value, type[i]); 6395 expectedTypes.push(assertedType.expectedType); 6396 valid = assertedType.valid; 6397 } 6398 } 6399 if (!valid) { 6400 if ('development' !== 'production') { 6401 warn('Invalid prop: type check failed for prop "' + prop.name + '".' + ' Expected ' + expectedTypes.map(formatType).join(', ') + ', got ' + formatValue(value) + '.', vm); 6402 } 6403 return false; 6404 } 6405 var validator = options.validator; 6406 if (validator) { 6407 if (!validator(value)) { 6408 'development' !== 'production' && warn('Invalid prop: custom validator check failed for prop "' + prop.name + '".', vm); 6409 return false; 6410 } 6411 } 6412 return true; 6413} 6414 6415/** 6416 * Force parsing value with coerce option. 6417 * 6418 * @param {*} value 6419 * @param {Object} options 6420 * @return {*} 6421 */ 6422 6423function coerceProp(prop, value, vm) { 6424 var coerce = prop.options.coerce; 6425 if (!coerce) { 6426 return value; 6427 } 6428 if (typeof coerce === 'function') { 6429 return coerce(value); 6430 } else { 6431 'development' !== 'production' && warn('Invalid coerce for prop "' + prop.name + '": expected function, got ' + typeof coerce + '.', vm); 6432 return value; 6433 } 6434} 6435 6436/** 6437 * Assert the type of a value 6438 * 6439 * @param {*} value 6440 * @param {Function} type 6441 * @return {Object} 6442 */ 6443 6444function assertType(value, type) { 6445 var valid; 6446 var expectedType; 6447 if (type === String) { 6448 expectedType = 'string'; 6449 valid = typeof value === expectedType; 6450 } else if (type === Number) { 6451 expectedType = 'number'; 6452 valid = typeof value === expectedType; 6453 } else if (type === Boolean) { 6454 expectedType = 'boolean'; 6455 valid = typeof value === expectedType; 6456 } else if (type === Function) { 6457 expectedType = 'function'; 6458 valid = typeof value === expectedType; 6459 } else if (type === Object) { 6460 expectedType = 'object'; 6461 valid = isPlainObject(value); 6462 } else if (type === Array) { 6463 expectedType = 'array'; 6464 valid = isArray(value); 6465 } else { 6466 valid = value instanceof type; 6467 } 6468 return { 6469 valid: valid, 6470 expectedType: expectedType 6471 }; 6472} 6473 6474/** 6475 * Format type for output 6476 * 6477 * @param {String} type 6478 * @return {String} 6479 */ 6480 6481function formatType(type) { 6482 return type ? type.charAt(0).toUpperCase() + type.slice(1) : 'custom type'; 6483} 6484 6485/** 6486 * Format value 6487 * 6488 * @param {*} value 6489 * @return {String} 6490 */ 6491 6492function formatValue(val) { 6493 return Object.prototype.toString.call(val).slice(8, -1); 6494} 6495 6496var bindingModes = config._propBindingModes; 6497 6498var propDef = { 6499 6500 bind: function bind() { 6501 var child = this.vm; 6502 var parent = child._context; 6503 // passed in from compiler directly 6504 var prop = this.descriptor.prop; 6505 var childKey = prop.path; 6506 var parentKey = prop.parentPath; 6507 var twoWay = prop.mode === bindingModes.TWO_WAY; 6508 6509 var parentWatcher = this.parentWatcher = new Watcher(parent, parentKey, function (val) { 6510 updateProp(child, prop, val); 6511 }, { 6512 twoWay: twoWay, 6513 filters: prop.filters, 6514 // important: props need to be observed on the 6515 // v-for scope if present 6516 scope: this._scope 6517 }); 6518 6519 // set the child initial value. 6520 initProp(child, prop, parentWatcher.value); 6521 6522 // setup two-way binding 6523 if (twoWay) { 6524 // important: defer the child watcher creation until 6525 // the created hook (after data observation) 6526 var self = this; 6527 child.$once('pre-hook:created', function () { 6528 self.childWatcher = new Watcher(child, childKey, function (val) { 6529 parentWatcher.set(val); 6530 }, { 6531 // ensure sync upward before parent sync down. 6532 // this is necessary in cases e.g. the child 6533 // mutates a prop array, then replaces it. (#1683) 6534 sync: true 6535 }); 6536 }); 6537 } 6538 }, 6539 6540 unbind: function unbind() { 6541 this.parentWatcher.teardown(); 6542 if (this.childWatcher) { 6543 this.childWatcher.teardown(); 6544 } 6545 } 6546}; 6547 6548var queue$1 = []; 6549var queued = false; 6550 6551/** 6552 * Push a job into the queue. 6553 * 6554 * @param {Function} job 6555 */ 6556 6557function pushJob(job) { 6558 queue$1.push(job); 6559 if (!queued) { 6560 queued = true; 6561 nextTick(flush); 6562 } 6563} 6564 6565/** 6566 * Flush the queue, and do one forced reflow before 6567 * triggering transitions. 6568 */ 6569 6570function flush() { 6571 // Force layout 6572 var f = document.documentElement.offsetHeight; 6573 for (var i = 0; i < queue$1.length; i++) { 6574 queue$1[i](); 6575 } 6576 queue$1 = []; 6577 queued = false;
6578 // dummy return, so js linters don't complain about 6579 // unused variable f 6580 return f; 6581} 6582 6583var TYPE_TRANSITION = 'transition'; 6584var TYPE_ANIMATION = 'animation'; 6585var transDurationProp = transitionProp + 'Duration'; 6586var animDurationProp = animationProp + 'Duration'; 6587 6588/** 6589 * If a just-entered element is applied the 6590 * leave class while its enter transition hasn't started yet, 6591 * and the transitioned property has the same value for both 6592 * enter/leave, then the leave transition will be skipped and 6593 * the transitionend event never fires. This function ensures 6594 * its callback to be called after a transition has started 6595 * by waiting for double raf. 6596 * 6597 * It falls back to setTimeout on devices that support CSS 6598 * transitions but not raf (e.g. Android 4.2 browser) - since 6599 * these environments are usually slow, we are giving it a 6600 * relatively large timeout. 6601 */ 6602 6603var raf = inBrowser && window.requestAnimationFrame; 6604var waitForTransitionStart = raf 6605/* istanbul ignore next */ 6606? function (fn) { 6607 raf(function () { 6608 raf(fn); 6609 }); 6610} : function (fn) { 6611 setTimeout(fn, 50); 6612}; 6613 6614/** 6615 * A Transition object that encapsulates the state and logic 6616 * of the transition. 6617 * 6618 * @param {Element} el 6619 * @param {String} id 6620 * @param {Object} hooks 6621 * @param {Vue} vm 6622 */ 6623function Transition(el, id, hooks, vm) { 6624 this.id = id; 6625 this.el = el; 6626 this.enterClass = hooks && hooks.enterClass || id + '-enter'; 6627 this.leaveClass = hooks && hooks.leaveClass || id + '-leave'; 6628 this.hooks = hooks; 6629 this.vm = vm; 6630 // async state 6631 this.pendingCssEvent = this.pendingCssCb = this.cancel = this.pendingJsCb = this.op = this.cb = null; 6632 this.justEntered = false; 6633 this.entered = this.left = false; 6634 this.typeCache = {}; 6635 // check css transition type 6636 this.type = hooks && hooks.type; 6637 /* istanbul ignore if */ 6638 if ('development' !== 'production') { 6639 if (this.type && this.type !== TYPE_TRANSITION && this.type !== TYPE_ANIMATION) { 6640 warn('invalid CSS transition type for transition="' + this.id + '": ' + this.type, vm); 6641 } 6642 } 6643 // bind 6644 var self = this;['enterNextTick', 'enterDone', 'leaveNextTick', 'leaveDone'].forEach(function (m) { 6645 self[m] = bind(self[m], self); 6646 }); 6647} 6648 6649var p$1 = Transition.prototype; 6650 6651/** 6652 * Start an entering transition. 6653 * 6654 * 1. enter transition triggered 6655 * 2. call beforeEnter hook 6656 * 3. add enter class 6657 * 4. insert/show element 6658 * 5. call enter hook (with possible explicit js callback) 6659 * 6. reflow 6660 * 7. based on transition type: 6661 * - transition: 6662 * remove class now, wait for transitionend, 6663 * then done if there's no explicit js callback. 6664 * - animation: 6665 * wait for animationend, remove class, 6666 * then done if there's no explicit js callback. 6667 * - no css transition: 6668 * done now if there's no explicit js callback. 6669 * 8. wait for either done or js callback, then call 6670 * afterEnter hook. 6671 * 6672 * @param {Function} op - insert/show the element 6673 * @param {Function} [cb] 6674 */ 6675 6676p$1.enter = function (op, cb) { 6677 this.cancelPending(); 6678 this.callHook('beforeEnter'); 6679 this.cb = cb; 6680 addClass(this.el, this.enterClass); 6681 op(); 6682 this.entered = false; 6683 this.callHookWithCb('enter'); 6684 if (this.entered) { 6685 return; // user called done synchronously. 6686 } 6687 this.cancel = this.hooks && this.hooks.enterCancelled; 6688 pushJob(this.enterNextTick); 6689}; 6690 6691/** 6692 * The "nextTick" phase of an entering transition, which is 6693 * to be pushed into a queue and executed after a reflow so 6694 * that removing the class can trigger a CSS transition. 6695 */ 6696 6697p$1.enterNextTick = function () { 6698 var _this = this; 6699 6700 // prevent transition skipping 6701 this.justEntered = true; 6702 waitForTransitionStart(function () { 6703 _this.justEntered = false; 6704 }); 6705 var enterDone = this.enterDone; 6706 var type = this.getCssTransitionType(this.enterClass); 6707 if (!this.pendingJsCb) { 6708 if (type === TYPE_TRANSITION) { 6709 // trigger transition by removing enter class now 6710 removeClass(this.el, this.enterClass); 6711 this.setupCssCb(transitionEndEvent, enterDone); 6712 } else if (type === TYPE_ANIMATION) { 6713 this.setupCssCb(animationEndEvent, enterDone); 6714 } else { 6715 enterDone(); 6716 } 6717 } else if (type === TYPE_TRANSITION) { 6718 removeClass(this.el, this.enterClass); 6719 } 6720}; 6721 6722/** 6723 * The "cleanup" phase of an entering transition. 6724 */ 6725 6726p$1.enterDone = function () { 6727 this.entered = true; 6728 this.cancel = this.pendingJsCb = null; 6729 removeClass(this.el, this.enterClass); 6730 this.callHook('afterEnter'); 6731 if (this.cb) this.cb(); 6732}; 6733 6734/** 6735 * Start a leaving transition. 6736 * 6737 * 1. leave transition triggered. 6738 * 2. call beforeLeave hook
6739 * 3. add leave class (trigger css transition) 6740 * 4. call leave hook (with possible explicit js callback) 6741 * 5. reflow if no explicit js callback is provided 6742 * 6. based on transition type: 6743 * - transition or animation: 6744 * wait for end event, remove class, then done if 6745 * there's no explicit js callback. 6746 * - no css transition: 6747 * done if there's no explicit js callback. 6748 * 7. wait for either done or js callback, then call 6749 * afterLeave hook. 6750 * 6751 * @param {Function} op - remove/hide the element 6752 * @param {Function} [cb] 6753 */ 6754 6755p$1.leave = function (op, cb) { 6756 this.cancelPending(); 6757 this.callHook('beforeLeave'); 6758 this.op = op; 6759 this.cb = cb; 6760 addClass(this.el, this.leaveClass); 6761 this.left = false; 6762 this.callHookWithCb('leave'); 6763 if (this.left) { 6764 return; // user called done synchronously. 6765 } 6766 this.cancel = this.hooks && this.hooks.leaveCancelled; 6767 // only need to handle leaveDone if 6768 // 1. the transition is already done (synchronously called 6769 // by the user, which causes this.op set to null) 6770 // 2. there's no explicit js callback 6771 if (this.op && !this.pendingJsCb) { 6772 // if a CSS transition leaves immediately after enter, 6773 // the transitionend event never fires. therefore we 6774 // detect such cases and end the leave immediately. 6775 if (this.justEntered) { 6776 this.leaveDone(); 6777 } else { 6778 pushJob(this.leaveNextTick); 6779 } 6780 } 6781}; 6782 6783/** 6784 * The "nextTick" phase of a leaving transition. 6785 */ 6786 6787p$1.leaveNextTick = function () { 6788 var type = this.getCssTransitionType(this.leaveClass); 6789 if (type) { 6790 var event = type === TYPE_TRANSITION ? transitionEndEvent : animationEndEvent; 6791 this.setupCssCb(event, this.leaveDone); 6792 } else { 6793 this.leaveDone(); 6794 } 6795}; 6796 6797/** 6798 * The "cleanup" phase of a leaving transition. 6799 */ 6800 6801p$1.leaveDone = function () { 6802 this.left = true; 6803 this.cancel = this.pendingJsCb = null; 6804 this.op(); 6805 removeClass(this.el, this.leaveClass); 6806 this.callHook('afterLeave'); 6807 if (this.cb) this.cb(); 6808 this.op = null; 6809}; 6810 6811/** 6812 * Cancel any pending callbacks from a previously running 6813 * but not finished transition. 6814 */ 6815 6816p$1.cancelPending = function () { 6817 this.op = this.cb = null; 6818 var hasPending = false; 6819 if (this.pendingCssCb) { 6820 hasPending = true; 6821 off(this.el, this.pendingCssEvent, this.pendingCssCb); 6822 this.pendingCssEvent = this.pendingCssCb = null; 6823 } 6824 if (this.pendingJsCb) { 6825 hasPending = true; 6826 this.pendingJsCb.cancel(); 6827 this.pendingJsCb = null; 6828 } 6829 if (hasPending) { 6830 removeClass(this.el, this.enterClass); 6831 removeClass(this.el, this.leaveClass); 6832 } 6833 if (this.cancel) { 6834 this.cancel.call(this.vm, this.el); 6835 this.cancel = null; 6836 } 6837}; 6838 6839/** 6840 * Call a user-provided synchronous hook function. 6841 * 6842 * @param {String} type 6843 */ 6844 6845p$1.callHook = function (type) { 6846 if (this.hooks && this.hooks[type]) { 6847 this.hooks[type].call(this.vm, this.el); 6848 } 6849}; 6850 6851/** 6852 * Call a user-provided, potentially-async hook function. 6853 * We check for the length of arguments to see if the hook 6854 * expects a `done` callback. If true, the transition's end 6855 * will be determined by when the user calls that callback; 6856 * otherwise, the end is determined by the CSS transition or 6857 * animation. 6858 * 6859 * @param {String} type 6860 */ 6861 6862p$1.callHookWithCb = function (type) { 6863 var hook = this.hooks && this.hooks[type]; 6864 if (hook) { 6865 if (hook.length > 1) { 6866 this.pendingJsCb = cancellable(this[type + 'Done']); 6867 } 6868 hook.call(this.vm, this.el, this.pendingJsCb); 6869 } 6870}; 6871 6872/** 6873 * Get an element's transition type based on the 6874 * calculated styles. 6875 * 6876 * @param {String} className 6877 * @return {Number} 6878 */ 6879 6880p$1.getCssTransitionType = function (className) { 6881 /* istanbul ignore if */ 6882 if (!transitionEndEvent || 6883 // skip CSS transitions if page is not visible - 6884 // this solves the issue of transitionend events not 6885 // firing until the page is visible again. 6886 // pageVisibility API is supported in IE10+, same as 6887 // CSS transitions. 6888 document.hidden || 6889 // explicit js-only transition 6890 this.hooks && this.hooks.css === false || 6891 // element is hidden 6892 isHidden(this.el)) { 6893 return; 6894 } 6895 var type = this.type || this.typeCache[className]; 6896 if (type) return type; 6897 var inlineStyles = this.el.style; 6898 var computedStyles = window.getComputedStyle(this.el); 6899 var transDuration = inlineStyles[transDurationProp] || computedStyles[transDurationProp]; 6900 if (transDuration && transDuration !== '0s') { 6901 type = TYPE_TRANSITION; 6902 } else { 6903 var animDuration = inlineStyles[animDurationProp] || computedStyles[animDurationProp]; 6904 if (animDuration && animDuration !== '0s') { 6905 type = TYPE_ANIMATION; 6906 } 6907 } 6908 if (type) { 6909 this.typeCache[className] = type; 6910 } 6911 return type; 6912}; 6913 6914/** 6915 * Setup a CSS transitionend/animationend callback. 6916 * 6917 * @param {String} event 6918 * @param {Function} cb 6919 */ 6920 6921p$1.setupCssCb = function (event, cb) { 6922 this.pendingCssEvent = event; 6923 var self = this; 6924 var el = this.el; 6925 var onEnd = this.pendingCssCb = function (e) { 6926 if (e.target === el) { 6927 off(el, event, onEnd); 6928 self.pendingCssEvent = self.pendingCssCb = null; 6929 if (!self.pendingJsCb && cb) { 6930 cb(); 6931 } 6932 } 6933 }; 6934 on(el, event, onEnd); 6935}; 6936 6937/** 6938 * Check if an element is hidden - in that case we can just 6939 * skip the transition alltogether. 6940 * 6941 * @param {Element} el 6942 * @return {Boolean} 6943 */ 6944 6945function isHidden(el) { 6946 if (/svg$/.test(el.namespaceURI)) { 6947 // SVG elements do not have offset(Width|Height) 6948 // so we need to check the client rect 6949 var rect = el.getBoundingClientRect(); 6950 return !(rect.width || rect.height); 6951 } else { 6952 return !(el.offsetWidth || el.offsetHeight || el.getClientRects().length); 6953 } 6954} 6955 6956var transition$1 = { 6957 6958 priority: TRANSITION, 6959
6960 update: function update(id, oldId) { 6961 var el = this.el; 6962 // resolve on owner vm 6963 var hooks = resolveAsset(this.vm.$options, 'transitions', id); 6964 id = id || 'v'; 6965 oldId = oldId || 'v'; 6966 el.__v_trans = new Transition(el, id, hooks, this.vm); 6967 removeClass(el, oldId + '-transition'); 6968 addClass(el, id + '-transition'); 6969 } 6970}; 6971 6972var internalDirectives = { 6973 style: style, 6974 'class': vClass, 6975 component: component, 6976 prop: propDef, 6977 transition: transition$1 6978}; 6979 6980// special binding prefixes 6981var bindRE = /^v-bind:|^:/; 6982var onRE = /^v-on:|^@/; 6983var dirAttrRE = /^v-([^:]+)(?:$|:(.*)$)/; 6984var modifierRE = /\.[^\.]+/g; 6985var transitionRE = /^(v-bind:|:)?transition$/; 6986 6987// default directive priority 6988var DEFAULT_PRIORITY = 1000; 6989var DEFAULT_TERMINAL_PRIORITY = 2000; 6990 6991/** 6992 * Compile a template and return a reusable composite link 6993 * function, which recursively contains more link functions 6994 * inside. This top level compile function would normally 6995 * be called on instance root nodes, but can also be used 6996 * for partial compilation if the partial argument is true. 6997 * 6998 * The returned composite link function, when called, will 6999 * return an unlink function that tearsdown all directives 7000 * created during the linking phase. 7001 * 7002 * @param {Element|DocumentFragment} el 7003 * @param {Object} options 7004 * @param {Boolean} partial 7005 * @return {Function} 7006 */ 7007 7008function compile(el, options, partial) { 7009 // link function for the node itself. 7010 var nodeLinkFn = partial || !options._asComponent ? compileNode(el, options) : null; 7011 // link function for the childNodes 7012 var childLinkFn = !(nodeLinkFn && nodeLinkFn.terminal) && !isScript(el) && el.hasChildNodes() ? compileNodeList(el.childNodes, options) : null; 7013 7014 /** 7015 * A composite linker function to be called on a already 7016 * compiled piece of DOM, which instantiates all directive 7017 * instances. 7018 * 7019 * @param {Vue} vm 7020 * @param {Element|DocumentFragment} el 7021 * @param {Vue} [host] - host vm of transcluded content 7022 * @param {Object} [scope] - v-for scope 7023 * @param {Fragment} [frag] - link context fragment 7024 * @return {Function|undefined} 7025 */ 7026 7027 return function compositeLinkFn(vm, el, host, scope, frag) {
7028 // cache childNodes before linking parent, fix #657 7029 var childNodes = toArray(el.childNodes); 7030 // link 7031 var dirs = linkAndCapture(function compositeLinkCapturer() { 7032 if (nodeLinkFn) nodeLinkFn(vm, el, host, scope, frag); 7033 if (childLinkFn) childLinkFn(vm, childNodes, host, scope, frag); 7034 }, vm); 7035 return makeUnlinkFn(vm, dirs); 7036 }; 7037} 7038 7039/** 7040 * Apply a linker to a vm/element pair and capture the 7041 * directives created during the process. 7042 * 7043 * @param {Function} linker 7044 * @param {Vue} vm 7045 */ 7046 7047function linkAndCapture(linker, vm) { 7048 /* istanbul ignore if */ 7049 if ('development' === 'production') {} 7050 var originalDirCount = vm._directives.length; 7051 linker(); 7052 var dirs = vm._directives.slice(originalDirCount); 7053 sortDirectives(dirs); 7054 for (var i = 0, l = dirs.length; i < l; i++) { 7055 dirs[i]._bind(); 7056 } 7057 return dirs; 7058} 7059 7060/** 7061 * sort directives by priority (stable sort) 7062 * 7063 * @param {Array} dirs 7064 */ 7065function sortDirectives(dirs) { 7066 if (dirs.length === 0) return; 7067 7068 var groupedMap = {}; 7069 var i, j, k, l; 7070 var index = 0; 7071 var priorities = []; 7072 for (i = 0, j = dirs.length; i < j; i++) { 7073 var dir = dirs[i]; 7074 var priority = dir.descriptor.def.priority || DEFAULT_PRIORITY; 7075 var array = groupedMap[priority]; 7076 if (!array) { 7077 array = groupedMap[priority] = []; 7078 priorities.push(priority); 7079 } 7080 array.push(dir); 7081 } 7082 7083 priorities.sort(function (a, b) { 7084 return a > b ? -1 : a === b ? 0 : 1; 7085 }); 7086 for (i = 0, j = priorities.length; i < j; i++) { 7087 var group = groupedMap[priorities[i]]; 7088 for (k = 0, l = group.length; k < l; k++) { 7089 dirs[index++] = group[k]; 7090 } 7091 } 7092} 7093 7094/** 7095 * Linker functions return an unlink function that 7096 * tearsdown all directives instances generated during 7097 * the process. 7098 * 7099 * We create unlink functions with only the necessary 7100 * information to avoid retaining additional closures. 7101 * 7102 * @param {Vue} vm 7103 * @param {Array} dirs 7104 * @param {Vue} [context] 7105 * @param {Array} [contextDirs] 7106 * @return {Function} 7107 */ 7108 7109function makeUnlinkFn(vm, dirs, context, contextDirs) { 7110 function unlink(destroying) { 7111 teardownDirs(vm, dirs, destroying); 7112 if (context && contextDirs) { 7113 teardownDirs(context, contextDirs); 7114 } 7115 } 7116 // expose linked directives 7117 unlink.dirs = dirs; 7118 return unlink; 7119} 7120 7121/** 7122 * Teardown partial linked directives. 7123 * 7124 * @param {Vue} vm 7125 * @param {Array} dirs 7126 * @param {Boolean} destroying 7127 */ 7128 7129function teardownDirs(vm, dirs, destroying) { 7130 var i = dirs.length; 7131 while (i--) { 7132 dirs[i]._teardown(); 7133 if ('development' !== 'production' && !destroying) { 7134 vm._directives.$remove(dirs[i]); 7135 } 7136 } 7137} 7138 7139/** 7140 * Compile link props on an instance. 7141 * 7142 * @param {Vue} vm 7143 * @param {Element} el 7144 * @param {Object} props 7145 * @param {Object} [scope] 7146 * @return {Function} 7147 */ 7148 7149function compileAndLinkProps(vm, el, props, scope) { 7150 var propsLinkFn = compileProps(el, props, vm); 7151 var propDirs = linkAndCapture(function () { 7152 propsLinkFn(vm, scope); 7153 }, vm); 7154 return makeUnlinkFn(vm, propDirs); 7155} 7156 7157/** 7158 * Compile the root element of an instance. 7159 * 7160 * 1. attrs on context container (context scope) 7161 * 2. attrs on the component template root node, if 7162 * replace:true (child scope) 7163 * 7164 * If this is a fragment instance, we only need to compile 1. 7165 * 7166 * @param {Element} el 7167 * @param {Object} options 7168 * @param {Object} contextOptions 7169 * @return {Function} 7170 */ 7171 7172function compileRoot(el, options, contextOptions) { 7173 var containerAttrs = options._containerAttrs; 7174 var replacerAttrs = options._replacerAttrs; 7175 var contextLinkFn, replacerLinkFn; 7176 7177 // only need to compile other attributes for 7178 // non-fragment instances 7179 if (el.nodeType !== 11) { 7180 // for components, container and replacer need to be 7181 // compiled separately and linked in different scopes. 7182 if (options._asComponent) { 7183 // 2. container attributes 7184 if (containerAttrs && contextOptions) { 7185 contextLinkFn = compileDirectives(containerAttrs, contextOptions); 7186 } 7187 if (replacerAttrs) { 7188 // 3. replacer attributes 7189 replacerLinkFn = compileDirectives(replacerAttrs, options); 7190 } 7191 } else { 7192 // non-component, just compile as a normal element. 7193 replacerLinkFn = compileDirectives(el.attributes, options); 7194 } 7195 } else if ('development' !== 'production' && containerAttrs) { 7196 // warn container directives for fragment instances 7197 var names = containerAttrs.filter(function (attr) { 7198 // allow vue-loader/vueify scoped css attributes 7199 return attr.name.indexOf('_v-') < 0 && 7200 // allow event listeners 7201 !onRE.test(attr.name) && 7202 // allow slots 7203 attr.name !== 'slot'; 7204 }).map(function (attr) { 7205 return '"' + attr.name + '"'; 7206 }); 7207 if (names.length) { 7208 var plural = names.length > 1; 7209 7210 var componentName = options.el.tagName.toLowerCase(); 7211 if (componentName === 'component' && options.name) { 7212 componentName += ':' + options.name; 7213 } 7214 7215 warn('Attribute' + (plural ? 's ' : ' ') + names.join(', ') + (plural ? ' are' : ' is') + ' ignored on component ' + '<' + componentName + '> because ' + 'the component is a fragment instance: ' + 'http://vuejs.org/guide/components.html#Fragment-Instance'); 7216 } 7217 } 7218 7219 options._containerAttrs = options._replacerAttrs = null; 7220 return function rootLinkFn(vm, el, scope) { 7221 // link context scope dirs 7222 var context = vm._context; 7223 var contextDirs; 7224 if (context && contextLinkFn) { 7225 contextDirs = linkAndCapture(function () { 7226 contextLinkFn(context, el, null, scope); 7227 }, context); 7228 } 7229 7230 // link self 7231 var selfDirs = linkAndCapture(function () { 7232 if (replacerLinkFn) replacerLinkFn(vm, el); 7233 }, vm); 7234 7235 // return the unlink function that tearsdown context 7236 // container directives. 7237 return makeUnlinkFn(vm, selfDirs, context, contextDirs); 7238 }; 7239} 7240 7241/** 7242 * Compile a node and return a nodeLinkFn based on the 7243 * node type. 7244 * 7245 * @param {Node} node 7246 * @param {Object} options 7247 * @return {Function|null} 7248 */ 7249 7250function compileNode(node, options) { 7251 var type = node.nodeType; 7252 if (type === 1 && !isScript(node)) { 7253 return compileElement(node, options); 7254 } else if (type === 3 && node.data.trim()) { 7255 return compileTextNode(node, options); 7256 } else { 7257 return null; 7258 } 7259} 7260 7261/** 7262 * Compile an element and return a nodeLinkFn. 7263 * 7264 * @param {Element} el 7265 * @param {Object} options 7266 * @return {Function|null} 7267 */ 7268 7269function compileElement(el, options) { 7270 // preprocess textareas. 7271 // textarea treats its text content as the initial value. 7272 // just bind it as an attr directive for value. 7273 if (el.tagName === 'TEXTAREA') { 7274 // a textarea which has v-pre attr should skip complie. 7275 if (getAttr(el, 'v-pre') !== null) { 7276 return skip; 7277 } 7278 var tokens = parseText(el.value); 7279 if (tokens) { 7280 el.setAttribute(':value', tokensToExp(tokens)); 7281 el.value = ''; 7282 } 7283 } 7284 var linkFn; 7285 var hasAttrs = el.hasAttributes(); 7286 var attrs = hasAttrs && toArray(el.attributes); 7287 // check terminal directives (for & if) 7288 if (hasAttrs) { 7289 linkFn = checkTerminalDirectives(el, attrs, options); 7290 } 7291 // check element directives 7292 if (!linkFn) { 7293 linkFn = checkElementDirectives(el, options); 7294 } 7295 // check component 7296 if (!linkFn) { 7297 linkFn = checkComponent(el, options); 7298 } 7299 // normal directives 7300 if (!linkFn && hasAttrs) { 7301 linkFn = compileDirectives(attrs, options); 7302 } 7303 return linkFn; 7304} 7305 7306/** 7307 * Compile a textNode and return a nodeLinkFn. 7308 * 7309 * @param {TextNode} node 7310 * @param {Object} options 7311 * @return {Function|null} textNodeLinkFn 7312 */ 7313 7314function compileTextNode(node, options) {
7315 // skip marked text nodes 7316 if (node._skip) { 7317 return removeText; 7318 } 7319 7320 var tokens = parseText(node.wholeText); 7321 if (!tokens) { 7322 return null; 7323 } 7324 7325 // mark adjacent text nodes as skipped, 7326 // because we are using node.wholeText to compile 7327 // all adjacent text nodes together. This fixes 7328 // issues in IE where sometimes it splits up a single 7329 // text node into multiple ones. 7330 var next = node.nextSibling; 7331 while (next && next.nodeType === 3) { 7332 next._skip = true; 7333 next = next.nextSibling; 7334 } 7335 7336 var frag = document.createDocumentFragment(); 7337 var el, token; 7338 for (var i = 0, l = tokens.length; i < l; i++) { 7339 token = tokens[i]; 7340 el = token.tag ? processTextToken(token, options) : document.createTextNode(token.value); 7341 frag.appendChild(el); 7342 } 7343 return makeTextNodeLinkFn(tokens, frag, options); 7344} 7345 7346/** 7347 * Linker for an skipped text node. 7348 * 7349 * @param {Vue} vm 7350 * @param {Text} node 7351 */ 7352 7353function removeText(vm, node) { 7354 remove(node); 7355} 7356 7357/** 7358 * Process a single text token. 7359 * 7360 * @param {Object} token 7361 * @param {Object} options 7362 * @return {Node} 7363 */ 7364 7365function processTextToken(token, options) { 7366 var el; 7367 if (token.oneTime) { 7368 el = document.createTextNode(token.value); 7369 } else { 7370 if (token.html) { 7371 el = document.createComment('v-html'); 7372 setTokenType('html'); 7373 } else { 7374 // IE will clean up empty textNodes during 7375 // frag.cloneNode(true), so we have to give it 7376 // something here... 7377 el = document.createTextNode(' '); 7378 setTokenType('text'); 7379 } 7380 } 7381 function setTokenType(type) { 7382 if (token.descriptor) return; 7383 var parsed = parseDirective(token.value); 7384 token.descriptor = { 7385 name: type, 7386 def: directives[type], 7387 expression: parsed.expression, 7388 filters: parsed.filters 7389 }; 7390 } 7391 return el; 7392} 7393 7394/** 7395 * Build a function that processes a textNode. 7396 * 7397 * @param {Array<Object>} tokens 7398 * @param {DocumentFragment} frag 7399 */ 7400 7401function makeTextNodeLinkFn(tokens, frag) { 7402 return function textNodeLinkFn(vm, el, host, scope) { 7403 var fragClone = frag.cloneNode(true); 7404 var childNodes = toArray(fragClone.childNodes); 7405 var token, value, node; 7406 for (var i = 0, l = tokens.length; i < l; i++) { 7407 token = tokens[i]; 7408 value = token.value; 7409 if (token.tag) { 7410 node = childNodes[i]; 7411 if (token.oneTime) { 7412 value = (scope || vm).$eval(value); 7413 if (token.html) { 7414 replace(node, parseTemplate(value, true)); 7415 } else { 7416 node.data = _toString(value); 7417 } 7418 } else { 7419 vm._bindDir(token.descriptor, node, host, scope); 7420 } 7421 } 7422 } 7423 replace(el, fragClone); 7424 }; 7425} 7426 7427/** 7428 * Compile a node list and return a childLinkFn. 7429 * 7430 * @param {NodeList} nodeList 7431 * @param {Object} options 7432 * @return {Function|undefined} 7433 */ 7434 7435function compileNodeList(nodeList, options) { 7436 var linkFns = []; 7437 var nodeLinkFn, childLinkFn, node; 7438 for (var i = 0, l = nodeList.length; i < l; i++) { 7439 node = nodeList[i]; 7440 nodeLinkFn = compileNode(node, options); 7441 childLinkFn = !(nodeLinkFn && nodeLinkFn.terminal) && node.tagName !== 'SCRIPT' && node.hasChildNodes() ? compileNodeList(node.childNodes, options) : null; 7442 linkFns.push(nodeLinkFn, childLinkFn); 7443 } 7444 return linkFns.length ? makeChildLinkFn(linkFns) : null; 7445} 7446 7447/** 7448 * Make a child link function for a node's childNodes. 7449 * 7450 * @param {Array<Function>} linkFns 7451 * @return {Function} childLinkFn 7452 */ 7453 7454function makeChildLinkFn(linkFns) { 7455 return function childLinkFn(vm, nodes, host, scope, frag) { 7456 var node, nodeLinkFn, childrenLinkFn; 7457 for (var i = 0, n = 0, l = linkFns.length; i < l; n++) { 7458 node = nodes[n]; 7459 nodeLinkFn = linkFns[i++]; 7460 childrenLinkFn = linkFns[i++];
7461 // cache childNodes before linking parent, fix #657 7462 var childNodes = toArray(node.childNodes); 7463 if (nodeLinkFn) { 7464 nodeLinkFn(vm, node, host, scope, frag); 7465 } 7466 if (childrenLinkFn) { 7467 childrenLinkFn(vm, childNodes, host, scope, frag); 7468 } 7469 } 7470 }; 7471} 7472 7473/** 7474 * Check for element directives (custom elements that should 7475 * be resovled as terminal directives). 7476 * 7477 * @param {Element} el 7478 * @param {Object} options 7479 */ 7480 7481function checkElementDirectives(el, options) { 7482 var tag = el.tagName.toLowerCase(); 7483 if (commonTagRE.test(tag)) { 7484 return; 7485 } 7486 var def = resolveAsset(options, 'elementDirectives', tag); 7487 if (def) { 7488 return makeTerminalNodeLinkFn(el, tag, '', options, def); 7489 } 7490} 7491 7492/** 7493 * Check if an element is a component. If yes, return 7494 * a component link function. 7495 * 7496 * @param {Element} el 7497 * @param {Object} options 7498 * @return {Function|undefined} 7499 */ 7500 7501function checkComponent(el, options) { 7502 var component = checkComponentAttr(el, options); 7503 if (component) { 7504 var ref = findRef(el); 7505 var descriptor = { 7506 name: 'component', 7507 ref: ref, 7508 expression: component.id, 7509 def: internalDirectives.component, 7510 modifiers: { 7511 literal: !component.dynamic 7512 } 7513 }; 7514 var componentLinkFn = function componentLinkFn(vm, el, host, scope, frag) { 7515 if (ref) { 7516 defineReactive((scope || vm).$refs, ref, null); 7517 } 7518 vm._bindDir(descriptor, el, host, scope, frag); 7519 }; 7520 componentLinkFn.terminal = true; 7521 return componentLinkFn; 7522 } 7523} 7524 7525/** 7526 * Check an element for terminal directives in fixed order. 7527 * If it finds one, return a terminal link function. 7528 * 7529 * @param {Element} el 7530 * @param {Array} attrs 7531 * @param {Object} options 7532 * @return {Function} terminalLinkFn 7533 */ 7534 7535function checkTerminalDirectives(el, attrs, options) { 7536 // skip v-pre 7537 if (getAttr(el, 'v-pre') !== null) { 7538 return skip; 7539 }
7540 // skip v-else block, but only if following v-if 7541 if (el.hasAttribute('v-else')) { 7542 var prev = el.previousElementSibling; 7543 if (prev && prev.hasAttribute('v-if')) { 7544 return skip; 7545 } 7546 } 7547 7548 var attr, name, value, modifiers, matched, dirName, rawName, arg, def, termDef; 7549 for (var i = 0, j = attrs.length; i < j; i++) { 7550 attr = attrs[i]; 7551 name = attr.name.replace(modifierRE, ''); 7552 if (matched = name.match(dirAttrRE)) { 7553 def = resolveAsset(options, 'directives', matched[1]); 7554 if (def && def.terminal) { 7555 if (!termDef || (def.priority || DEFAULT_TERMINAL_PRIORITY) > termDef.priority) { 7556 termDef = def; 7557 rawName = attr.name; 7558 modifiers = parseModifiers(attr.name); 7559 value = attr.value; 7560 dirName = matched[1]; 7561 arg = matched[2]; 7562 } 7563 } 7564 } 7565 } 7566 7567 if (termDef) { 7568 return makeTerminalNodeLinkFn(el, dirName, value, options, termDef, rawName, arg, modifiers); 7569 } 7570} 7571 7572function skip() {} 7573skip.terminal = true; 7574 7575/** 7576 * Build a node link function for a terminal directive. 7577 * A terminal link function terminates the current 7578 * compilation recursion and handles compilation of the 7579 * subtree in the directive. 7580 * 7581 * @param {Element} el 7582 * @param {String} dirName 7583 * @param {String} value 7584 * @param {Object} options 7585 * @param {Object} def 7586 * @param {String} [rawName] 7587 * @param {String} [arg] 7588 * @param {Object} [modifiers] 7589 * @return {Function} terminalLinkFn 7590 */ 7591 7592function makeTerminalNodeLinkFn(el, dirName, value, options, def, rawName, arg, modifiers) { 7593 var parsed = parseDirective(value); 7594 var descriptor = { 7595 name: dirName, 7596 arg: arg, 7597 expression: parsed.expression, 7598 filters: parsed.filters, 7599 raw: value, 7600 attr: rawName, 7601 modifiers: modifiers, 7602 def: def 7603 }; 7604 // check ref for v-for, v-if and router-view 7605 if (dirName === 'for' || dirName === 'router-view') { 7606 descriptor.ref = findRef(el); 7607 } 7608 var fn = function terminalNodeLinkFn(vm, el, host, scope, frag) { 7609 if (descriptor.ref) { 7610 defineReactive((scope || vm).$refs, descriptor.ref, null); 7611 } 7612 vm._bindDir(descriptor, el, host, scope, frag); 7613 }; 7614 fn.terminal = true; 7615 return fn; 7616} 7617 7618/** 7619 * Compile the directives on an element and return a linker. 7620 * 7621 * @param {Array|NamedNodeMap} attrs 7622 * @param {Object} options 7623 * @return {Function} 7624 */ 7625 7626function compileDirectives(attrs, options) { 7627 var i = attrs.length; 7628 var dirs = []; 7629 var attr, name, value, rawName, rawValue, dirName, arg, modifiers, dirDef, tokens, matched; 7630 while (i--) { 7631 attr = attrs[i]; 7632 name = rawName = attr.name; 7633 value = rawValue = attr.value; 7634 tokens = parseText(value); 7635 // reset arg 7636 arg = null; 7637 // check modifiers 7638 modifiers = parseModifiers(name); 7639 name = name.replace(modifierRE, ''); 7640 7641 // attribute interpolations 7642 if (tokens) { 7643 value = tokensToExp(tokens); 7644 arg = name; 7645 pushDir('bind', directives.bind, tokens); 7646 // warn against mixing mustaches with v-bind 7647 if ('development' !== 'production') { 7648 if (name === 'class' && Array.prototype.some.call(attrs, function (attr) { 7649 return attr.name === ':class' || attr.name === 'v-bind:class'; 7650 })) { 7651 warn('class="' + rawValue + '": Do not mix mustache interpolation ' + 'and v-bind for "class" on the same element. Use one or the other.', options); 7652 } 7653 } 7654 } else 7655 7656 // special attribute: transition 7657 if (transitionRE.test(name)) { 7658 modifiers.literal = !bindRE.test(name); 7659 pushDir('transition', internalDirectives.transition); 7660 } else 7661 7662 // event handlers 7663 if (onRE.test(name)) { 7664 arg = name.replace(onRE, ''); 7665 pushDir('on', directives.on); 7666 } else 7667 7668 // attribute bindings 7669 if (bindRE.test(name)) { 7670 dirName = name.replace(bindRE, ''); 7671 if (dirName === 'style' || dirName === 'class') { 7672 pushDir(dirName, internalDirectives[dirName]); 7673 } else { 7674 arg = dirName; 7675 pushDir('bind', directives.bind); 7676 } 7677 } else 7678 7679 // normal directives 7680 if (matched = name.match(dirAttrRE)) { 7681 dirName = matched[1]; 7682 arg = matched[2]; 7683 7684 // skip v-else (when used with v-show) 7685 if (dirName === 'else') { 7686 continue; 7687 } 7688 7689 dirDef = resolveAsset(options, 'directives', dirName, true); 7690 if (dirDef) { 7691 pushDir(dirName, dirDef); 7692 } 7693 } 7694 } 7695 7696 /** 7697 * Push a directive. 7698 * 7699 * @param {String} dirName 7700 * @param {Object|Function} def 7701 * @param {Array} [interpTokens] 7702 */ 7703 7704 function pushDir(dirName, def, interpTokens) { 7705 var hasOneTimeToken = interpTokens && hasOneTime(interpTokens); 7706 var parsed = !hasOneTimeToken && parseDirective(value); 7707 dirs.push({ 7708 name: dirName, 7709 attr: rawName, 7710 raw: rawValue, 7711 def: def, 7712 arg: arg, 7713 modifiers: modifiers, 7714 // conversion from interpolation strings with one-time token 7715 // to expression is differed until directive bind time so that we 7716 // have access to the actual vm context for one-time bindings. 7717 expression: parsed && parsed.expression, 7718 filters: parsed && parsed.filters, 7719 interp: interpTokens, 7720 hasOneTime: hasOneTimeToken 7721 }); 7722 } 7723 7724 if (dirs.length) { 7725 return makeNodeLinkFn(dirs); 7726 } 7727} 7728 7729/** 7730 * Parse modifiers from directive attribute name. 7731 * 7732 * @param {String} name 7733 * @return {Object} 7734 */ 7735 7736function parseModifiers(name) { 7737 var res = Object.create(null); 7738 var match = name.match(modifierRE); 7739 if (match) { 7740 var i = match.length; 7741 while (i--) { 7742 res[match[i].slice(1)] = true; 7743 } 7744 } 7745 return res; 7746} 7747 7748/**
7749 * Build a link function for all directives on a single node. 7750 * 7751 * @param {Array} directives 7752 * @return {Function} directivesLinkFn 7753 */ 7754 7755function makeNodeLinkFn(directives) { 7756 return function nodeLinkFn(vm, el, host, scope, frag) { 7757 // reverse apply because it's sorted low to high 7758 var i = directives.length; 7759 while (i--) { 7760 vm._bindDir(directives[i], el, host, scope, frag); 7761 } 7762 }; 7763} 7764 7765/** 7766 * Check if an interpolation string contains one-time tokens. 7767 * 7768 * @param {Array} tokens 7769 * @return {Boolean} 7770 */ 7771 7772function hasOneTime(tokens) { 7773 var i = tokens.length; 7774 while (i--) { 7775 if (tokens[i].oneTime) return true; 7776 } 7777} 7778 7779function isScript(el) { 7780 return el.tagName === 'SCRIPT' && (!el.hasAttribute('type') || el.getAttribute('type') === 'text/javascript'); 7781} 7782 7783var specialCharRE = /[^\w\-:\.]/; 7784 7785/** 7786 * Process an element or a DocumentFragment based on a 7787 * instance option object. This allows us to transclude 7788 * a template node/fragment before the instance is created, 7789 * so the processed fragment can then be cloned and reused 7790 * in v-for. 7791 * 7792 * @param {Element} el 7793 * @param {Object} options 7794 * @return {Element|DocumentFragment} 7795 */ 7796 7797function transclude(el, options) { 7798 // extract container attributes to pass them down 7799 // to compiler, because they need to be compiled in 7800 // parent scope. we are mutating the options object here 7801 // assuming the same object will be used for compile 7802 // right after this. 7803 if (options) { 7804 options._containerAttrs = extractAttrs(el); 7805 } 7806 // for template tags, what we want is its content as 7807 // a documentFragment (for fragment instances) 7808 if (isTemplate(el)) { 7809 el = parseTemplate(el); 7810 } 7811 if (options) { 7812 if (options._asComponent && !options.template) { 7813 options.template = '<slot></slot>'; 7814 } 7815 if (options.template) { 7816 options._content = extractContent(el); 7817 el = transcludeTemplate(el, options); 7818 } 7819 } 7820 if (isFragment(el)) { 7821 // anchors for fragment instance 7822 // passing in `persist: true` to avoid them being 7823 // discarded by IE during template cloning 7824 prepend(createAnchor('v-start', true), el); 7825 el.appendChild(createAnchor('v-end', true)); 7826 } 7827 return el; 7828} 7829 7830/** 7831 * Process the template option. 7832 * If the replace option is true this will swap the $el. 7833 * 7834 * @param {Element} el 7835 * @param {Object} options 7836 * @return {Element|DocumentFragment} 7837 */ 7838 7839function transcludeTemplate(el, options) { 7840 var template = options.template; 7841 var frag = parseTemplate(template, true); 7842 if (frag) { 7843 var replacer = frag.firstChild; 7844 if (!replacer) { 7845 return frag; 7846 } 7847 var tag = replacer.tagName && replacer.tagName.toLowerCase(); 7848 if (options.replace) { 7849 /* istanbul ignore if */ 7850 if (el === document.body) { 7851 'development' !== 'production' && warn('You are mounting an instance with a template to ' + '<body>. This will replace <body> entirely. You ' + 'should probably use `replace: false` here.'); 7852 } 7853 // there are many cases where the instance must 7854 // become a fragment instance: basically anything that 7855 // can create more than 1 root nodes. 7856 if ( 7857 // multi-children template 7858 frag.childNodes.length > 1 || 7859 // non-element template 7860 replacer.nodeType !== 1 || 7861 // single nested component 7862 tag === 'component' || resolveAsset(options, 'components', tag) || hasBindAttr(replacer, 'is') || 7863 // element directive 7864 resolveAsset(options, 'elementDirectives', tag) || 7865 // for block 7866 replacer.hasAttribute('v-for') || 7867 // if block 7868 replacer.hasAttribute('v-if')) { 7869 return frag; 7870 } else { 7871 options._replacerAttrs = extractAttrs(replacer); 7872 mergeAttrs(el, replacer); 7873 return replacer; 7874 } 7875 } else { 7876 el.appendChild(frag); 7877 return el; 7878 } 7879 } else { 7880 'development' !== 'production' && warn('Invalid template option: ' + template); 7881 } 7882} 7883 7884/** 7885 * Helper to extract a component container's attributes 7886 * into a plain object array. 7887 * 7888 * @param {Element} el 7889 * @return {Array} 7890 */ 7891 7892function extractAttrs(el) {
7893 if (el.nodeType === 1 && el.hasAttributes()) { 7894 return toArray(el.attributes); 7895 } 7896} 7897 7898/** 7899 * Merge the attributes of two elements, and make sure 7900 * the class names are merged properly. 7901 * 7902 * @param {Element} from 7903 * @param {Element} to 7904 */ 7905 7906function mergeAttrs(from, to) { 7907 var attrs = from.attributes; 7908 var i = attrs.length; 7909 var name, value; 7910 while (i--) { 7911 name = attrs[i].name; 7912 value = attrs[i].value; 7913 if (!to.hasAttribute(name) && !specialCharRE.test(name)) { 7914 to.setAttribute(name, value); 7915 } else if (name === 'class' && !parseText(value) && (value = value.trim())) { 7916 value.split(/\s+/).forEach(function (cls) { 7917 addClass(to, cls); 7918 }); 7919 } 7920 } 7921} 7922 7923/** 7924 * Scan and determine slot content distribution. 7925 * We do this during transclusion instead at compile time so that 7926 * the distribution is decoupled from the compilation order of 7927 * the slots. 7928 * 7929 * @param {Element|DocumentFragment} template 7930 * @param {Element} content 7931 * @param {Vue} vm 7932 */ 7933 7934function resolveSlots(vm, content) { 7935 if (!content) { 7936 return; 7937 } 7938 var contents = vm._slotContents = Object.create(null); 7939 var el, name; 7940 for (var i = 0, l = content.children.length; i < l; i++) { 7941 el = content.children[i]; 7942 /* eslint-disable no-cond-assign */ 7943 if (name = el.getAttribute('slot')) { 7944 (contents[name] || (contents[name] = [])).push(el); 7945 } 7946 /* eslint-enable no-cond-assign */ 7947 if ('development' !== 'production' && getBindAttr(el, 'slot')) { 7948 warn('The "slot" attribute must be static.', vm.$parent); 7949 } 7950 } 7951 for (name in contents) { 7952 contents[name] = extractFragment(contents[name], content); 7953 } 7954 if (content.hasChildNodes()) { 7955 var nodes = content.childNodes; 7956 if (nodes.length === 1 && nodes[0].nodeType === 3 && !nodes[0].data.trim()) { 7957 return; 7958 } 7959 contents['default'] = extractFragment(content.childNodes, content); 7960 } 7961} 7962 7963/** 7964 * Extract qualified content nodes from a node list. 7965 * 7966 * @param {NodeList} nodes 7967 * @return {DocumentFragment} 7968 */ 7969 7970function extractFragment(nodes, parent) { 7971 var frag = document.createDocumentFragment(); 7972 nodes = toArray(nodes); 7973 for (var i = 0, l = nodes.length; i < l; i++) { 7974 var node = nodes[i]; 7975 if (isTemplate(node) && !node.hasAttribute('v-if') && !node.hasAttribute('v-for')) { 7976 parent.removeChild(node); 7977 node = parseTemplate(node, true); 7978 } 7979 frag.appendChild(node); 7980 } 7981 return frag; 7982} 7983 7984 7985 7986var compiler = Object.freeze({ 7987 compile: compile, 7988 compileAndLinkProps: compileAndLinkProps, 7989 compileRoot: compileRoot, 7990 transclude: transclude, 7991 resolveSlots: resolveSlots 7992}); 7993 7994function stateMixin (Vue) { 7995 /** 7996 * Accessor for `$data` property, since setting $data 7997 * requires observing the new object and updating 7998 * proxied properties. 7999 */ 8000 8001 Object.defineProperty(Vue.prototype, '$data', { 8002 get: function get() { 8003 return this._data; 8004 }, 8005 set: function set(newData) { 8006 if (newData !== this._data) { 8007 this._setData(newData); 8008 } 8009 } 8010 }); 8011 8012 /** 8013 * Setup the scope of an instance, which contains: 8014 * - observed data 8015 * - computed properties 8016 * - user methods 8017 * - meta properties 8018 */ 8019 8020 Vue.prototype._initState = function () { 8021 this._initProps(); 8022 this._initMeta(); 8023 this._initMethods(); 8024 this._initData(); 8025 this._initComputed(); 8026 }; 8027 8028 /** 8029 * Initialize props. 8030 */ 8031 8032 Vue.prototype._initProps = function () { 8033 var options = this.$options; 8034 var el = options.el; 8035 var props = options.props; 8036 if (props && !el) { 8037 'development' !== 'production' && warn('Props will not be compiled if no `el` option is ' + 'provided at instantiation.', this); 8038 } 8039 // make sure to convert string selectors into element now 8040 el = options.el = query(el); 8041 this._propsUnlinkFn = el && el.nodeType === 1 && props 8042 // props must be linked in proper scope if inside v-for 8043 ? compileAndLinkProps(this, el, props, this._scope) : null; 8044 }; 8045 8046 /** 8047 * Initialize the data. 8048 */ 8049 8050 Vue.prototype._initData = function () { 8051 var dataFn = this.$options.data; 8052 var data = this._data = dataFn ? dataFn() : {}; 8053 if (!isPlainObject(data)) { 8054 data = {}; 8055 'development' !== 'production' && warn('data functions should return an object.', this); 8056 } 8057 var props = this._props; 8058 // proxy data on instance 8059 var keys = Object.keys(data); 8060 var i, key; 8061 i = keys.length; 8062 while (i--) { 8063 key = keys[i]; 8064 // there are two scenarios where we can proxy a data key: 8065 // 1. it's not already defined as a prop 8066 // 2. it's provided via a instantiation option AND there are no 8067 // template prop present 8068 if (!props || !hasOwn(props, key)) { 8069 this._proxy(key); 8070 } else if ('development' !== 'production') { 8071 warn('Data field "' + key + '" is already defined ' + 'as a prop. To provide default value for a prop, use the "default" ' + 'prop option; if you want to pass prop values to an instantiation ' + 'call, use the "propsData" option.', this); 8072 } 8073 } 8074 // observe data 8075 observe(data, this); 8076 }; 8077 8078 /** 8079 * Swap the instance's $data. Called in $data's setter. 8080 * 8081 * @param {Object} newData 8082 */ 8083 8084 Vue.prototype._setData = function (newData) { 8085 newData = newData || {}; 8086 var oldData = this._data; 8087 this._data = newData; 8088 var keys, key, i; 8089 // unproxy keys not present in new data 8090 keys = Object.keys(oldData); 8091 i = keys.length; 8092 while (i--) { 8093 key = keys[i]; 8094 if (!(key in newData)) { 8095 this._unproxy(key); 8096 } 8097 } 8098 // proxy keys not already proxied, 8099 // and trigger change for changed values 8100 keys = Object.keys(newData); 8101 i = keys.length; 8102 while (i--) { 8103 key = keys[i]; 8104 if (!hasOwn(this, key)) { 8105 // new property 8106 this._proxy(key); 8107 } 8108 } 8109 oldData.__ob__.removeVm(this); 8110 observe(newData, this); 8111 this._digest(); 8112 }; 8113 8114 /** 8115 * Proxy a property, so that 8116 * vm.prop === vm._data.prop 8117 * 8118 * @param {String} key 8119 */ 8120 8121 Vue.prototype._proxy = function (key) { 8122 if (!isReserved(key)) { 8123 // need to store ref to self here 8124 // because these getter/setters might 8125 // be called by child scopes via 8126 // prototype inheritance. 8127 var self = this; 8128 Object.defineProperty(self, key, { 8129 configurable: true, 8130 enumerable: true, 8131 get: function proxyGetter() { 8132 return self._data[key]; 8133 }, 8134 set: function proxySetter(val) { 8135 self._data[key] = val; 8136 } 8137 }); 8138 } 8139 }; 8140 8141 /** 8142 * Unproxy a property. 8143 * 8144 * @param {String} key 8145 */ 8146 8147 Vue.prototype._unproxy = function (key) { 8148 if (!isReserved(key)) { 8149 delete this[key]; 8150 } 8151 }; 8152 8153 /** 8154 * Force update on every watcher in scope. 8155 */ 8156 8157 Vue.prototype._digest = function () { 8158 for (var i = 0, l = this._watchers.length; i < l; i++) { 8159 this._watchers[i].update(true); // shallow updates 8160 } 8161 }; 8162 8163 /** 8164 * Setup computed properties. They are essentially 8165 * special getter/setters 8166 */ 8167 8168 function noop() {} 8169 Vue.prototype._initComputed = function () { 8170 var computed = this.$options.computed; 8171 if (computed) { 8172 for (var key in computed) { 8173 var userDef = computed[key]; 8174 var def = { 8175 enumerable: true, 8176 configurable: true 8177 }; 8178 if (typeof userDef === 'function') { 8179 def.get = makeComputedGetter(userDef, this); 8180 def.set = noop; 8181 } else { 8182 def.get = userDef.get ? userDef.cache !== false ? makeComputedGetter(userDef.get, this) : bind(userDef.get, this) : noop; 8183 def.set = userDef.set ? bind(userDef.set, this) : noop; 8184 } 8185 Object.defineProperty(this, key, def); 8186 } 8187 } 8188 }; 8189 8190 function makeComputedGetter(getter, owner) { 8191 var watcher = new Watcher(owner, getter, null, { 8192 lazy: true 8193 }); 8194 return function computedGetter() { 8195 if (watcher.dirty) { 8196 watcher.evaluate(); 8197 } 8198 if (Dep.target) {
8199 watcher.depend(); 8200 } 8201 return watcher.value; 8202 }; 8203 } 8204 8205 /** 8206 * Setup instance methods. Methods must be bound to the 8207 * instance since they might be passed down as a prop to 8208 * child components. 8209 */ 8210 8211 Vue.prototype._initMethods = function () { 8212 var methods = this.$options.methods; 8213 if (methods) { 8214 for (var key in methods) { 8215 this[key] = bind(methods[key], this); 8216 } 8217 } 8218 }; 8219 8220 /** 8221 * Initialize meta information like $index, $key & $value. 8222 */ 8223 8224 Vue.prototype._initMeta = function () { 8225 var metas = this.$options._meta; 8226 if (metas) { 8227 for (var key in metas) { 8228 defineReactive(this, key, metas[key]); 8229 } 8230 } 8231 }; 8232} 8233 8234var eventRE = /^v-on:|^@/; 8235 8236function eventsMixin (Vue) { 8237 /** 8238 * Setup the instance's option events & watchers. 8239 * If the value is a string, we pull it from the 8240 * instance's methods by name. 8241 */ 8242 8243 Vue.prototype._initEvents = function () { 8244 var options = this.$options; 8245 if (options._asComponent) { 8246 registerComponentEvents(this, options.el); 8247 } 8248 registerCallbacks(this, '$on', options.events); 8249 registerCallbacks(this, '$watch', options.watch); 8250 }; 8251 8252 /** 8253 * Register v-on events on a child component 8254 * 8255 * @param {Vue} vm 8256 * @param {Element} el 8257 */ 8258 8259 function registerComponentEvents(vm, el) { 8260 var attrs = el.attributes; 8261 var name, value, handler; 8262 for (var i = 0, l = attrs.length; i < l; i++) { 8263 name = attrs[i].name; 8264 if (eventRE.test(name)) { 8265 name = name.replace(eventRE, ''); 8266 // force the expression into a statement so that 8267 // it always dynamically resolves the method to call (#2670) 8268 // kinda ugly hack, but does the job. 8269 value = attrs[i].value; 8270 if (isSimplePath(value)) { 8271 value += '.apply(this, $arguments)'; 8272 } 8273 handler = (vm._scope || vm._context).$eval(value, true); 8274 handler._fromParent = true; 8275 vm.$on(name.replace(eventRE), handler); 8276 } 8277 } 8278 } 8279 8280 /** 8281 * Register callbacks for option events and watchers. 8282 * 8283 * @param {Vue} vm 8284 * @param {String} action 8285 * @param {Object} hash 8286 */ 8287 8288 function registerCallbacks(vm, action, hash) { 8289 if (!hash) return; 8290 var handlers, key, i, j; 8291 for (key in hash) { 8292 handlers = hash[key]; 8293 if (isArray(handlers)) { 8294 for (i = 0, j = handlers.length; i < j; i++) { 8295 register(vm, action, key, handlers[i]); 8296 } 8297 } else { 8298 register(vm, action, key, handlers); 8299 } 8300 } 8301 } 8302 8303 /** 8304 * Helper to register an event/watch callback. 8305 * 8306 * @param {Vue} vm 8307 * @param {String} action 8308 * @param {String} key 8309 * @param {Function|String|Object} handler 8310 * @param {Object} [options] 8311 */ 8312 8313 function register(vm, action, key, handler, options) { 8314 var type = typeof handler; 8315 if (type === 'function') { 8316 vm[action](key, handler, options); 8317 } else if (type === 'string') { 8318 var methods = vm.$options.methods; 8319 var method = methods && methods[handler]; 8320 if (method) { 8321 vm[action](key, method, options); 8322 } else { 8323 'development' !== 'production' && warn('Unknown method: "' + handler + '" when ' + 'registering callback for ' + action + ': "' + key + '".', vm); 8324 } 8325 } else if (handler && type === 'object') { 8326 register(vm, action, key, handler.handler, handler); 8327 } 8328 } 8329 8330 /** 8331 * Setup recursive attached/detached calls 8332 */ 8333 8334 Vue.prototype._initDOMHooks = function () { 8335 this.$on('hook:attached', onAttached); 8336 this.$on('hook:detached', onDetached); 8337 }; 8338 8339 /** 8340 * Callback to recursively call attached hook on children 8341 */ 8342 8343 function onAttached() { 8344 if (!this._isAttached) { 8345 this._isAttached = true; 8346 this.$children.forEach(callAttach); 8347 } 8348 } 8349 8350 /** 8351 * Iterator to call attached hook 8352 * 8353 * @param {Vue} child 8354 */ 8355 8356 function callAttach(child) { 8357 if (!child._isAttached && inDoc(child.$el)) { 8358 child._callHook('attached'); 8359 } 8360 } 8361 8362 /** 8363 * Callback to recursively call detached hook on children 8364 */ 8365 8366 function onDetached() { 8367 if (this._isAttached) { 8368 this._isAttached = false;
8369 this.$children.forEach(callDetach); 8370 } 8371 } 8372 8373 /** 8374 * Iterator to call detached hook 8375 * 8376 * @param {Vue} child 8377 */ 8378 8379 function callDetach(child) { 8380 if (child._isAttached && !inDoc(child.$el)) { 8381 child._callHook('detached'); 8382 } 8383 } 8384 8385 /** 8386 * Trigger all handlers for a hook 8387 * 8388 * @param {String} hook 8389 */ 8390 8391 Vue.prototype._callHook = function (hook) { 8392 this.$emit('pre-hook:' + hook); 8393 var handlers = this.$options[hook]; 8394 if (handlers) { 8395 for (var i = 0, j = handlers.length; i < j; i++) { 8396 handlers[i].call(this); 8397 } 8398 } 8399 this.$emit('hook:' + hook); 8400 }; 8401} 8402 8403function noop$1() {} 8404 8405/** 8406 * A directive links a DOM element with a piece of data, 8407 * which is the result of evaluating an expression. 8408 * It registers a watcher with the expression and calls 8409 * the DOM update function when a change is triggered. 8410 * 8411 * @param {Object} descriptor 8412 * - {String} name 8413 * - {Object} def 8414 * - {String} expression 8415 * - {Array<Object>} [filters] 8416 * - {Object} [modifiers] 8417 * - {Boolean} literal 8418 * - {String} attr 8419 * - {String} arg 8420 * - {String} raw 8421 * - {String} [ref] 8422 * - {Array<Object>} [interp] 8423 * - {Boolean} [hasOneTime] 8424 * @param {Vue} vm 8425 * @param {Node} el 8426 * @param {Vue} [host] - transclusion host component 8427 * @param {Object} [scope] - v-for scope 8428 * @param {Fragment} [frag] - owner fragment 8429 * @constructor 8430 */ 8431function Directive(descriptor, vm, el, host, scope, frag) { 8432 this.vm = vm; 8433 this.el = el; 8434 // copy descriptor properties 8435 this.descriptor = descriptor; 8436 this.name = descriptor.name; 8437 this.expression = descriptor.expression; 8438 this.arg = descriptor.arg; 8439 this.modifiers = descriptor.modifiers; 8440 this.filters = descriptor.filters; 8441 this.literal = this.modifiers && this.modifiers.literal; 8442 // private 8443 this._locked = false; 8444 this._bound = false; 8445 this._listeners = null; 8446 // link context 8447 this._host = host; 8448 this._scope = scope; 8449 this._frag = frag; 8450 // store directives on node in dev mode 8451 if ('development' !== 'production' && this.el) { 8452 this.el._vue_directives = this.el._vue_directives || []; 8453 this.el._vue_directives.push(this); 8454 } 8455} 8456 8457/** 8458 * Initialize the directive, mixin definition properties, 8459 * setup the watcher, call definition bind() and update() 8460 * if present. 8461 */ 8462 8463Directive.prototype._bind = function () { 8464 var name = this.name; 8465 var descriptor = this.descriptor; 8466 8467 // remove attribute 8468 if ((name !== 'cloak' || this.vm._isCompiled) && this.el && this.el.removeAttribute) { 8469 var attr = descriptor.attr || 'v-' + name; 8470 this.el.removeAttribute(attr); 8471 } 8472 8473 // copy def properties 8474 var def = descriptor.def; 8475 if (typeof def === 'function') { 8476 this.update = def; 8477 } else { 8478 extend(this, def); 8479 } 8480 8481 // setup directive params 8482 this._setupParams(); 8483 8484 // initial bind 8485 if (this.bind) { 8486 this.bind(); 8487 } 8488 this._bound = true; 8489 8490 if (this.literal) { 8491 this.update && this.update(descriptor.raw); 8492 } else if ((this.expression || this.modifiers) && (this.update || this.twoWay) && !this._checkStatement()) { 8493 // wrapped updater for context 8494 var dir = this; 8495 if (this.update) { 8496 this._update = function (val, oldVal) { 8497 if (!dir._locked) { 8498 dir.update(val, oldVal); 8499 } 8500 }; 8501 } else { 8502 this._update = noop$1; 8503 } 8504 var preProcess = this._preProcess ? bind(this._preProcess, this) : null; 8505 var postProcess = this._postProcess ? bind(this._postProcess, this) : null; 8506 var watcher = this._watcher = new Watcher(this.vm, this.expression, this._update, // callback 8507 { 8508 filters: this.filters, 8509 twoWay: this.twoWay, 8510 deep: this.deep, 8511 preProcess: preProcess, 8512 postProcess: postProcess, 8513 scope: this._scope 8514 }); 8515 // v-model with inital inline value need to sync back to 8516 // model instead of update to DOM on init. They would 8517 // set the afterBind hook to indicate that. 8518 if (this.afterBind) { 8519 this.afterBind(); 8520 } else if (this.update) { 8521 this.update(watcher.value); 8522 } 8523 } 8524}; 8525 8526/** 8527 * Setup all param attributes, e.g. track-by, 8528 * transition-mode, etc... 8529 */ 8530 8531Directive.prototype._setupParams = function () { 8532 if (!this.params) { 8533 return; 8534 } 8535 var params = this.params; 8536 // swap the params array with a fresh object. 8537 this.params = Object.create(null); 8538 var i = params.length; 8539 var key, val, mappedKey; 8540 while (i--) { 8541 key = hyphenate(params[i]); 8542 mappedKey = camelize(key); 8543 val = getBindAttr(this.el, key); 8544 if (val != null) { 8545 // dynamic 8546 this._setupParamWatcher(mappedKey, val); 8547 } else { 8548 // static 8549 val = getAttr(this.el, key); 8550 if (val != null) { 8551 this.params[mappedKey] = val === '' ? true : val; 8552 } 8553 } 8554 } 8555}; 8556 8557/**
8558 * Setup a watcher for a dynamic param. 8559 * 8560 * @param {String} key 8561 * @param {String} expression 8562 */ 8563 8564Directive.prototype._setupParamWatcher = function (key, expression) { 8565 var self = this; 8566 var called = false; 8567 var unwatch = (this._scope || this.vm).$watch(expression, function (val, oldVal) { 8568 self.params[key] = val; 8569 // since we are in immediate mode, 8570 // only call the param change callbacks if this is not the first update. 8571 if (called) { 8572 var cb = self.paramWatchers && self.paramWatchers[key]; 8573 if (cb) { 8574 cb.call(self, val, oldVal); 8575 } 8576 } else { 8577 called = true; 8578 } 8579 }, { 8580 immediate: true, 8581 user: false 8582 });(this._paramUnwatchFns || (this._paramUnwatchFns = [])).push(unwatch); 8583}; 8584 8585/** 8586 * Check if the directive is a function caller 8587 * and if the expression is a callable one. If both true, 8588 * we wrap up the expression and use it as the event 8589 * handler. 8590 * 8591 * e.g. on-click="a++" 8592 * 8593 * @return {Boolean} 8594 */ 8595 8596Directive.prototype._checkStatement = function () { 8597 var expression = this.expression; 8598 if (expression && this.acceptStatement && !isSimplePath(expression)) { 8599 var fn = parseExpression$1(expression).get; 8600 var scope = this._scope || this.vm; 8601 var handler = function handler(e) { 8602 scope.$event = e; 8603 fn.call(scope, scope); 8604 scope.$event = null; 8605 }; 8606 if (this.filters) { 8607 handler = scope._applyFilters(handler, null, this.filters); 8608 } 8609 this.update(handler); 8610 return true; 8611 } 8612}; 8613 8614/** 8615 * Set the corresponding value with the setter. 8616 * This should only be used in two-way directives 8617 * e.g. v-model. 8618 * 8619 * @param {*} value 8620 * @public 8621 */ 8622 8623Directive.prototype.set = function (value) { 8624 /* istanbul ignore else */ 8625 if (this.twoWay) { 8626 this._withLock(function () { 8627 this._watcher.set(value); 8628 }); 8629 } else if ('development' !== 'production') { 8630 warn('Directive.set() can only be used inside twoWay' + 'directives.'); 8631 } 8632}; 8633 8634/** 8635 * Execute a function while preventing that function from 8636 * triggering updates on this directive instance. 8637 * 8638 * @param {Function} fn 8639 */ 8640 8641Directive.prototype._withLock = function (fn) { 8642 var self = this; 8643 self._locked = true; 8644 fn.call(self); 8645 nextTick(function () { 8646 self._locked = false; 8647 }); 8648}; 8649 8650/** 8651 * Convenience method that attaches a DOM event listener 8652 * to the directive element and autometically tears it down 8653 * during unbind. 8654 * 8655 * @param {String} event 8656 * @param {Function} handler 8657 * @param {Boolean} [useCapture] 8658 */ 8659 8660Directive.prototype.on = function (event, handler, useCapture) { 8661 on(this.el, event, handler, useCapture);(this._listeners || (this._listeners = [])).push([event, handler]); 8662}; 8663 8664/** 8665 * Teardown the watcher and call unbind. 8666 */ 8667 8668Directive.prototype._teardown = function () { 8669 if (this._bound) { 8670 this._bound = false; 8671 if (this.unbind) { 8672 this.unbind(); 8673 } 8674 if (this._watcher) { 8675 this._watcher.teardown(); 8676 } 8677 var listeners = this._listeners; 8678 var i; 8679 if (listeners) { 8680 i = listeners.length; 8681 while (i--) { 8682 off(this.el, listeners[i][0], listeners[i][1]); 8683 } 8684 } 8685 var unwatchFns = this._paramUnwatchFns; 8686 if (unwatchFns) { 8687 i = unwatchFns.length; 8688 while (i--) { 8689 unwatchFns[i](); 8690 } 8691 } 8692 if ('development' !== 'production' && this.el) { 8693 this.el._vue_directives.$remove(this); 8694 } 8695 this.vm = this.el = this._watcher = this._listeners = null; 8696 } 8697}; 8698 8699function lifecycleMixin (Vue) { 8700 /** 8701 * Update v-ref for component. 8702 * 8703 * @param {Boolean} remove 8704 */ 8705 8706 Vue.prototype._updateRef = function (remove) { 8707 var ref = this.$options._ref; 8708 if (ref) { 8709 var refs = (this._scope || this._context).$refs; 8710 if (remove) { 8711 if (refs[ref] === this) { 8712 refs[ref] = null; 8713 } 8714 } else { 8715 refs[ref] = this; 8716 } 8717 } 8718 }; 8719 8720 /** 8721 * Transclude, compile and link element. 8722 * 8723 * If a pre-compiled linker is available, that means the 8724 * passed in element will be pre-transcluded and compiled 8725 * as well - all we need to do is to call the linker. 8726 * 8727 * Otherwise we need to call transclude/compile/link here. 8728 * 8729 * @param {Element} el 8730 */ 8731 8732 Vue.prototype._compile = function (el) { 8733 var options = this.$options; 8734 8735 // transclude and init element 8736 // transclude can potentially replace original 8737 // so we need to keep reference; this step also injects 8738 // the template and caches the original attributes 8739 // on the container node and replacer node. 8740 var original = el; 8741 el = transclude(el, options); 8742 this._initElement(el); 8743 8744 // handle v-pre on root node (#2026)
8745 if (el.nodeType === 1 && getAttr(el, 'v-pre') !== null) { 8746 return; 8747 } 8748 8749 // root is always compiled per-instance, because 8750 // container attrs and props can be different every time. 8751 var contextOptions = this._context && this._context.$options; 8752 var rootLinker = compileRoot(el, options, contextOptions); 8753 8754 // resolve slot distribution 8755 resolveSlots(this, options._content); 8756 8757 // compile and link the rest 8758 var contentLinkFn; 8759 var ctor = this.constructor; 8760 // component compilation can be cached 8761 // as long as it's not using inline-template 8762 if (options._linkerCachable) { 8763 contentLinkFn = ctor.linker; 8764 if (!contentLinkFn) { 8765 contentLinkFn = ctor.linker = compile(el, options); 8766 } 8767 } 8768 8769 // link phase 8770 // make sure to link root with prop scope! 8771 var rootUnlinkFn = rootLinker(this, el, this._scope); 8772 var contentUnlinkFn = contentLinkFn ? contentLinkFn(this, el) : compile(el, options)(this, el); 8773 8774 // register composite unlink function 8775 // to be called during instance destruction 8776 this._unlinkFn = function () { 8777 rootUnlinkFn(); 8778 // passing destroying: true to avoid searching and 8779 // splicing the directives 8780 contentUnlinkFn(true); 8781 }; 8782 8783 // finally replace original 8784 if (options.replace) { 8785 replace(original, el); 8786 } 8787 8788 this._isCompiled = true; 8789 this._callHook('compiled'); 8790 }; 8791 8792 /** 8793 * Initialize instance element. Called in the public 8794 * $mount() method. 8795 * 8796 * @param {Element} el 8797 */ 8798 8799 Vue.prototype._initElement = function (el) { 8800 if (isFragment(el)) { 8801 this._isFragment = true; 8802 this.$el = this._fragmentStart = el.firstChild; 8803 this._fragmentEnd = el.lastChild; 8804 // set persisted text anchors to empty 8805 if (this._fragmentStart.nodeType === 3) { 8806 this._fragmentStart.data = this._fragmentEnd.data = ''; 8807 } 8808 this._fragment = el; 8809 } else { 8810 this.$el = el; 8811 } 8812 this.$el.__vue__ = this; 8813 this._callHook('beforeCompile'); 8814 }; 8815 8816 /** 8817 * Create and bind a directive to an element. 8818 * 8819 * @param {Object} descriptor - parsed directive descriptor 8820 * @param {Node} node - target node 8821 * @param {Vue} [host] - transclusion host component 8822 * @param {Object} [scope] - v-for scope 8823 * @param {Fragment} [frag] - owner fragment 8824 */ 8825 8826 Vue.prototype._bindDir = function (descriptor, node, host, scope, frag) { 8827 this._directives.push(new Directive(descriptor, this, node, host, scope, frag)); 8828 }; 8829 8830 /** 8831 * Teardown an instance, unobserves the data, unbind all the 8832 * directives, turn off all the event listeners, etc. 8833 * 8834 * @param {Boolean} remove - whether to remove the DOM node. 8835 * @param {Boolean} deferCleanup - if true, defer cleanup to 8836 * be called later 8837 */ 8838 8839 Vue.prototype._destroy = function (remove, deferCleanup) { 8840 if (this._isBeingDestroyed) { 8841 if (!deferCleanup) { 8842 this._cleanup(); 8843 } 8844 return; 8845 } 8846 8847 var destroyReady; 8848 var pendingRemoval; 8849 8850 var self = this; 8851 // Cleanup should be called either synchronously or asynchronoysly as 8852 // callback of this.$remove(), or if remove and deferCleanup are false. 8853 // In any case it should be called after all other removing, unbinding and 8854 // turning of is done 8855 var cleanupIfPossible = function cleanupIfPossible() { 8856 if (destroyReady && !pendingRemoval && !deferCleanup) { 8857 self._cleanup(); 8858 } 8859 }; 8860 8861 // remove DOM element 8862 if (remove && this.$el) { 8863 pendingRemoval = true; 8864 this.$remove(function () { 8865 pendingRemoval = false; 8866 cleanupIfPossible(); 8867 }); 8868 } 8869 8870 this._callHook('beforeDestroy'); 8871 this._isBeingDestroyed = true; 8872 var i; 8873 // remove self from parent. only necessary 8874 // if parent is not being destroyed as well. 8875 var parent = this.$parent; 8876 if (parent && !parent._isBeingDestroyed) { 8877 parent.$children.$remove(this); 8878 // unregister ref (remove: true) 8879 this._updateRef(true); 8880 } 8881 // destroy all children. 8882 i = this.$children.length; 8883 while (i--) { 8884 this.$children[i].$destroy(); 8885 } 8886 // teardown props 8887 if (this._propsUnlinkFn) { 8888 this._propsUnlinkFn(); 8889 } 8890 // teardown all directives. this also tearsdown all 8891 // directive-owned watchers. 8892 if (this._unlinkFn) { 8893 this._unlinkFn(); 8894 } 8895 i = this._watchers.length; 8896 while (i--) { 8897 this._watchers[i].teardown(); 8898 } 8899 // remove reference to self on $el 8900 if (this.$el) { 8901 this.$el.__vue__ = null; 8902 } 8903 8904 destroyReady = true; 8905 cleanupIfPossible(); 8906 }; 8907 8908 /** 8909 * Clean up to ensure garbage collection. 8910 * This is called after the leave transition if there 8911 * is any. 8912 */ 8913 8914 Vue.prototype._cleanup = function () { 8915 if (this._isDestroyed) { 8916 return; 8917 } 8918 // remove self from owner fragment 8919 // do it in cleanup so that we can call $destroy with 8920 // defer right when a fragment is about to be removed. 8921 if (this._frag) { 8922 this._frag.children.$remove(this); 8923 } 8924 // remove reference from data ob 8925 // frozen object may not have observer. 8926 if (this._data && this._data.__ob__) { 8927 this._data.__ob__.removeVm(this); 8928 } 8929 // Clean up references to private properties and other 8930 // instances. preserve reference to _data so that proxy 8931 // accessors still work. The only potential side effect
8932 // here is that mutating the instance after it's destroyed 8933 // may affect the state of other components that are still 8934 // observing the same object, but that seems to be a 8935 // reasonable responsibility for the user rather than 8936 // always throwing an error on them. 8937 this.$el = this.$parent = this.$root = this.$children = this._watchers = this._context = this._scope = this._directives = null; 8938 // call the last hook... 8939 this._isDestroyed = true; 8940 this._callHook('destroyed'); 8941 // turn off all instance listeners. 8942 this.$off(); 8943 }; 8944} 8945 8946function miscMixin (Vue) { 8947 /** 8948 * Apply a list of filter (descriptors) to a value. 8949 * Using plain for loops here because this will be called in 8950 * the getter of any watcher with filters so it is very 8951 * performance sensitive. 8952 * 8953 * @param {*} value 8954 * @param {*} [oldValue] 8955 * @param {Array} filters 8956 * @param {Boolean} write 8957 * @return {*} 8958 */ 8959 8960 Vue.prototype._applyFilters = function (value, oldValue, filters, write) { 8961 var filter, fn, args, arg, offset, i, l, j, k; 8962 for (i = 0, l = filters.length; i < l; i++) { 8963 filter = filters[write ? l - i - 1 : i]; 8964 fn = resolveAsset(this.$options, 'filters', filter.name, true); 8965 if (!fn) continue; 8966 fn = write ? fn.write : fn.read || fn; 8967 if (typeof fn !== 'function') continue; 8968 args = write ? [value, oldValue] : [value]; 8969 offset = write ? 2 : 1; 8970 if (filter.args) { 8971 for (j = 0, k = filter.args.length; j < k; j++) { 8972 arg = filter.args[j]; 8973 args[j + offset] = arg.dynamic ? this.$get(arg.value) : arg.value; 8974 } 8975 } 8976 value = fn.apply(this, args); 8977 } 8978 return value; 8979 }; 8980 8981 /** 8982 * Resolve a component, depending on whether the component 8983 * is defined normally or using an async factory function. 8984 * Resolves synchronously if already resolved, otherwise 8985 * resolves asynchronously and caches the resolved 8986 * constructor on the factory. 8987 * 8988 * @param {String|Function} value 8989 * @param {Function} cb 8990 */ 8991 8992 Vue.prototype._resolveComponent = function (value, cb) { 8993 var factory; 8994 if (typeof value === 'function') { 8995 factory = value; 8996 } else { 8997 factory = resolveAsset(this.$options, 'components', value, true); 8998 } 8999 /* istanbul ignore if */ 9000 if (!factory) { 9001 return; 9002 } 9003 // async component factory 9004 if (!factory.options) { 9005 if (factory.resolved) { 9006 // cached 9007 cb(factory.resolved); 9008 } else if (factory.requested) { 9009 // pool callbacks 9010 factory.pendingCallbacks.push(cb); 9011 } else { 9012 factory.requested = true; 9013 var cbs = factory.pendingCallbacks = [cb]; 9014 factory.call(this, function resolve(res) { 9015 if (isPlainObject(res)) { 9016 res = Vue.extend(res); 9017 } 9018 // cache resolved 9019 factory.resolved = res; 9020 // invoke callbacks 9021 for (var i = 0, l = cbs.length; i < l; i++) { 9022 cbs[i](res); 9023 } 9024 }, function reject(reason) { 9025 'development' !== 'production' && warn('Failed to resolve async component' + (typeof value === 'string' ? ': ' + value : '') + '. ' + (reason ? '\nReason: ' + reason : '')); 9026 }); 9027 } 9028 } else { 9029 // normal component 9030 cb(factory); 9031 } 9032 }; 9033} 9034 9035var filterRE$1 = /[^|]\|[^|]/; 9036 9037function dataAPI (Vue) { 9038 /** 9039 * Get the value from an expression on this vm. 9040 * 9041 * @param {String} exp 9042 * @param {Boolean} [asStatement] 9043 * @return {*} 9044 */ 9045 9046 Vue.prototype.$get = function (exp, asStatement) { 9047 var res = parseExpression$1(exp); 9048 if (res) { 9049 if (asStatement) { 9050 var self = this; 9051 return function statementHandler() { 9052 self.$arguments = toArray(arguments); 9053 var result = res.get.call(self, self); 9054 self.$arguments = null; 9055 return result; 9056 }; 9057 } else { 9058 try { 9059 return res.get.call(this, this); 9060 } catch (e) {} 9061 } 9062 } 9063 }; 9064 9065 /** 9066 * Set the value from an expression on this vm. 9067 * The expression must be a valid left-hand 9068 * expression in an assignment. 9069 * 9070 * @param {String} exp 9071 * @param {*} val 9072 */ 9073 9074 Vue.prototype.$set = function (exp, val) { 9075 var res = parseExpression$1(exp, true); 9076 if (res && res.set) { 9077 res.set.call(this, this, val); 9078 } 9079 }; 9080 9081 /** 9082 * Delete a property on the VM 9083 * 9084 * @param {String} key 9085 */ 9086 9087 Vue.prototype.$delete = function (key) { 9088 del(this._data, key); 9089 }; 9090 9091 /** 9092 * Watch an expression, trigger callback when its 9093 * value changes. 9094 * 9095 * @param {String|Function} expOrFn 9096 * @param {Function} cb 9097 * @param {Object} [options] 9098 * - {Boolean} deep 9099 * - {Boolean} immediate 9100 * @return {Function} - unwatchFn 9101 */ 9102 9103 Vue.prototype.$watch = function (expOrFn, cb, options) { 9104 var vm = this; 9105 var parsed; 9106 if (typeof expOrFn === 'string') { 9107 parsed = parseDirective(expOrFn); 9108 expOrFn = parsed.expression; 9109 } 9110 var watcher = new Watcher(vm, expOrFn, cb, { 9111 deep: options && options.deep, 9112 sync: options && options.sync, 9113 filters: parsed && parsed.filters, 9114 user: !options || options.user !== false 9115 }); 9116 if (options && options.immediate) { 9117 cb.call(vm, watcher.value); 9118 } 9119 return function unwatchFn() { 9120 watcher.teardown(); 9121 }; 9122 }; 9123 9124 /** 9125 * Evaluate a text directive, including filters. 9126 * 9127 * @param {String} text 9128 * @param {Boolean} [asStatement] 9129 * @return {String} 9130 */ 9131 9132 Vue.prototype.$eval = function (text, asStatement) { 9133 // check for filters. 9134 if (filterRE$1.test(text)) { 9135 var dir = parseDirective(text); 9136 // the filter regex check might give false positive 9137 // for pipes inside strings, so it's possible that 9138 // we don't get any filters here 9139 var val = this.$get(dir.expression, asStatement); 9140 return dir.filters ? this._applyFilters(val, null, dir.filters) : val; 9141 } else { 9142 // no filter 9143 return this.$get(text, asStatement); 9144 } 9145 }; 9146 9147 /** 9148 * Interpolate a piece of template text. 9149 * 9150 * @param {String} text 9151 * @return {String} 9152 */ 9153 9154 Vue.prototype.$interpolate = function (text) { 9155 var tokens = parseText(text); 9156 var vm = this; 9157 if (tokens) { 9158 if (tokens.length === 1) { 9159 return vm.$eval(tokens[0].value) + ''; 9160 } else { 9161 return tokens.map(function (token) { 9162 return token.tag ? vm.$eval(token.value) : token.value; 9163 }).join(''); 9164 } 9165 } else { 9166 return text; 9167 } 9168 }; 9169 9170 /** 9171 * Log instance data as a plain JS object 9172 * so that it is easier to inspect in console. 9173 * This method assumes console is available. 9174 * 9175 * @param {String} [path] 9176 */ 9177 9178 Vue.prototype.$log = function (path) { 9179 var data = path ? getPath(this._data, path) : this._data; 9180 if (data) { 9181 data = clean(data); 9182 } 9183 // include computed fields 9184 if (!path) { 9185 var key; 9186 for (key in this.$options.computed) { 9187 data[key] = clean(this[key]); 9188 } 9189 if (this._props) { 9190 for (key in this._props) { 9191 data[key] = clean(this[key]); 9192 } 9193 } 9194 } 9195 console.log(data); 9196 }; 9197 9198 /** 9199 * "clean" a getter/setter converted object into a plain
9200 * object copy. 9201 * 9202 * @param {Object} - obj 9203 * @return {Object} 9204 */ 9205 9206 function clean(obj) { 9207 return JSON.parse(JSON.stringify(obj)); 9208 } 9209} 9210 9211function domAPI (Vue) { 9212 /** 9213 * Convenience on-instance nextTick. The callback is 9214 * auto-bound to the instance, and this avoids component 9215 * modules having to rely on the global Vue. 9216 * 9217 * @param {Function} fn 9218 */ 9219 9220 Vue.prototype.$nextTick = function (fn) { 9221 nextTick(fn, this); 9222 }; 9223 9224 /** 9225 * Append instance to target 9226 * 9227 * @param {Node} target 9228 * @param {Function} [cb] 9229 * @param {Boolean} [withTransition] - defaults to true 9230 */ 9231 9232 Vue.prototype.$appendTo = function (target, cb, withTransition) { 9233 return insert(this, target, cb, withTransition, append, appendWithTransition); 9234 }; 9235 9236 /** 9237 * Prepend instance to target 9238 * 9239 * @param {Node} target 9240 * @param {Function} [cb] 9241 * @param {Boolean} [withTransition] - defaults to true 9242 */ 9243 9244 Vue.prototype.$prependTo = function (target, cb, withTransition) { 9245 target = query(target); 9246 if (target.hasChildNodes()) { 9247 this.$before(target.firstChild, cb, withTransition); 9248 } else { 9249 this.$appendTo(target, cb, withTransition); 9250 } 9251 return this; 9252 }; 9253 9254 /** 9255 * Insert instance before target 9256 * 9257 * @param {Node} target 9258 * @param {Function} [cb] 9259 * @param {Boolean} [withTransition] - defaults to true 9260 */ 9261 9262 Vue.prototype.$before = function (target, cb, withTransition) { 9263 return insert(this, target, cb, withTransition, beforeWithCb, beforeWithTransition); 9264 }; 9265 9266 /** 9267 * Insert instance after target 9268 * 9269 * @param {Node} target 9270 * @param {Function} [cb] 9271 * @param {Boolean} [withTransition] - defaults to true 9272 */ 9273 9274 Vue.prototype.$after = function (target, cb, withTransition) { 9275 target = query(target); 9276 if (target.nextSibling) { 9277 this.$before(target.nextSibling, cb, withTransition); 9278 } else { 9279 this.$appendTo(target.parentNode, cb, withTransition); 9280 } 9281 return this; 9282 }; 9283 9284 /** 9285 * Remove instance from DOM 9286 * 9287 * @param {Function} [cb] 9288 * @param {Boolean} [withTransition] - defaults to true 9289 */ 9290 9291 Vue.prototype.$remove = function (cb, withTransition) { 9292 if (!this.$el.parentNode) { 9293 return cb && cb(); 9294 } 9295 var inDocument = this._isAttached && inDoc(this.$el); 9296 // if we are not in document, no need to check 9297 // for transitions 9298 if (!inDocument) withTransition = false; 9299 var self = this; 9300 var realCb = function realCb() { 9301 if (inDocument) self._callHook('detached'); 9302 if (cb) cb(); 9303 }; 9304 if (this._isFragment) { 9305 removeNodeRange(this._fragmentStart, this._fragmentEnd, this, this._fragment, realCb); 9306 } else { 9307 var op = withTransition === false ? removeWithCb : removeWithTransition; 9308 op(this.$el, this, realCb); 9309 } 9310 return this; 9311 }; 9312 9313 /** 9314 * Shared DOM insertion function. 9315 * 9316 * @param {Vue} vm 9317 * @param {Element} target 9318 * @param {Function} [cb] 9319 * @param {Boolean} [withTransition] 9320 * @param {Function} op1 - op for non-transition insert 9321 * @param {Function} op2 - op for transition insert 9322 * @return vm 9323 */ 9324 9325 function insert(vm, target, cb, withTransition, op1, op2) { 9326 target = query(target); 9327 var targetIsDetached = !inDoc(target); 9328 var op = withTransition === false || targetIsDetached ? op1 : op2; 9329 var shouldCallHook = !targetIsDetached && !vm._isAttached && !inDoc(vm.$el); 9330 if (vm._isFragment) { 9331 mapNodeRange(vm._fragmentStart, vm._fragmentEnd, function (node) { 9332 op(node, target, vm); 9333 }); 9334 cb && cb(); 9335 } else { 9336 op(vm.$el, target, vm, cb); 9337 } 9338 if (shouldCallHook) { 9339 vm._callHook('attached'); 9340 } 9341 return vm; 9342 } 9343 9344 /** 9345 * Check for selectors 9346 * 9347 * @param {String|Element} el 9348 */ 9349 9350 function query(el) { 9351 return typeof el === 'string' ? document.querySelector(el) : el; 9352 } 9353 9354 /** 9355 * Append operation that takes a callback. 9356 * 9357 * @param {Node} el 9358 * @param {Node} target 9359 * @param {Vue} vm - unused 9360 * @param {Function} [cb] 9361 */ 9362 9363 function append(el, target, vm, cb) { 9364 target.appendChild(el); 9365 if (cb) cb(); 9366 } 9367 9368 /** 9369 * InsertBefore operation that takes a callback. 9370 * 9371 * @param {Node} el 9372 * @param {Node} target 9373 * @param {Vue} vm - unused 9374 * @param {Function} [cb] 9375 */ 9376 9377 function beforeWithCb(el, target, vm, cb) { 9378 before(el, target); 9379 if (cb) cb(); 9380 } 9381 9382 /** 9383 * Remove operation that takes a callback. 9384 * 9385 * @param {Node} el 9386 * @param {Vue} vm - unused 9387 * @param {Function} [cb] 9388 */ 9389 9390 function removeWithCb(el, vm, cb) { 9391 remove(el); 9392 if (cb) cb(); 9393 } 9394} 9395 9396function eventsAPI (Vue) { 9397 /** 9398 * Listen on the given `event` with `fn`. 9399 * 9400 * @param {String} event 9401 * @param {Function} fn 9402 */ 9403 9404 Vue.prototype.$on = function (event, fn) { 9405 (this._events[event] || (this._events[event] = [])).push(fn); 9406 modifyListenerCount(this, event, 1); 9407 return this; 9408 }; 9409 9410 /**
9411 * Adds an `event` listener that will be invoked a single 9412 * time then automatically removed. 9413 * 9414 * @param {String} event 9415 * @param {Function} fn 9416 */ 9417 9418 Vue.prototype.$once = function (event, fn) { 9419 var self = this; 9420 function on() { 9421 self.$off(event, on); 9422 fn.apply(this, arguments); 9423 } 9424 on.fn = fn; 9425 this.$on(event, on); 9426 return this; 9427 }; 9428 9429 /** 9430 * Remove the given callback for `event` or all 9431 * registered callbacks. 9432 * 9433 * @param {String} event 9434 * @param {Function} fn 9435 */ 9436 9437 Vue.prototype.$off = function (event, fn) { 9438 var cbs; 9439 // all 9440 if (!arguments.length) { 9441 if (this.$parent) { 9442 for (event in this._events) { 9443 cbs = this._events[event]; 9444 if (cbs) { 9445 modifyListenerCount(this, event, -cbs.length); 9446 } 9447 } 9448 } 9449 this._events = {}; 9450 return this; 9451 } 9452 // specific event 9453 cbs = this._events[event]; 9454 if (!cbs) { 9455 return this; 9456 } 9457 if (arguments.length === 1) { 9458 modifyListenerCount(this, event, -cbs.length); 9459 this._events[event] = null; 9460 return this; 9461 } 9462 // specific handler 9463 var cb; 9464 var i = cbs.length; 9465 while (i--) { 9466 cb = cbs[i]; 9467 if (cb === fn || cb.fn === fn) { 9468 modifyListenerCount(this, event, -1); 9469 cbs.splice(i, 1); 9470 break; 9471 } 9472 } 9473 return this; 9474 }; 9475 9476 /** 9477 * Trigger an event on self. 9478 * 9479 * @param {String|Object} event 9480 * @return {Boolean} shouldPropagate 9481 */ 9482 9483 Vue.prototype.$emit = function (event) { 9484 var isSource = typeof event === 'string'; 9485 event = isSource ? event : event.name; 9486 var cbs = this._events[event]; 9487 var shouldPropagate = isSource || !cbs; 9488 if (cbs) { 9489 cbs = cbs.length > 1 ? toArray(cbs) : cbs; 9490 // this is a somewhat hacky solution to the question raised 9491 // in #2102: for an inline component listener like <comp @test="doThis">, 9492 // the propagation handling is somewhat broken. Therefore we 9493 // need to treat these inline callbacks differently. 9494 var hasParentCbs = isSource && cbs.some(function (cb) { 9495 return cb._fromParent; 9496 }); 9497 if (hasParentCbs) { 9498 shouldPropagate = false; 9499 } 9500 var args = toArray(arguments, 1); 9501 for (var i = 0, l = cbs.length; i < l; i++) { 9502 var cb = cbs[i]; 9503 var res = cb.apply(this, args); 9504 if (res === true && (!hasParentCbs || cb._fromParent)) { 9505 shouldPropagate = true; 9506 } 9507 } 9508 } 9509 return shouldPropagate; 9510 }; 9511 9512 /** 9513 * Recursively broadcast an event to all children instances. 9514 * 9515 * @param {String|Object} event 9516 * @param {...*} additional arguments 9517 */ 9518 9519 Vue.prototype.$broadcast = function (event) { 9520 var isSource = typeof event === 'string'; 9521 event = isSource ? event : event.name; 9522 // if no child has registered for this event, 9523 // then there's no need to broadcast. 9524 if (!this._eventsCount[event]) return; 9525 var children = this.$children; 9526 var args = toArray(arguments); 9527 if (isSource) { 9528 // use object event to indicate non-source emit 9529 // on children 9530 args[0] = { name: event, source: this }; 9531 } 9532 for (var i = 0, l = children.length; i < l; i++) { 9533 var child = children[i]; 9534 var shouldPropagate = child.$emit.apply(child, args); 9535 if (shouldPropagate) { 9536 child.$broadcast.apply(child, args); 9537 } 9538 } 9539 return this; 9540 }; 9541 9542 /** 9543 * Recursively propagate an event up the parent chain. 9544 * 9545 * @param {String} event 9546 * @param {...*} additional arguments 9547 */ 9548 9549 Vue.prototype.$dispatch = function (event) { 9550 var shouldPropagate = this.$emit.apply(this, arguments); 9551 if (!shouldPropagate) return; 9552 var parent = this.$parent; 9553 var args = toArray(arguments); 9554 // use object event to indicate non-source emit 9555 // on parents 9556 args[0] = { name: event, source: this }; 9557 while (parent) { 9558 shouldPropagate = parent.$emit.apply(parent, args); 9559 parent = shouldPropagate ? parent.$parent : null; 9560 } 9561 return this; 9562 }; 9563 9564 /** 9565 * Modify the listener counts on all parents. 9566 * This bookkeeping allows $broadcast to return early when 9567 * no child has listened to a certain event. 9568 * 9569 * @param {Vue} vm 9570 * @param {String} event 9571 * @param {Number} count 9572 */ 9573 9574 var hookRE = /^hook:/; 9575 function modifyListenerCount(vm, event, count) { 9576 var parent = vm.$parent; 9577 // hooks do not get broadcasted so no need 9578 // to do bookkeeping for them 9579 if (!parent || !count || hookRE.test(event)) return; 9580 while (parent) { 9581 parent._eventsCount[event] = (parent._eventsCount[event] || 0) + count; 9582 parent = parent.$parent; 9583 } 9584 } 9585} 9586 9587function lifecycleAPI (Vue) { 9588 /** 9589 * Set instance target element and kick off the compilation 9590 * process. The passed in `el` can be a selector string, an 9591 * existing Element, or a DocumentFragment (for block 9592 * instances). 9593 * 9594 * @param {Element|DocumentFragment|string} el 9595 * @public 9596 */ 9597 9598 Vue.prototype.$mount = function (el) { 9599 if (this._isCompiled) { 9600 'development' !== 'production' && warn('$mount() should be called only once.', this); 9601 return; 9602 } 9603 el = query(el); 9604 if (!el) { 9605 el = document.createElement('div'); 9606 } 9607 this._compile(el); 9608 this._initDOMHooks(); 9609 if (inDoc(this.$el)) { 9610 this._callHook('attached'); 9611 ready.call(this); 9612 } else { 9613 this.$once('hook:attached', ready); 9614 } 9615 return this; 9616 }; 9617 9618 /** 9619 * Mark an instance as ready. 9620 */ 9621 9622 function ready() { 9623 this._isAttached = true; 9624 this._isReady = true; 9625 this._callHook('ready'); 9626 } 9627 9628 /** 9629 * Teardown the instance, simply delegate to the internal 9630 * _destroy. 9631 * 9632 * @param {Boolean} remove 9633 * @param {Boolean} deferCleanup 9634 */ 9635 9636 Vue.prototype.$destroy = function (remove, deferCleanup) { 9637 this._destroy(remove, deferCleanup); 9638 }; 9639 9640 /** 9641 * Partially compile a piece of DOM and return a 9642 * decompile function. 9643 * 9644 * @param {Element|DocumentFragment} el 9645 * @param {Vue} [host] 9646 * @param {Object} [scope] 9647 * @param {Fragment} [frag] 9648 * @return {Function} 9649 */ 9650 9651 Vue.prototype.$compile = function (el, host, scope, frag) { 9652 return compile(el, this.$options, true)(this, el, host, scope, frag); 9653 }; 9654} 9655 9656/** 9657 * The exposed Vue constructor. 9658 * 9659 * API conventions: 9660 * - public API methods/properties are prefixed with `$` 9661 * - internal methods/properties are prefixed with `_` 9662 * - non-prefixed properties are assumed to be proxied user 9663 * data. 9664 * 9665 * @constructor 9666 * @param {Object} [options] 9667 * @public 9668 */ 9669 9670function Vue(options) { 9671 this._init(options); 9672} 9673 9674// install internals 9675initMixin(Vue); 9676stateMixin(Vue); 9677eventsMixin(Vue); 9678lifecycleMixin(Vue); 9679miscMixin(Vue); 9680 9681// install instance APIs 9682dataAPI(Vue); 9683domAPI(Vue); 9684eventsAPI(Vue); 9685lifecycleAPI(Vue); 9686 9687var slot = { 9688 9689 priority: SLOT, 9690 params: ['name'], 9691 9692 bind: function bind() { 9693 // this was resolved during component transclusion 9694 var name = this.params.name || 'default'; 9695 var content = this.vm._slotContents && this.vm._slotContents[name]; 9696 if (!content || !content.hasChildNodes()) { 9697 this.fallback(); 9698 } else { 9699 this.compile(content.cloneNode(true), this.vm._context, this.vm); 9700 } 9701 }, 9702 9703 compile: function compile(content, context, host) { 9704 if (content && context) { 9705 if (this.el.hasChildNodes() && content.childNodes.length === 1 && content.childNodes[0].nodeType === 1 && content.childNodes[0].hasAttribute('v-if')) { 9706 // if the inserted slot has v-if 9707 // inject fallback content as the v-else 9708 var elseBlock = document.createElement('template'); 9709 elseBlock.setAttribute('v-else', ''); 9710 elseBlock.innerHTML = this.el.innerHTML; 9711 // the else block should be compiled in child scope 9712 elseBlock._context = this.vm; 9713 content.appendChild(elseBlock); 9714 } 9715 var scope = host ? host._scope : this._scope; 9716 this.unlink = context.$compile(content, host, scope, this._frag); 9717 } 9718 if (content) { 9719 replace(this.el, content); 9720 } else { 9721 remove(this.el); 9722 } 9723 }, 9724 9725 fallback: function fallback() { 9726 this.compile(extractContent(this.el, true), this.vm); 9727 }, 9728 9729 unbind: function unbind() { 9730 if (this.unlink) { 9731 this.unlink(); 9732 } 9733 } 9734}; 9735 9736var partial = { 9737 9738 priority: PARTIAL, 9739 9740 params: ['name'], 9741 9742 // watch changes to name for dynamic partials 9743 paramWatchers: { 9744 name: function name(value) { 9745 vIf.remove.call(this); 9746 if (value) { 9747 this.insert(value); 9748 } 9749 } 9750 }, 9751 9752 bind: function bind() { 9753 this.anchor = createAnchor('v-partial'); 9754 replace(this.el, this.anchor); 9755 this.insert(this.params.name); 9756 }, 9757 9758 insert: function insert(id) { 9759 var partial = resolveAsset(this.vm.$options, 'partials', id, true); 9760 if (partial) { 9761 this.factory = new FragmentFactory(this.vm, partial); 9762 vIf.insert.call(this); 9763 } 9764 }, 9765 9766 unbind: function unbind() { 9767 if (this.frag) { 9768 this.frag.destroy(); 9769 } 9770 } 9771}; 9772 9773var elementDirectives = { 9774 slot: slot, 9775 partial: partial 9776}; 9777
9778var convertArray = vFor._postProcess; 9779 9780/** 9781 * Limit filter for arrays 9782 * 9783 * @param {Number} n 9784 * @param {Number} offset (Decimal expected) 9785 */ 9786 9787function limitBy(arr, n, offset) { 9788 offset = offset ? parseInt(offset, 10) : 0; 9789 n = toNumber(n); 9790 return typeof n === 'number' ? arr.slice(offset, offset + n) : arr; 9791} 9792 9793/** 9794 * Filter filter for arrays 9795 * 9796 * @param {String} search 9797 * @param {String} [delimiter] 9798 * @param {String} ...dataKeys 9799 */ 9800 9801function filterBy(arr, search, delimiter) { 9802 arr = convertArray(arr); 9803 if (search == null) { 9804 return arr; 9805 } 9806 if (typeof search === 'function') { 9807 return arr.filter(search); 9808 } 9809 // cast to lowercase string 9810 search = ('' + search).toLowerCase(); 9811 // allow optional `in` delimiter 9812 // because why not 9813 var n = delimiter === 'in' ? 3 : 2; 9814 // extract and flatten keys 9815 var keys = Array.prototype.concat.apply([], toArray(arguments, n)); 9816 var res = []; 9817 var item, key, val, j; 9818 for (var i = 0, l = arr.length; i < l; i++) { 9819 item = arr[i]; 9820 val = item && item.$value || item; 9821 j = keys.length; 9822 if (j) { 9823 while (j--) { 9824 key = keys[j]; 9825 if (key === '$key' && contains(item.$key, search) || contains(getPath(val, key), search)) { 9826 res.push(item); 9827 break; 9828 } 9829 } 9830 } else if (contains(item, search)) { 9831 res.push(item); 9832 } 9833 } 9834 return res; 9835} 9836 9837/** 9838 * Order filter for arrays 9839 * 9840 * @param {String|Array<String>|Function} ...sortKeys 9841 * @param {Number} [order] 9842 */ 9843 9844function orderBy(arr) { 9845 var comparator = null; 9846 var sortKeys = undefined; 9847 arr = convertArray(arr); 9848 9849 // determine order (last argument) 9850 var args = toArray(arguments, 1); 9851 var order = args[args.length - 1]; 9852 if (typeof order === 'number') { 9853 order = order < 0 ? -1 : 1; 9854 args = args.length > 1 ? args.slice(0, -1) : args; 9855 } else { 9856 order = 1; 9857 } 9858 9859 // determine sortKeys & comparator 9860 var firstArg = args[0]; 9861 if (!firstArg) { 9862 return arr; 9863 } else if (typeof firstArg === 'function') { 9864 // custom comparator 9865 comparator = function (a, b) { 9866 return firstArg(a, b) * order; 9867 }; 9868 } else { 9869 // string keys. flatten first 9870 sortKeys = Array.prototype.concat.apply([], args); 9871 comparator = function (a, b, i) { 9872 i = i || 0; 9873 return i >= sortKeys.length - 1 ? baseCompare(a, b, i) : baseCompare(a, b, i) || comparator(a, b, i + 1); 9874 }; 9875 } 9876 9877 function baseCompare(a, b, sortKeyIndex) { 9878 var sortKey = sortKeys[sortKeyIndex]; 9879 if (sortKey) { 9880 if (sortKey !== '$key') { 9881 if (isObject(a) && '$value' in a) a = a.$value; 9882 if (isObject(b) && '$value' in b) b = b.$value; 9883 } 9884 a = isObject(a) ? getPath(a, sortKey) : a; 9885 b = isObject(b) ? getPath(b, sortKey) : b; 9886 } 9887 return a === b ? 0 : a > b ? order : -order; 9888 } 9889 9890 // sort on a copy to avoid mutating original array 9891 return arr.slice().sort(comparator); 9892} 9893 9894/** 9895 * String contain helper 9896 * 9897 * @param {*} val 9898 * @param {String} search 9899 */ 9900 9901function contains(val, search) { 9902 var i; 9903 if (isPlainObject(val)) { 9904 var keys = Object.keys(val); 9905 i = keys.length; 9906 while (i--) { 9907 if (contains(val[keys[i]], search)) { 9908 return true; 9909 } 9910 } 9911 } else if (isArray(val)) { 9912 i = val.length; 9913 while (i--) { 9914 if (contains(val[i], search)) { 9915 return true; 9916 } 9917 } 9918 } else if (val != null) { 9919 return val.toString().toLowerCase().indexOf(search) > -1; 9920 } 9921} 9922 9923var digitsRE = /(\d{3})(?=\d)/g; 9924 9925// asset collections must be a plain object. 9926var filters = { 9927 9928 orderBy: orderBy, 9929 filterBy: filterBy, 9930 limitBy: limitBy, 9931 9932 /** 9933 * Stringify value. 9934 * 9935 * @param {Number} indent 9936 */ 9937 9938 json: { 9939 read: function read(value, indent) { 9940 return typeof value === 'string' ? value : JSON.stringify(value, null, arguments.length > 1 ? indent : 2); 9941 }, 9942 write: function write(value) { 9943 try { 9944 return JSON.parse(value); 9945 } catch (e) { 9946 return value; 9947 } 9948 } 9949 }, 9950 9951 /** 9952 * 'abc' => 'Abc' 9953 */ 9954 9955 capitalize: function capitalize(value) { 9956 if (!value && value !== 0) return ''; 9957 value = value.toString(); 9958 return value.charAt(0).toUpperCase() + value.slice(1); 9959 }, 9960 9961 /** 9962 * 'abc' => 'ABC' 9963 */ 9964 9965 uppercase: function uppercase(value) { 9966 return value || value === 0 ? value.toString().toUpperCase() : ''; 9967 }, 9968 9969 /** 9970 * 'AbC' => 'abc' 9971 */ 9972 9973 lowercase: function lowercase(value) { 9974 return value || value === 0 ? value.toString().toLowerCase() : ''; 9975 }, 9976 9977 /** 9978 * 12345 => $12,345.00 9979 * 9980 * @param {String} sign 9981 * @param {Number} decimals Decimal places 9982 */ 9983 9984 currency: function currency(value, _currency, decimals) { 9985 value = parseFloat(value); 9986 if (!isFinite(value) || !value && value !== 0) return ''; 9987 _currency = _currency != null ? _currency : '$'; 9988 decimals = decimals != null ? decimals : 2; 9989 var stringified = Math.abs(value).toFixed(decimals); 9990 var _int = decimals ? stringified.slice(0, -1 - decimals) : stringified; 9991 var i = _int.length % 3; 9992 var head = i > 0 ? _int.slice(0, i) + (_int.length > 3 ? ',' : '') : ''; 9993 var _float = decimals ? stringified.slice(-1 - decimals) : ''; 9994 var sign = value < 0 ? '-' : ''; 9995 return sign + _currency + head + _int.slice(i).replace(digitsRE, '$1,') + _float; 9996 }, 9997 9998 /** 9999 * 'item' => 'items' 10000 * 10001 * @params
10002 * an array of strings corresponding to 10003 * the single, double, triple ... forms of the word to 10004 * be pluralized. When the number to be pluralized 10005 * exceeds the length of the args, it will use the last 10006 * entry in the array. 10007 * 10008 * e.g. ['single', 'double', 'triple', 'multiple'] 10009 */ 10010 10011 pluralize: function pluralize(value) { 10012 var args = toArray(arguments, 1); 10013 var length = args.length; 10014 if (length > 1) { 10015 var index = value % 10 - 1; 10016 return index in args ? args[index] : args[length - 1]; 10017 } else { 10018 return args[0] + (value === 1 ? '' : 's'); 10019 } 10020 }, 10021 10022 /** 10023 * Debounce a handler function. 10024 * 10025 * @param {Function} handler 10026 * @param {Number} delay = 300 10027 * @return {Function} 10028 */ 10029 10030 debounce: function debounce(handler, delay) { 10031 if (!handler) return; 10032 if (!delay) { 10033 delay = 300; 10034 } 10035 return _debounce(handler, delay); 10036 } 10037}; 10038 10039function installGlobalAPI (Vue) { 10040 /** 10041 * Vue and every constructor that extends Vue has an 10042 * associated options object, which can be accessed during 10043 * compilation steps as `this.constructor.options`. 10044 * 10045 * These can be seen as the default options of every 10046 * Vue instance. 10047 */ 10048 10049 Vue.options = { 10050 directives: directives, 10051 elementDirectives: elementDirectives, 10052 filters: filters, 10053 transitions: {}, 10054 components: {}, 10055 partials: {}, 10056 replace: true 10057 }; 10058 10059 /** 10060 * Expose useful internals 10061 */ 10062 10063 Vue.util = util; 10064 Vue.config = config; 10065 Vue.set = set; 10066 Vue['delete'] = del; 10067 Vue.nextTick = nextTick; 10068 10069 /** 10070 * The following are exposed for advanced usage / plugins 10071 */ 10072 10073 Vue.compiler = compiler; 10074 Vue.FragmentFactory = FragmentFactory; 10075 Vue.internalDirectives = internalDirectives; 10076 Vue.parsers = { 10077 path: path, 10078 text: text, 10079 template: template, 10080 directive: directive, 10081 expression: expression 10082 }; 10083 10084 /** 10085 * Each instance constructor, including Vue, has a unique 10086 * cid. This enables us to create wrapped "child 10087 * constructors" for prototypal inheritance and cache them. 10088 */ 10089 10090 Vue.cid = 0; 10091 var cid = 1; 10092 10093 /** 10094 * Class inheritance 10095 * 10096 * @param {Object} extendOptions 10097 */ 10098 10099 Vue.extend = function (extendOptions) { 10100 extendOptions = extendOptions || {}; 10101 var Super = this; 10102 var isFirstExtend = Super.cid === 0; 10103 if (isFirstExtend && extendOptions._Ctor) { 10104 return extendOptions._Ctor; 10105 } 10106 var name = extendOptions.name || Super.options.name; 10107 if ('development' !== 'production') {
10108 if (!/^[a-zA-Z][\w-]*$/.test(name)) { 10109 warn('Invalid component name: "' + name + '". Component names ' + 'can only contain alphanumeric characaters and the hyphen.'); 10110 name = null; 10111 } 10112 } 10113 var Sub = createClass(name || 'VueComponent'); 10114 Sub.prototype = Object.create(Super.prototype); 10115 Sub.prototype.constructor = Sub; 10116 Sub.cid = cid++; 10117 Sub.options = mergeOptions(Super.options, extendOptions); 10118 Sub['super'] = Super; 10119 // allow further extension 10120 Sub.extend = Super.extend; 10121 // create asset registers, so extended classes 10122 // can have their private assets too. 10123 config._assetTypes.forEach(function (type) { 10124 Sub[type] = Super[type]; 10125 }); 10126 // enable recursive self-lookup 10127 if (name) { 10128 Sub.options.components[name] = Sub; 10129 } 10130 // cache constructor 10131 if (isFirstExtend) { 10132 extendOptions._Ctor = Sub; 10133 } 10134 return Sub; 10135 }; 10136 10137 /** 10138 * A function that returns a sub-class constructor with the 10139 * given name. This gives us much nicer output when 10140 * logging instances in the console. 10141 * 10142 * @param {String} name 10143 * @return {Function} 10144 */ 10145 10146 function createClass(name) { 10147 /* eslint-disable no-new-func */ 10148 return new Function('return function ' + classify(name) + ' (options) { this._init(options) }')(); 10149 /* eslint-enable no-new-func */ 10150 } 10151 10152 /** 10153 * Plugin system 10154 * 10155 * @param {Object} plugin 10156 */ 10157 10158 Vue.use = function (plugin) { 10159 /* istanbul ignore if */ 10160 if (plugin.installed) { 10161 return; 10162 } 10163 // additional parameters 10164 var args = toArray(arguments, 1); 10165 args.unshift(this); 10166 if (typeof plugin.install === 'function') { 10167 plugin.install.apply(plugin, args); 10168 } else { 10169 plugin.apply(null, args); 10170 } 10171 plugin.installed = true; 10172 return this; 10173 }; 10174 10175 /** 10176 * Apply a global mixin by merging it into the default 10177 * options. 10178 */ 10179 10180 Vue.mixin = function (mixin) { 10181 Vue.options = mergeOptions(Vue.options, mixin); 10182 }; 10183 10184 /** 10185 * Create asset registration methods with the following 10186 * signature: 10187 * 10188 * @param {String} id 10189 * @param {*} definition 10190 */ 10191 10192 config._assetTypes.forEach(function (type) { 10193 Vue[type] = function (id, definition) { 10194 if (!definition) { 10195 return this.options[type + 's'][id]; 10196 } else { 10197 /* istanbul ignore if */ 10198 if ('development' !== 'production') { 10199 if (type === 'component' && (commonTagRE.test(id) || reservedTagRE.test(id))) { 10200 warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + id); 10201 } 10202 } 10203 if (type === 'component' && isPlainObject(definition)) { 10204 if (!definition.name) { 10205 definition.name = id; 10206 } 10207 definition = Vue.extend(definition); 10208 } 10209 this.options[type + 's'][id] = definition; 10210 return definition; 10211 } 10212 }; 10213 }); 10214 10215 // expose internal transition API 10216 extend(Vue.transition, transition); 10217} 10218 10219installGlobalAPI(Vue); 10220 10221Vue.version = '1.0.28'; 10222 10223// devtools global hook 10224/* istanbul ignore next */ 10225setTimeout(function () { 10226 if (config.devtools) { 10227 if (devtools) { 10228 devtools.emit('init', Vue); 10229 } else if ('development' !== 'production' && inBrowser && /Chrome\/\d+/.test(window.navigator.userAgent)) { 10230 console.log('Download the Vue Devtools for a better development experience:\n' + 'https://github.com/vuejs/vue-devtools'); 10231 } 10232 } 10233}
10233, 0); 10234 10235return Vue; 10236 10237})));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.