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