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