From f1167535af6a4a7cf7638b2f326a9e0c018bfd8d Mon Sep 17 00:00:00 2001 From: Jacob Schatz Date: Wed, 24 Feb 2016 10:00:16 -0500 Subject: [PATCH 1/6] Setup todos vuejs Add todo js file Add global container id --- app/assets/javascripts/Todos.js.coffee | 5 + app/assets/javascripts/application.js.coffee | 1 + app/assets/javascripts/dispatcher.js.coffee | 2 + app/helpers/page_layout_helper.rb | 6 + app/views/dashboard/todos/index.html.haml | 1 + app/views/layouts/header/_default.html.haml | 2 +- db/schema.rb | 87 + vendor/assets/javascripts/vue.js | 9517 ++++++++++++++++++ 8 files changed, 9620 insertions(+), 1 deletion(-) create mode 100644 app/assets/javascripts/Todos.js.coffee create mode 100644 vendor/assets/javascripts/vue.js diff --git a/app/assets/javascripts/Todos.js.coffee b/app/assets/javascripts/Todos.js.coffee new file mode 100644 index 000000000000..5c75fa5e43af --- /dev/null +++ b/app/assets/javascripts/Todos.js.coffee @@ -0,0 +1,5 @@ +class @Todos + constructor: -> + new Vue( + + ) \ No newline at end of file diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 367bd098bfdb..d5053a52f9bd 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -5,6 +5,7 @@ # the compiled file. # #= require jquery +#= require vue #= require jquery-ui/autocomplete #= require jquery-ui/datepicker #= require jquery-ui/effect-highlight diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 4c6cf3417e89..d79b97a9f57f 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -21,6 +21,8 @@ class Dispatcher when 'projects:issues:index' Issues.init() shortcut_handler = new ShortcutsNavigation() + when 'dashboard:todos:index' + new Todos() when 'projects:issues:show' new Issue() shortcut_handler = new ShortcutsIssuable() diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index 82f805fa4444..54083b64d9b9 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -34,6 +34,12 @@ def page_description(description = nil) end end + def container_id(title=nil) + if title + @container_id = title + end + end + def page_image default = image_url('gitlab_logo.png') diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index 946d7df39338..640c4b2cf593 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -1,5 +1,6 @@ - page_title "Todos" - header_title "Todos", dashboard_todos_path +- container_id "todos" .top-area %ul.nav-links diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 4781ff235070..580ecbb25996 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -1,5 +1,5 @@ %header.navbar.navbar-fixed-top.navbar-gitlab{ class: nav_header_class } - %div{ class: fluid_layout ? "container-fluid" : "container-fluid" } + %div{ class: "container-fluid #{container_id}" } .header-content %button.navbar-toggle{type: 'button'} %span.sr-only Toggle navigation diff --git a/db/schema.rb b/db/schema.rb index 4708c29d9ae9..d47ed856cad0 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -24,6 +24,17 @@ t.datetime "updated_at" end + create_table "appearances", force: :cascade do |t| + t.string "title" + t.text "description" + t.string "logo" + t.integer "updated_by" + t.datetime "created_at" + t.datetime "updated_at" + t.string "dark_logo" + t.string "light_logo" + end + create_table "application_settings", force: :cascade do |t| t.integer "default_projects_limit" t.boolean "signup_enabled" @@ -69,6 +80,24 @@ t.boolean "email_author_in_body", default: false end + create_table "approvals", force: :cascade do |t| + t.integer "merge_request_id", null: false + t.integer "user_id", null: false + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "approvers", force: :cascade do |t| + t.integer "target_id", null: false + t.string "target_type" + t.integer "user_id", null: false + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "approvers", ["target_id", "target_type"], name: "index_approvers_on_target_id_and_target_type", using: :btree + add_index "approvers", ["user_id"], name: "index_approvers_on_user_id", using: :btree + create_table "audit_events", force: :cascade do |t| t.integer "author_id", null: false t.string "type", null: false @@ -349,6 +378,19 @@ add_index "emails", ["email"], name: "index_emails_on_email", unique: true, using: :btree add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree + create_table "emoji_awards", force: :cascade do |t| + t.string "name" + t.integer "user_id" + t.integer "awardable_id" + t.string "awardable_type" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "emoji_awards", ["awardable_id"], name: "index_emoji_awards_on_awardable_id", using: :btree + add_index "emoji_awards", ["awardable_type"], name: "index_emoji_awards_on_awardable_type", using: :btree + add_index "emoji_awards", ["user_id"], name: "index_emoji_awards_on_user_id", using: :btree + create_table "events", force: :cascade do |t| t.string "target_type" t.integer "target_id" @@ -377,6 +419,28 @@ add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree + create_table "git_hooks", force: :cascade do |t| + t.string "force_push_regex" + t.string "delete_branch_regex" + t.string "commit_message_regex" + t.boolean "deny_delete_tag" + t.integer "project_id" + t.datetime "created_at" + t.datetime "updated_at" + t.string "author_email_regex" + t.boolean "member_check", default: false, null: false + t.string "file_name_regex" + t.boolean "is_sample", default: false + t.integer "max_file_size", default: 0, null: false + end + + create_table "historical_data", force: :cascade do |t| + t.date "date", null: false + t.integer "active_user_count" + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "identities", force: :cascade do |t| t.string "extern_uid" t.string "provider" @@ -451,6 +515,15 @@ add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree + create_table "ldap_group_links", force: :cascade do |t| + t.string "cn", null: false + t.integer "group_access", null: false + t.integer "group_id", null: false + t.datetime "created_at" + t.datetime "updated_at" + t.string "provider" + end + create_table "lfs_objects", force: :cascade do |t| t.string "oid", null: false t.integer "size", limit: 8, null: false @@ -470,6 +543,12 @@ add_index "lfs_objects_projects", ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree + create_table "licenses", force: :cascade do |t| + t.text "data", null: false + t.datetime "created_at" + t.datetime "updated_at" + end + create_table "members", force: :cascade do |t| t.integer "access_level", null: false t.integer "source_id", null: false @@ -646,6 +725,14 @@ add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree + create_table "project_group_links", force: :cascade do |t| + t.integer "project_id", null: false + t.integer "group_id", null: false + t.datetime "created_at" + t.datetime "updated_at" + t.integer "group_access", default: 30, null: false + end + create_table "project_import_data", force: :cascade do |t| t.integer "project_id" t.text "data" diff --git a/vendor/assets/javascripts/vue.js b/vendor/assets/javascripts/vue.js new file mode 100644 index 000000000000..ddace17ef13e --- /dev/null +++ b/vendor/assets/javascripts/vue.js @@ -0,0 +1,9517 @@ +/*! + * Vue.js v1.0.14 + * (c) 2016 Evan You + * Released under the MIT License. + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + global.Vue = factory(); +}(this, function () { 'use strict'; + + function set(obj, key, val) { + if (hasOwn(obj, key)) { + obj[key] = val; + return; + } + if (obj._isVue) { + set(obj._data, key, val); + return; + } + var ob = obj.__ob__; + if (!ob) { + obj[key] = val; + return; + } + ob.convert(key, val); + ob.dep.notify(); + if (ob.vms) { + var i = ob.vms.length; + while (i--) { + var vm = ob.vms[i]; + vm._proxy(key); + vm._digest(); + } + } + return val; + } + + /** + * Delete a property and trigger change if necessary. + * + * @param {Object} obj + * @param {String} key + */ + + function del(obj, key) { + if (!hasOwn(obj, key)) { + return; + } + delete obj[key]; + var ob = obj.__ob__; + if (!ob) { + return; + } + ob.dep.notify(); + if (ob.vms) { + var i = ob.vms.length; + while (i--) { + var vm = ob.vms[i]; + vm._unproxy(key); + vm._digest(); + } + } + } + + var hasOwnProperty = Object.prototype.hasOwnProperty; + /** + * Check whether the object has the property. + * + * @param {Object} obj + * @param {String} key + * @return {Boolean} + */ + + function hasOwn(obj, key) { + return hasOwnProperty.call(obj, key); + } + + /** + * Check if an expression is a literal value. + * + * @param {String} exp + * @return {Boolean} + */ + + var literalValueRE = /^\s?(true|false|[\d\.]+|'[^']*'|"[^"]*")\s?$/; + + function isLiteral(exp) { + return literalValueRE.test(exp); + } + + /** + * Check if a string starts with $ or _ + * + * @param {String} str + * @return {Boolean} + */ + + function isReserved(str) { + var c = (str + '').charCodeAt(0); + return c === 0x24 || c === 0x5F; + } + + /** + * Guard text output, make sure undefined outputs + * empty string + * + * @param {*} value + * @return {String} + */ + + function _toString(value) { + return value == null ? '' : value.toString(); + } + + /** + * Check and convert possible numeric strings to numbers + * before setting back to data + * + * @param {*} value + * @return {*|Number} + */ + + function toNumber(value) { + if (typeof value !== 'string') { + return value; + } else { + var parsed = Number(value); + return isNaN(parsed) ? value : parsed; + } + } + + /** + * Convert string boolean literals into real booleans. + * + * @param {*} value + * @return {*|Boolean} + */ + + function toBoolean(value) { + return value === 'true' ? true : value === 'false' ? false : value; + } + + /** + * Strip quotes from a string + * + * @param {String} str + * @return {String | false} + */ + + function stripQuotes(str) { + var a = str.charCodeAt(0); + var b = str.charCodeAt(str.length - 1); + return a === b && (a === 0x22 || a === 0x27) ? str.slice(1, -1) : str; + } + + /** + * Camelize a hyphen-delmited string. + * + * @param {String} str + * @return {String} + */ + + var camelizeRE = /-(\w)/g; + + function camelize(str) { + return str.replace(camelizeRE, toUpper); + } + + function toUpper(_, c) { + return c ? c.toUpperCase() : ''; + } + + /** + * Hyphenate a camelCase string. + * + * @param {String} str + * @return {String} + */ + + var hyphenateRE = /([a-z\d])([A-Z])/g; + + function hyphenate(str) { + return str.replace(hyphenateRE, '$1-$2').toLowerCase(); + } + + /** + * Converts hyphen/underscore/slash delimitered names into + * camelized classNames. + * + * e.g. my-component => MyComponent + * some_else => SomeElse + * some/comp => SomeComp + * + * @param {String} str + * @return {String} + */ + + var classifyRE = /(?:^|[-_\/])(\w)/g; + + function classify(str) { + return str.replace(classifyRE, toUpper); + } + + /** + * Simple bind, faster than native + * + * @param {Function} fn + * @param {Object} ctx + * @return {Function} + */ + + function bind$1(fn, ctx) { + return function (a) { + var l = arguments.length; + return l ? l > 1 ? fn.apply(ctx, arguments) : fn.call(ctx, a) : fn.call(ctx); + }; + } + + /** + * Convert an Array-like object to a real Array. + * + * @param {Array-like} list + * @param {Number} [start] - start index + * @return {Array} + */ + + function toArray(list, start) { + start = start || 0; + var i = list.length - start; + var ret = new Array(i); + while (i--) { + ret[i] = list[i + start]; + } + return ret; + } + + /** + * Mix properties into target object. + * + * @param {Object} to + * @param {Object} from + */ + + function extend(to, from) { + var keys = Object.keys(from); + var i = keys.length; + while (i--) { + to[keys[i]] = from[keys[i]]; + } + return to; + } + + /** + * Quick object check - this is primarily used to tell + * Objects from primitive values when we know the value + * is a JSON-compliant type. + * + * @param {*} obj + * @return {Boolean} + */ + + function isObject(obj) { + return obj !== null && typeof obj === 'object'; + } + + /** + * Strict object type check. Only returns true + * for plain JavaScript objects. + * + * @param {*} obj + * @return {Boolean} + */ + + var toString = Object.prototype.toString; + var OBJECT_STRING = '[object Object]'; + + function isPlainObject(obj) { + return toString.call(obj) === OBJECT_STRING; + } + + /** + * Array type check. + * + * @param {*} obj + * @return {Boolean} + */ + + var isArray = Array.isArray; + + /** + * Define a non-enumerable property + * + * @param {Object} obj + * @param {String} key + * @param {*} val + * @param {Boolean} [enumerable] + */ + + function def(obj, key, val, enumerable) { + Object.defineProperty(obj, key, { + value: val, + enumerable: !!enumerable, + writable: true, + configurable: true + }); + } + + /** + * Debounce a function so it only gets called after the + * input stops arriving after the given wait period. + * + * @param {Function} func + * @param {Number} wait + * @return {Function} - the debounced function + */ + + function _debounce(func, wait) { + var timeout, args, context, timestamp, result; + var later = function later() { + var last = Date.now() - timestamp; + if (last < wait && last >= 0) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + result = func.apply(context, args); + if (!timeout) context = args = null; + } + }; + return function () { + context = this; + args = arguments; + timestamp = Date.now(); + if (!timeout) { + timeout = setTimeout(later, wait); + } + return result; + }; + } + + /** + * Manual indexOf because it's slightly faster than + * native. + * + * @param {Array} arr + * @param {*} obj + */ + + function indexOf(arr, obj) { + var i = arr.length; + while (i--) { + if (arr[i] === obj) return i; + } + return -1; + } + + /** + * Make a cancellable version of an async callback. + * + * @param {Function} fn + * @return {Function} + */ + + function cancellable(fn) { + var cb = function cb() { + if (!cb.cancelled) { + return fn.apply(this, arguments); + } + }; + cb.cancel = function () { + cb.cancelled = true; + }; + return cb; + } + + /** + * Check if two values are loosely equal - that is, + * if they are plain objects, do they have the same shape? + * + * @param {*} a + * @param {*} b + * @return {Boolean} + */ + + function looseEqual(a, b) { + /* eslint-disable eqeqeq */ + return a == b || (isObject(a) && isObject(b) ? JSON.stringify(a) === JSON.stringify(b) : false); + /* eslint-enable eqeqeq */ + } + + var hasProto = ('__proto__' in {}); + + // Browser environment sniffing + var inBrowser = typeof window !== 'undefined' && Object.prototype.toString.call(window) !== '[object Object]'; + + var isIE9 = inBrowser && navigator.userAgent.toLowerCase().indexOf('msie 9.0') > 0; + + var isAndroid = inBrowser && navigator.userAgent.toLowerCase().indexOf('android') > 0; + + var transitionProp = undefined; + var transitionEndEvent = undefined; + var animationProp = undefined; + var animationEndEvent = undefined; + + // Transition property/event sniffing + if (inBrowser && !isIE9) { + var isWebkitTrans = window.ontransitionend === undefined && window.onwebkittransitionend !== undefined; + var isWebkitAnim = window.onanimationend === undefined && window.onwebkitanimationend !== undefined; + transitionProp = isWebkitTrans ? 'WebkitTransition' : 'transition'; + transitionEndEvent = isWebkitTrans ? 'webkitTransitionEnd' : 'transitionend'; + animationProp = isWebkitAnim ? 'WebkitAnimation' : 'animation'; + animationEndEvent = isWebkitAnim ? 'webkitAnimationEnd' : 'animationend'; + } + + /** + * Defer a task to execute it asynchronously. Ideally this + * should be executed as a microtask, so we leverage + * MutationObserver if it's available, and fallback to + * setTimeout(0). + * + * @param {Function} cb + * @param {Object} ctx + */ + + var nextTick = (function () { + var callbacks = []; + var pending = false; + var timerFunc; + function nextTickHandler() { + pending = false; + var copies = callbacks.slice(0); + callbacks = []; + for (var i = 0; i < copies.length; i++) { + copies[i](); + } + } + /* istanbul ignore if */ + if (typeof MutationObserver !== 'undefined') { + var counter = 1; + var observer = new MutationObserver(nextTickHandler); + var textNode = document.createTextNode(counter); + observer.observe(textNode, { + characterData: true + }); + timerFunc = function () { + counter = (counter + 1) % 2; + textNode.data = counter; + }; + } else { + timerFunc = setTimeout; + } + return function (cb, ctx) { + var func = ctx ? function () { + cb.call(ctx); + } : cb; + callbacks.push(func); + if (pending) return; + pending = true; + timerFunc(nextTickHandler, 0); + }; + })(); + + function Cache(limit) { + this.size = 0; + this.limit = limit; + this.head = this.tail = undefined; + this._keymap = Object.create(null); + } + + var p = Cache.prototype; + + /** + * Put into the cache associated with . + * Returns the entry which was removed to make room for + * the new entry. Otherwise undefined is returned. + * (i.e. if there was enough room already). + * + * @param {String} key + * @param {*} value + * @return {Entry|undefined} + */ + + p.put = function (key, value) { + var entry = { + key: key, + value: value + }; + this._keymap[key] = entry; + if (this.tail) { + this.tail.newer = entry; + entry.older = this.tail; + } else { + this.head = entry; + } + this.tail = entry; + if (this.size === this.limit) { + return this.shift(); + } else { + this.size++; + } + }; + + /** + * Purge the least recently used (oldest) entry from the + * cache. Returns the removed entry or undefined if the + * cache was empty. + */ + + p.shift = function () { + var entry = this.head; + if (entry) { + this.head = this.head.newer; + this.head.older = undefined; + entry.newer = entry.older = undefined; + this._keymap[entry.key] = undefined; + } + return entry; + }; + + /** + * Get and register recent use of . Returns the value + * associated with or undefined if not in cache. + * + * @param {String} key + * @param {Boolean} returnEntry + * @return {Entry|*} + */ + + p.get = function (key, returnEntry) { + var entry = this._keymap[key]; + if (entry === undefined) return; + if (entry === this.tail) { + return returnEntry ? entry : entry.value; + } + // HEAD--------------TAIL + // <.older .newer> + // <--- add direction -- + // A B C E + if (entry.newer) { + if (entry === this.head) { + this.head = entry.newer; + } + entry.newer.older = entry.older; // C <-- E. + } + if (entry.older) { + entry.older.newer = entry.newer; // C. --> E + } + entry.newer = undefined; // D --x + entry.older = this.tail; // D. --> E + if (this.tail) { + this.tail.newer = entry; // E. <-- D + } + this.tail = entry; + return returnEntry ? entry : entry.value; + }; + + var cache$1 = new Cache(1000); + var filterTokenRE = /[^\s'"]+|'[^']*'|"[^"]*"/g; + var reservedArgRE = /^in$|^-?\d+/; + + /** + * Parser state + */ + + var str; + var dir; + var c; + var prev; + var i; + var l; + var lastFilterIndex; + var inSingle; + var inDouble; + var curly; + var square; + var paren; + /** + * Push a filter to the current directive object + */ + + function pushFilter() { + var exp = str.slice(lastFilterIndex, i).trim(); + var filter; + if (exp) { + filter = {}; + var tokens = exp.match(filterTokenRE); + filter.name = tokens[0]; + if (tokens.length > 1) { + filter.args = tokens.slice(1).map(processFilterArg); + } + } + if (filter) { + (dir.filters = dir.filters || []).push(filter); + } + lastFilterIndex = i + 1; + } + + /** + * Check if an argument is dynamic and strip quotes. + * + * @param {String} arg + * @return {Object} + */ + + function processFilterArg(arg) { + if (reservedArgRE.test(arg)) { + return { + value: toNumber(arg), + dynamic: false + }; + } else { + var stripped = stripQuotes(arg); + var dynamic = stripped === arg; + return { + value: dynamic ? arg : stripped, + dynamic: dynamic + }; + } + } + + /** + * Parse a directive value and extract the expression + * and its filters into a descriptor. + * + * Example: + * + * "a + 1 | uppercase" will yield: + * { + * expression: 'a + 1', + * filters: [ + * { name: 'uppercase', args: null } + * ] + * } + * + * @param {String} str + * @return {Object} + */ + + function parseDirective(s) { + + var hit = cache$1.get(s); + if (hit) { + return hit; + } + + // reset parser state + str = s; + inSingle = inDouble = false; + curly = square = paren = 0; + lastFilterIndex = 0; + dir = {}; + + for (i = 0, l = str.length; i < l; i++) { + prev = c; + c = str.charCodeAt(i); + if (inSingle) { + // check single quote + if (c === 0x27 && prev !== 0x5C) inSingle = !inSingle; + } else if (inDouble) { + // check double quote + if (c === 0x22 && prev !== 0x5C) inDouble = !inDouble; + } else if (c === 0x7C && // pipe + str.charCodeAt(i + 1) !== 0x7C && str.charCodeAt(i - 1) !== 0x7C) { + if (dir.expression == null) { + // first filter, end of expression + lastFilterIndex = i + 1; + dir.expression = str.slice(0, i).trim(); + } else { + // already has filter + pushFilter(); + } + } else { + switch (c) { + case 0x22: + inDouble = true;break; // " + case 0x27: + inSingle = true;break; // ' + case 0x28: + paren++;break; // ( + case 0x29: + paren--;break; // ) + case 0x5B: + square++;break; // [ + case 0x5D: + square--;break; // ] + case 0x7B: + curly++;break; // { + case 0x7D: + curly--;break; // } + } + } + } + + if (dir.expression == null) { + dir.expression = str.slice(0, i).trim(); + } else if (lastFilterIndex !== 0) { + pushFilter(); + } + + cache$1.put(s, dir); + return dir; + } + + var directive = Object.freeze({ + parseDirective: parseDirective + }); + + var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g; + var cache = undefined; + var tagRE = undefined; + var htmlRE = undefined; + /** + * Escape a string so it can be used in a RegExp + * constructor. + * + * @param {String} str + */ + + function escapeRegex(str) { + return str.replace(regexEscapeRE, '\\$&'); + } + + function compileRegex() { + var open = escapeRegex(config.delimiters[0]); + var close = escapeRegex(config.delimiters[1]); + var unsafeOpen = escapeRegex(config.unsafeDelimiters[0]); + var unsafeClose = escapeRegex(config.unsafeDelimiters[1]); + tagRE = new RegExp(unsafeOpen + '(.+?)' + unsafeClose + '|' + open + '(.+?)' + close, 'g'); + htmlRE = new RegExp('^' + unsafeOpen + '.*' + unsafeClose + '$'); + // reset cache + cache = new Cache(1000); + } + + /** + * Parse a template text string into an array of tokens. + * + * @param {String} text + * @return {Array | null} + * - {String} type + * - {String} value + * - {Boolean} [html] + * - {Boolean} [oneTime] + */ + + function parseText(text) { + if (!cache) { + compileRegex(); + } + var hit = cache.get(text); + if (hit) { + return hit; + } + text = text.replace(/\n/g, ''); + if (!tagRE.test(text)) { + return null; + } + var tokens = []; + var lastIndex = tagRE.lastIndex = 0; + var match, index, html, value, first, oneTime; + /* eslint-disable no-cond-assign */ + while (match = tagRE.exec(text)) { + /* eslint-enable no-cond-assign */ + index = match.index; + // push text token + if (index > lastIndex) { + tokens.push({ + value: text.slice(lastIndex, index) + }); + } + // tag token + html = htmlRE.test(match[0]); + value = html ? match[1] : match[2]; + first = value.charCodeAt(0); + oneTime = first === 42; // * + value = oneTime ? value.slice(1) : value; + tokens.push({ + tag: true, + value: value.trim(), + html: html, + oneTime: oneTime + }); + lastIndex = index + match[0].length; + } + if (lastIndex < text.length) { + tokens.push({ + value: text.slice(lastIndex) + }); + } + cache.put(text, tokens); + return tokens; + } + + /** + * Format a list of tokens into an expression. + * e.g. tokens parsed from 'a {{b}} c' can be serialized + * into one single expression as '"a " + b + " c"'. + * + * @param {Array} tokens + * @param {Vue} [vm] + * @return {String} + */ + + function tokensToExp(tokens, vm) { + if (tokens.length > 1) { + return tokens.map(function (token) { + return formatToken(token, vm); + }).join('+'); + } else { + return formatToken(tokens[0], vm, true); + } + } + + /** + * Format a single token. + * + * @param {Object} token + * @param {Vue} [vm] + * @param {Boolean} [single] + * @return {String} + */ + + function formatToken(token, vm, single) { + return token.tag ? token.oneTime && vm ? '"' + vm.$eval(token.value) + '"' : inlineFilters(token.value, single) : '"' + token.value + '"'; + } + + /** + * For an attribute with multiple interpolation tags, + * e.g. attr="some-{{thing | filter}}", in order to combine + * the whole thing into a single watchable expression, we + * have to inline those filters. This function does exactly + * that. This is a bit hacky but it avoids heavy changes + * to directive parser and watcher mechanism. + * + * @param {String} exp + * @param {Boolean} single + * @return {String} + */ + + var filterRE$1 = /[^|]\|[^|]/; + function inlineFilters(exp, single) { + if (!filterRE$1.test(exp)) { + return single ? exp : '(' + exp + ')'; + } else { + var dir = parseDirective(exp); + if (!dir.filters) { + return '(' + exp + ')'; + } else { + return 'this._applyFilters(' + dir.expression + // value + ',null,' + // oldValue (null for read) + JSON.stringify(dir.filters) + // filter descriptors + ',false)'; // write? + } + } + } + + var text$1 = Object.freeze({ + compileRegex: compileRegex, + parseText: parseText, + tokensToExp: tokensToExp + }); + + var delimiters = ['{{', '}}']; + var unsafeDelimiters = ['{{{', '}}}']; + + var config = Object.defineProperties({ + + /** + * Whether to print debug messages. + * Also enables stack trace for warnings. + * + * @type {Boolean} + */ + + debug: false, + + /** + * Whether to suppress warnings. + * + * @type {Boolean} + */ + + silent: false, + + /** + * Whether to use async rendering. + */ + + async: true, + + /** + * Whether to warn against errors caught when evaluating + * expressions. + */ + + warnExpressionErrors: true, + + /** + * Whether or not to handle fully object properties which + * are already backed by getters and seters. Depending on + * use case and environment, this might introduce non-neglible + * performance penalties. + */ + convertAllProperties: false, + + /** + * Internal flag to indicate the delimiters have been + * changed. + * + * @type {Boolean} + */ + + _delimitersChanged: true, + + /** + * List of asset types that a component can own. + * + * @type {Array} + */ + + _assetTypes: ['component', 'directive', 'elementDirective', 'filter', 'transition', 'partial'], + + /** + * prop binding modes + */ + + _propBindingModes: { + ONE_WAY: 0, + TWO_WAY: 1, + ONE_TIME: 2 + }, + + /** + * Max circular updates allowed in a batcher flush cycle. + */ + + _maxUpdateCount: 100 + + }, { + delimiters: { /** + * Interpolation delimiters. Changing these would trigger + * the text parser to re-compile the regular expressions. + * + * @type {Array} + */ + + get: function get() { + return delimiters; + }, + set: function set(val) { + delimiters = val; + compileRegex(); + }, + configurable: true, + enumerable: true + }, + unsafeDelimiters: { + get: function get() { + return unsafeDelimiters; + }, + set: function set(val) { + unsafeDelimiters = val; + compileRegex(); + }, + configurable: true, + enumerable: true + } + }); + + var warn = undefined; + + if ('development' !== 'production') { + (function () { + var hasConsole = typeof console !== 'undefined'; + warn = function (msg, e) { + if (hasConsole && (!config.silent || config.debug)) { + console.warn('[Vue warn]: ' + msg); + /* istanbul ignore if */ + if (config.debug) { + if (e) { + throw e; + } else { + console.warn(new Error('Warning Stack Trace').stack); + } + } + } + }; + })(); + } + + /** + * Append with transition. + * + * @param {Element} el + * @param {Element} target + * @param {Vue} vm + * @param {Function} [cb] + */ + + function appendWithTransition(el, target, vm, cb) { + applyTransition(el, 1, function () { + target.appendChild(el); + }, vm, cb); + } + + /** + * InsertBefore with transition. + * + * @param {Element} el + * @param {Element} target + * @param {Vue} vm + * @param {Function} [cb] + */ + + function beforeWithTransition(el, target, vm, cb) { + applyTransition(el, 1, function () { + before(el, target); + }, vm, cb); + } + + /** + * Remove with transition. + * + * @param {Element} el + * @param {Vue} vm + * @param {Function} [cb] + */ + + function removeWithTransition(el, vm, cb) { + applyTransition(el, -1, function () { + remove(el); + }, vm, cb); + } + + /** + * Apply transitions with an operation callback. + * + * @param {Element} el + * @param {Number} direction + * 1: enter + * -1: leave + * @param {Function} op - the actual DOM operation + * @param {Vue} vm + * @param {Function} [cb] + */ + + function applyTransition(el, direction, op, vm, cb) { + var transition = el.__v_trans; + if (!transition || + // skip if there are no js hooks and CSS transition is + // not supported + !transition.hooks && !transitionEndEvent || + // skip transitions for initial compile + !vm._isCompiled || + // if the vm is being manipulated by a parent directive + // during the parent's compilation phase, skip the + // animation. + vm.$parent && !vm.$parent._isCompiled) { + op(); + if (cb) cb(); + return; + } + var action = direction > 0 ? 'enter' : 'leave'; + transition[action](op, cb); + } + + /** + * Query an element selector if it's not an element already. + * + * @param {String|Element} el + * @return {Element} + */ + + function query(el) { + if (typeof el === 'string') { + var selector = el; + el = document.querySelector(el); + if (!el) { + 'development' !== 'production' && warn('Cannot find element: ' + selector); + } + } + return el; + } + + /** + * Check if a node is in the document. + * Note: document.documentElement.contains should work here + * but always returns false for comment nodes in phantomjs, + * making unit tests difficult. This is fixed by doing the + * contains() check on the node's parentNode instead of + * the node itself. + * + * @param {Node} node + * @return {Boolean} + */ + + function inDoc(node) { + var doc = document.documentElement; + var parent = node && node.parentNode; + return doc === node || doc === parent || !!(parent && parent.nodeType === 1 && doc.contains(parent)); + } + + /** + * Get and remove an attribute from a node. + * + * @param {Node} node + * @param {String} _attr + */ + + function getAttr(node, _attr) { + var val = node.getAttribute(_attr); + if (val !== null) { + node.removeAttribute(_attr); + } + return val; + } + + /** + * Get an attribute with colon or v-bind: prefix. + * + * @param {Node} node + * @param {String} name + * @return {String|null} + */ + + function getBindAttr(node, name) { + var val = getAttr(node, ':' + name); + if (val === null) { + val = getAttr(node, 'v-bind:' + name); + } + return val; + } + + /** + * Check the presence of a bind attribute. + * + * @param {Node} node + * @param {String} name + * @return {Boolean} + */ + + function hasBindAttr(node, name) { + return node.hasAttribute(name) || node.hasAttribute(':' + name) || node.hasAttribute('v-bind:' + name); + } + + /** + * Insert el before target + * + * @param {Element} el + * @param {Element} target + */ + + function before(el, target) { + target.parentNode.insertBefore(el, target); + } + + /** + * Insert el after target + * + * @param {Element} el + * @param {Element} target + */ + + function after(el, target) { + if (target.nextSibling) { + before(el, target.nextSibling); + } else { + target.parentNode.appendChild(el); + } + } + + /** + * Remove el from DOM + * + * @param {Element} el + */ + + function remove(el) { + el.parentNode.removeChild(el); + } + + /** + * Prepend el to target + * + * @param {Element} el + * @param {Element} target + */ + + function prepend(el, target) { + if (target.firstChild) { + before(el, target.firstChild); + } else { + target.appendChild(el); + } + } + + /** + * Replace target with el + * + * @param {Element} target + * @param {Element} el + */ + + function replace(target, el) { + var parent = target.parentNode; + if (parent) { + parent.replaceChild(el, target); + } + } + + /** + * Add event listener shorthand. + * + * @param {Element} el + * @param {String} event + * @param {Function} cb + */ + + function on$1(el, event, cb) { + el.addEventListener(event, cb); + } + + /** + * Remove event listener shorthand. + * + * @param {Element} el + * @param {String} event + * @param {Function} cb + */ + + function off(el, event, cb) { + el.removeEventListener(event, cb); + } + + /** + * In IE9, setAttribute('class') will result in empty class + * if the element also has the :class attribute; However in + * PhantomJS, setting `className` does not work on SVG elements... + * So we have to do a conditional check here. + * + * @param {Element} el + * @param {String} cls + */ + + function setClass(el, cls) { + /* istanbul ignore if */ + if (isIE9 && !(el instanceof SVGElement)) { + el.className = cls; + } else { + el.setAttribute('class', cls); + } + } + + /** + * Add class with compatibility for IE & SVG + * + * @param {Element} el + * @param {String} cls + */ + + function addClass(el, cls) { + if (el.classList) { + el.classList.add(cls); + } else { + var cur = ' ' + (el.getAttribute('class') || '') + ' '; + if (cur.indexOf(' ' + cls + ' ') < 0) { + setClass(el, (cur + cls).trim()); + } + } + } + + /** + * Remove class with compatibility for IE & SVG + * + * @param {Element} el + * @param {String} cls + */ + + function removeClass(el, cls) { + if (el.classList) { + el.classList.remove(cls); + } else { + var cur = ' ' + (el.getAttribute('class') || '') + ' '; + var tar = ' ' + cls + ' '; + while (cur.indexOf(tar) >= 0) { + cur = cur.replace(tar, ' '); + } + setClass(el, cur.trim()); + } + if (!el.className) { + el.removeAttribute('class'); + } + } + + /** + * Extract raw content inside an element into a temporary + * container div + * + * @param {Element} el + * @param {Boolean} asFragment + * @return {Element} + */ + + function extractContent(el, asFragment) { + var child; + var rawContent; + /* istanbul ignore if */ + if (isTemplate(el) && el.content instanceof DocumentFragment) { + el = el.content; + } + if (el.hasChildNodes()) { + trimNode(el); + rawContent = asFragment ? document.createDocumentFragment() : document.createElement('div'); + /* eslint-disable no-cond-assign */ + while (child = el.firstChild) { + /* eslint-enable no-cond-assign */ + rawContent.appendChild(child); + } + } + return rawContent; + } + + /** + * Trim possible empty head/tail textNodes inside a parent. + * + * @param {Node} node + */ + + function trimNode(node) { + trim(node, node.firstChild); + trim(node, node.lastChild); + } + + function trim(parent, node) { + if (node && node.nodeType === 3 && !node.data.trim()) { + parent.removeChild(node); + } + } + + /** + * Check if an element is a template tag. + * Note if the template appears inside an SVG its tagName + * will be in lowercase. + * + * @param {Element} el + */ + + function isTemplate(el) { + return el.tagName && el.tagName.toLowerCase() === 'template'; + } + + /** + * Create an "anchor" for performing dom insertion/removals. + * This is used in a number of scenarios: + * - fragment instance + * - v-html + * - v-if + * - v-for + * - component + * + * @param {String} content + * @param {Boolean} persist - IE trashes empty textNodes on + * cloneNode(true), so in certain + * cases the anchor needs to be + * non-empty to be persisted in + * templates. + * @return {Comment|Text} + */ + + function createAnchor(content, persist) { + var anchor = config.debug ? document.createComment(content) : document.createTextNode(persist ? ' ' : ''); + anchor.__vue_anchor = true; + return anchor; + } + + /** + * Find a component ref attribute that starts with $. + * + * @param {Element} node + * @return {String|undefined} + */ + + var refRE = /^v-ref:/; + + function findRef(node) { + if (node.hasAttributes()) { + var attrs = node.attributes; + for (var i = 0, l = attrs.length; i < l; i++) { + var name = attrs[i].name; + if (refRE.test(name)) { + return camelize(name.replace(refRE, '')); + } + } + } + } + + /** + * Map a function to a range of nodes . + * + * @param {Node} node + * @param {Node} end + * @param {Function} op + */ + + function mapNodeRange(node, end, op) { + var next; + while (node !== end) { + next = node.nextSibling; + op(node); + node = next; + } + op(end); + } + + /** + * Remove a range of nodes with transition, store + * the nodes in a fragment with correct ordering, + * and call callback when done. + * + * @param {Node} start + * @param {Node} end + * @param {Vue} vm + * @param {DocumentFragment} frag + * @param {Function} cb + */ + + function removeNodeRange(start, end, vm, frag, cb) { + var done = false; + var removed = 0; + var nodes = []; + mapNodeRange(start, end, function (node) { + if (node === end) done = true; + nodes.push(node); + removeWithTransition(node, vm, onRemoved); + }); + function onRemoved() { + removed++; + if (done && removed >= nodes.length) { + for (var i = 0; i < nodes.length; i++) { + frag.appendChild(nodes[i]); + } + cb && cb(); + } + } + } + + 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)$/; + var reservedTagRE = /^(slot|partial|component)$/; + + /** + * Check if an element is a component, if yes return its + * component id. + * + * @param {Element} el + * @param {Object} options + * @return {Object|undefined} + */ + + function checkComponentAttr(el, options) { + var tag = el.tagName.toLowerCase(); + var hasAttrs = el.hasAttributes(); + if (!commonTagRE.test(tag) && !reservedTagRE.test(tag)) { + if (resolveAsset(options, 'components', tag)) { + return { id: tag }; + } else { + var is = hasAttrs && getIsBinding(el); + if (is) { + return is; + } else if ('development' !== 'production') { + if (tag.indexOf('-') > -1 || /HTMLUnknownElement/.test(el.toString()) && + // Chrome returns unknown for several HTML5 elements. + // https://code.google.com/p/chromium/issues/detail?id=540526 + !/^(data|time|rtc|rb)$/.test(tag)) { + warn('Unknown custom element: <' + tag + '> - did you ' + 'register the component correctly?'); + } + } + } + } else if (hasAttrs) { + return getIsBinding(el); + } + } + + /** + * Get "is" binding from an element. + * + * @param {Element} el + * @return {Object|undefined} + */ + + function getIsBinding(el) { + // dynamic syntax + var exp = getAttr(el, 'is'); + if (exp != null) { + return { id: exp }; + } else { + exp = getBindAttr(el, 'is'); + if (exp != null) { + return { id: exp, dynamic: true }; + } + } + } + + /** + * Set a prop's initial value on a vm and its data object. + * + * @param {Vue} vm + * @param {Object} prop + * @param {*} value + */ + + function initProp(vm, prop, value) { + var key = prop.path; + value = coerceProp(prop, value); + vm[key] = vm._data[key] = assertProp(prop, value) ? value : undefined; + } + + /** + * Assert whether a prop is valid. + * + * @param {Object} prop + * @param {*} value + */ + + function assertProp(prop, value) { + // if a prop is not provided and is not required, + // skip the check. + if (prop.raw === null && !prop.required) { + return true; + } + var options = prop.options; + var type = options.type; + var valid = true; + var expectedType; + if (type) { + if (type === String) { + expectedType = 'string'; + valid = typeof value === expectedType; + } else if (type === Number) { + expectedType = 'number'; + valid = typeof value === 'number'; + } else if (type === Boolean) { + expectedType = 'boolean'; + valid = typeof value === 'boolean'; + } else if (type === Function) { + expectedType = 'function'; + valid = typeof value === 'function'; + } else if (type === Object) { + expectedType = 'object'; + valid = isPlainObject(value); + } else if (type === Array) { + expectedType = 'array'; + valid = isArray(value); + } else { + valid = value instanceof type; + } + } + if (!valid) { + 'development' !== 'production' && warn('Invalid prop: type check failed for ' + prop.path + '="' + prop.raw + '".' + ' Expected ' + formatType(expectedType) + ', got ' + formatValue(value) + '.'); + return false; + } + var validator = options.validator; + if (validator) { + if (!validator.call(null, value)) { + 'development' !== 'production' && warn('Invalid prop: custom validator check failed for ' + prop.path + '="' + prop.raw + '"'); + return false; + } + } + return true; + } + + /** + * Force parsing value with coerce option. + * + * @param {*} value + * @param {Object} options + * @return {*} + */ + + function coerceProp(prop, value) { + var coerce = prop.options.coerce; + if (!coerce) { + return value; + } + // coerce is a function + return coerce(value); + } + + function formatType(val) { + return val ? val.charAt(0).toUpperCase() + val.slice(1) : 'custom type'; + } + + function formatValue(val) { + return Object.prototype.toString.call(val).slice(8, -1); + } + + /** + * Option overwriting strategies are functions that handle + * how to merge a parent option value and a child option + * value into the final value. + * + * All strategy functions follow the same signature: + * + * @param {*} parentVal + * @param {*} childVal + * @param {Vue} [vm] + */ + + var strats = config.optionMergeStrategies = Object.create(null); + + /** + * Helper that recursively merges two data objects together. + */ + + function mergeData(to, from) { + var key, toVal, fromVal; + for (key in from) { + toVal = to[key]; + fromVal = from[key]; + if (!hasOwn(to, key)) { + set(to, key, fromVal); + } else if (isObject(toVal) && isObject(fromVal)) { + mergeData(toVal, fromVal); + } + } + return to; + } + + /** + * Data + */ + + strats.data = function (parentVal, childVal, vm) { + if (!vm) { + // in a Vue.extend merge, both should be functions + if (!childVal) { + return parentVal; + } + if (typeof childVal !== 'function') { + 'development' !== 'production' && warn('The "data" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.'); + return parentVal; + } + if (!parentVal) { + return childVal; + } + // when parentVal & childVal are both present, + // we need to return a function that returns the + // merged result of both functions... no need to + // check if parentVal is a function here because + // it has to be a function to pass previous merges. + return function mergedDataFn() { + return mergeData(childVal.call(this), parentVal.call(this)); + }; + } else if (parentVal || childVal) { + return function mergedInstanceDataFn() { + // instance merge + var instanceData = typeof childVal === 'function' ? childVal.call(vm) : childVal; + var defaultData = typeof parentVal === 'function' ? parentVal.call(vm) : undefined; + if (instanceData) { + return mergeData(instanceData, defaultData); + } else { + return defaultData; + } + }; + } + }; + + /** + * El + */ + + strats.el = function (parentVal, childVal, vm) { + if (!vm && childVal && typeof childVal !== 'function') { + 'development' !== 'production' && warn('The "el" option should be a function ' + 'that returns a per-instance value in component ' + 'definitions.'); + return; + } + var ret = childVal || parentVal; + // invoke the element factory if this is instance merge + return vm && typeof ret === 'function' ? ret.call(vm) : ret; + }; + + /** + * Hooks and param attributes are merged as arrays. + */ + + strats.init = strats.created = strats.ready = strats.attached = strats.detached = strats.beforeCompile = strats.compiled = strats.beforeDestroy = strats.destroyed = function (parentVal, childVal) { + return childVal ? parentVal ? parentVal.concat(childVal) : isArray(childVal) ? childVal : [childVal] : parentVal; + }; + + /** + * 0.11 deprecation warning + */ + + strats.paramAttributes = function () { + /* istanbul ignore next */ + 'development' !== 'production' && warn('"paramAttributes" option has been deprecated in 0.12. ' + 'Use "props" instead.'); + }; + + /** + * Assets + * + * When a vm is present (instance creation), we need to do + * a three-way merge between constructor options, instance + * options and parent options. + */ + + function mergeAssets(parentVal, childVal) { + var res = Object.create(parentVal); + return childVal ? extend(res, guardArrayAssets(childVal)) : res; + } + + config._assetTypes.forEach(function (type) { + strats[type + 's'] = mergeAssets; + }); + + /** + * Events & Watchers. + * + * Events & watchers hashes should not overwrite one + * another, so we merge them as arrays. + */ + + strats.watch = strats.events = function (parentVal, childVal) { + if (!childVal) return parentVal; + if (!parentVal) return childVal; + var ret = {}; + extend(ret, parentVal); + for (var key in childVal) { + var parent = ret[key]; + var child = childVal[key]; + if (parent && !isArray(parent)) { + parent = [parent]; + } + ret[key] = parent ? parent.concat(child) : [child]; + } + return ret; + }; + + /** + * Other object hashes. + */ + + strats.props = strats.methods = strats.computed = function (parentVal, childVal) { + if (!childVal) return parentVal; + if (!parentVal) return childVal; + var ret = Object.create(null); + extend(ret, parentVal); + extend(ret, childVal); + return ret; + }; + + /** + * Default strategy. + */ + + var defaultStrat = function defaultStrat(parentVal, childVal) { + return childVal === undefined ? parentVal : childVal; + }; + + /** + * Make sure component options get converted to actual + * constructors. + * + * @param {Object} options + */ + + function guardComponents(options) { + if (options.components) { + var components = options.components = guardArrayAssets(options.components); + var def; + var ids = Object.keys(components); + for (var i = 0, l = ids.length; i < l; i++) { + var key = ids[i]; + if (commonTagRE.test(key) || reservedTagRE.test(key)) { + 'development' !== 'production' && warn('Do not use built-in or reserved HTML elements as component ' + 'id: ' + key); + continue; + } + def = components[key]; + if (isPlainObject(def)) { + components[key] = Vue.extend(def); + } + } + } + } + + /** + * Ensure all props option syntax are normalized into the + * Object-based format. + * + * @param {Object} options + */ + + function guardProps(options) { + var props = options.props; + var i, val; + if (isArray(props)) { + options.props = {}; + i = props.length; + while (i--) { + val = props[i]; + if (typeof val === 'string') { + options.props[val] = null; + } else if (val.name) { + options.props[val.name] = val; + } + } + } else if (isPlainObject(props)) { + var keys = Object.keys(props); + i = keys.length; + while (i--) { + val = props[keys[i]]; + if (typeof val === 'function') { + props[keys[i]] = { type: val }; + } + } + } + } + + /** + * Guard an Array-format assets option and converted it + * into the key-value Object format. + * + * @param {Object|Array} assets + * @return {Object} + */ + + function guardArrayAssets(assets) { + if (isArray(assets)) { + var res = {}; + var i = assets.length; + var asset; + while (i--) { + asset = assets[i]; + var id = typeof asset === 'function' ? asset.options && asset.options.name || asset.id : asset.name || asset.id; + if (!id) { + 'development' !== 'production' && warn('Array-syntax assets must provide a "name" or "id" field.'); + } else { + res[id] = asset; + } + } + return res; + } + return assets; + } + + /** + * Merge two option objects into a new one. + * Core utility used in both instantiation and inheritance. + * + * @param {Object} parent + * @param {Object} child + * @param {Vue} [vm] - if vm is present, indicates this is + * an instantiation merge. + */ + + function mergeOptions(parent, child, vm) { + guardComponents(child); + guardProps(child); + var options = {}; + var key; + if (child.mixins) { + for (var i = 0, l = child.mixins.length; i < l; i++) { + parent = mergeOptions(parent, child.mixins[i], vm); + } + } + for (key in parent) { + mergeField(key); + } + for (key in child) { + if (!hasOwn(parent, key)) { + mergeField(key); + } + } + function mergeField(key) { + var strat = strats[key] || defaultStrat; + options[key] = strat(parent[key], child[key], vm, key); + } + return options; + } + + /** + * Resolve an asset. + * This function is used because child instances need access + * to assets defined in its ancestor chain. + * + * @param {Object} options + * @param {String} type + * @param {String} id + * @return {Object|Function} + */ + + function resolveAsset(options, type, id) { + var assets = options[type]; + var camelizedId; + return assets[id] || + // camelCase ID + assets[camelizedId = camelize(id)] || + // Pascal Case ID + assets[camelizedId.charAt(0).toUpperCase() + camelizedId.slice(1)]; + } + + /** + * Assert asset exists + */ + + function assertAsset(val, type, id) { + if (!val) { + 'development' !== 'production' && warn('Failed to resolve ' + type + ': ' + id); + } + } + + var arrayProto = Array.prototype; + var arrayMethods = Object.create(arrayProto) + + /** + * Intercept mutating methods and emit events + */ + + ;['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(function (method) { + // cache original method + var original = arrayProto[method]; + def(arrayMethods, method, function mutator() { + // avoid leaking arguments: + // http://jsperf.com/closure-with-arguments + var i = arguments.length; + var args = new Array(i); + while (i--) { + args[i] = arguments[i]; + } + var result = original.apply(this, args); + var ob = this.__ob__; + var inserted; + switch (method) { + case 'push': + inserted = args; + break; + case 'unshift': + inserted = args; + break; + case 'splice': + inserted = args.slice(2); + break; + } + if (inserted) ob.observeArray(inserted); + // notify change + ob.dep.notify(); + return result; + }); + }); + + /** + * Swap the element at the given index with a new value + * and emits corresponding event. + * + * @param {Number} index + * @param {*} val + * @return {*} - replaced element + */ + + def(arrayProto, '$set', function $set(index, val) { + if (index >= this.length) { + this.length = Number(index) + 1; + } + return this.splice(index, 1, val)[0]; + }); + + /** + * Convenience method to remove the element at given index. + * + * @param {Number} index + * @param {*} val + */ + + def(arrayProto, '$remove', function $remove(item) { + /* istanbul ignore if */ + if (!this.length) return; + var index = indexOf(this, item); + if (index > -1) { + return this.splice(index, 1); + } + }); + + var uid$3 = 0; + + /** + * A dep is an observable that can have multiple + * directives subscribing to it. + * + * @constructor + */ + function Dep() { + this.id = uid$3++; + this.subs = []; + } + + // the current target watcher being evaluated. + // this is globally unique because there could be only one + // watcher being evaluated at any time. + Dep.target = null; + + /** + * Add a directive subscriber. + * + * @param {Directive} sub + */ + + Dep.prototype.addSub = function (sub) { + this.subs.push(sub); + }; + + /** + * Remove a directive subscriber. + * + * @param {Directive} sub + */ + + Dep.prototype.removeSub = function (sub) { + this.subs.$remove(sub); + }; + + /** + * Add self as a dependency to the target watcher. + */ + + Dep.prototype.depend = function () { + Dep.target.addDep(this); + }; + + /** + * Notify all subscribers of a new value. + */ + + Dep.prototype.notify = function () { + // stablize the subscriber list first + var subs = toArray(this.subs); + for (var i = 0, l = subs.length; i < l; i++) { + subs[i].update(); + } + }; + + var arrayKeys = Object.getOwnPropertyNames(arrayMethods); + + /** + * Observer class that are attached to each observed + * object. Once attached, the observer converts target + * object's property keys into getter/setters that + * collect dependencies and dispatches updates. + * + * @param {Array|Object} value + * @constructor + */ + + function Observer(value) { + this.value = value; + this.dep = new Dep(); + def(value, '__ob__', this); + if (isArray(value)) { + var augment = hasProto ? protoAugment : copyAugment; + augment(value, arrayMethods, arrayKeys); + this.observeArray(value); + } else { + this.walk(value); + } + } + + // Instance methods + + /** + * Walk through each property and convert them into + * getter/setters. This method should only be called when + * value type is Object. + * + * @param {Object} obj + */ + + Observer.prototype.walk = function (obj) { + var keys = Object.keys(obj); + for (var i = 0, l = keys.length; i < l; i++) { + this.convert(keys[i], obj[keys[i]]); + } + }; + + /** + * Observe a list of Array items. + * + * @param {Array} items + */ + + Observer.prototype.observeArray = function (items) { + for (var i = 0, l = items.length; i < l; i++) { + observe(items[i]); + } + }; + + /** + * Convert a property into getter/setter so we can emit + * the events when the property is accessed/changed. + * + * @param {String} key + * @param {*} val + */ + + Observer.prototype.convert = function (key, val) { + defineReactive(this.value, key, val); + }; + + /** + * Add an owner vm, so that when $set/$delete mutations + * happen we can notify owner vms to proxy the keys and + * digest the watchers. This is only called when the object + * is observed as an instance's root $data. + * + * @param {Vue} vm + */ + + Observer.prototype.addVm = function (vm) { + (this.vms || (this.vms = [])).push(vm); + }; + + /** + * Remove an owner vm. This is called when the object is + * swapped out as an instance's $data object. + * + * @param {Vue} vm + */ + + Observer.prototype.removeVm = function (vm) { + this.vms.$remove(vm); + }; + + // helpers + + /** + * Augment an target Object or Array by intercepting + * the prototype chain using __proto__ + * + * @param {Object|Array} target + * @param {Object} proto + */ + + function protoAugment(target, src) { + target.__proto__ = src; + } + + /** + * Augment an target Object or Array by defining + * hidden properties. + * + * @param {Object|Array} target + * @param {Object} proto + */ + + function copyAugment(target, src, keys) { + for (var i = 0, l = keys.length; i < l; i++) { + var key = keys[i]; + def(target, key, src[key]); + } + } + + /** + * Attempt to create an observer instance for a value, + * returns the new observer if successfully observed, + * or the existing observer if the value already has one. + * + * @param {*} value + * @param {Vue} [vm] + * @return {Observer|undefined} + * @static + */ + + function observe(value, vm) { + if (!value || typeof value !== 'object') { + return; + } + var ob; + if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) { + ob = value.__ob__; + } else if ((isArray(value) || isPlainObject(value)) && Object.isExtensible(value) && !value._isVue) { + ob = new Observer(value); + } + if (ob && vm) { + ob.addVm(vm); + } + return ob; + } + + /** + * Define a reactive property on an Object. + * + * @param {Object} obj + * @param {String} key + * @param {*} val + */ + + function defineReactive(obj, key, val) { + var dep = new Dep(); + + // cater for pre-defined getter/setters + var getter, setter; + if (config.convertAllProperties) { + var property = Object.getOwnPropertyDescriptor(obj, key); + if (property && property.configurable === false) { + return; + } + getter = property && property.get; + setter = property && property.set; + } + + var childOb = observe(val); + Object.defineProperty(obj, key, { + enumerable: true, + configurable: true, + get: function reactiveGetter() { + var value = getter ? getter.call(obj) : val; + if (Dep.target) { + dep.depend(); + if (childOb) { + childOb.dep.depend(); + } + if (isArray(value)) { + for (var e, i = 0, l = value.length; i < l; i++) { + e = value[i]; + e && e.__ob__ && e.__ob__.dep.depend(); + } + } + } + return value; + }, + set: function reactiveSetter(newVal) { + var value = getter ? getter.call(obj) : val; + if (newVal === value) { + return; + } + if (setter) { + setter.call(obj, newVal); + } else { + val = newVal; + } + childOb = observe(newVal); + dep.notify(); + } + }); + } + + var util = Object.freeze({ + defineReactive: defineReactive, + set: set, + del: del, + hasOwn: hasOwn, + isLiteral: isLiteral, + isReserved: isReserved, + _toString: _toString, + toNumber: toNumber, + toBoolean: toBoolean, + stripQuotes: stripQuotes, + camelize: camelize, + hyphenate: hyphenate, + classify: classify, + bind: bind$1, + toArray: toArray, + extend: extend, + isObject: isObject, + isPlainObject: isPlainObject, + def: def, + debounce: _debounce, + indexOf: indexOf, + cancellable: cancellable, + looseEqual: looseEqual, + isArray: isArray, + hasProto: hasProto, + inBrowser: inBrowser, + isIE9: isIE9, + isAndroid: isAndroid, + get transitionProp () { return transitionProp; }, + get transitionEndEvent () { return transitionEndEvent; }, + get animationProp () { return animationProp; }, + get animationEndEvent () { return animationEndEvent; }, + nextTick: nextTick, + query: query, + inDoc: inDoc, + getAttr: getAttr, + getBindAttr: getBindAttr, + hasBindAttr: hasBindAttr, + before: before, + after: after, + remove: remove, + prepend: prepend, + replace: replace, + on: on$1, + off: off, + setClass: setClass, + addClass: addClass, + removeClass: removeClass, + extractContent: extractContent, + trimNode: trimNode, + isTemplate: isTemplate, + createAnchor: createAnchor, + findRef: findRef, + mapNodeRange: mapNodeRange, + removeNodeRange: removeNodeRange, + mergeOptions: mergeOptions, + resolveAsset: resolveAsset, + assertAsset: assertAsset, + checkComponentAttr: checkComponentAttr, + initProp: initProp, + assertProp: assertProp, + coerceProp: coerceProp, + commonTagRE: commonTagRE, + reservedTagRE: reservedTagRE, + get warn () { return warn; } + }); + + var uid = 0; + + function initMixin (Vue) { + + /** + * The main init sequence. This is called for every + * instance, including ones that are created from extended + * constructors. + * + * @param {Object} options - this options object should be + * the result of merging class + * options and the options passed + * in to the constructor. + */ + + Vue.prototype._init = function (options) { + + options = options || {}; + + this.$el = null; + this.$parent = options.parent; + this.$root = this.$parent ? this.$parent.$root : this; + this.$children = []; + this.$refs = {}; // child vm references + this.$els = {}; // element references + this._watchers = []; // all watchers as an array + this._directives = []; // all directives + + // a uid + this._uid = uid++; + + // a flag to avoid this being observed + this._isVue = true; + + // events bookkeeping + this._events = {}; // registered callbacks + this._eventsCount = {}; // for $broadcast optimization + + // fragment instance properties + this._isFragment = false; + this._fragment = // @type {DocumentFragment} + this._fragmentStart = // @type {Text|Comment} + this._fragmentEnd = null; // @type {Text|Comment} + + // lifecycle state + this._isCompiled = this._isDestroyed = this._isReady = this._isAttached = this._isBeingDestroyed = false; + this._unlinkFn = null; + + // context: + // if this is a transcluded component, context + // will be the common parent vm of this instance + // and its host. + this._context = options._context || this.$parent; + + // scope: + // if this is inside an inline v-for, the scope + // will be the intermediate scope created for this + // repeat fragment. this is used for linking props + // and container directives. + this._scope = options._scope; + + // fragment: + // if this instance is compiled inside a Fragment, it + // needs to reigster itself as a child of that fragment + // for attach/detach to work properly. + this._frag = options._frag; + if (this._frag) { + this._frag.children.push(this); + } + + // push self into parent / transclusion host + if (this.$parent) { + this.$parent.$children.push(this); + } + + // merge options. + options = this.$options = mergeOptions(this.constructor.options, options, this); + + // set ref + this._updateRef(); + + // initialize data as empty object. + // it will be filled up in _initScope(). + this._data = {}; + + // call init hook + this._callHook('init'); + + // initialize data observation and scope inheritance. + this._initState(); + + // setup event system and option events. + this._initEvents(); + + // call created hook + this._callHook('created'); + + // if `el` option is passed, start compilation. + if (options.el) { + this.$mount(options.el); + } + }; + } + + var pathCache = new Cache(1000); + + // actions + var APPEND = 0; + var PUSH = 1; + var INC_SUB_PATH_DEPTH = 2; + var PUSH_SUB_PATH = 3; + + // states + var BEFORE_PATH = 0; + var IN_PATH = 1; + var BEFORE_IDENT = 2; + var IN_IDENT = 3; + var IN_SUB_PATH = 4; + var IN_SINGLE_QUOTE = 5; + var IN_DOUBLE_QUOTE = 6; + var AFTER_PATH = 7; + var ERROR = 8; + + var pathStateMachine = []; + + pathStateMachine[BEFORE_PATH] = { + 'ws': [BEFORE_PATH], + 'ident': [IN_IDENT, APPEND], + '[': [IN_SUB_PATH], + 'eof': [AFTER_PATH] + }; + + pathStateMachine[IN_PATH] = { + 'ws': [IN_PATH], + '.': [BEFORE_IDENT], + '[': [IN_SUB_PATH], + 'eof': [AFTER_PATH] + }; + + pathStateMachine[BEFORE_IDENT] = { + 'ws': [BEFORE_IDENT], + 'ident': [IN_IDENT, APPEND] + }; + + pathStateMachine[IN_IDENT] = { + 'ident': [IN_IDENT, APPEND], + '0': [IN_IDENT, APPEND], + 'number': [IN_IDENT, APPEND], + 'ws': [IN_PATH, PUSH], + '.': [BEFORE_IDENT, PUSH], + '[': [IN_SUB_PATH, PUSH], + 'eof': [AFTER_PATH, PUSH] + }; + + pathStateMachine[IN_SUB_PATH] = { + "'": [IN_SINGLE_QUOTE, APPEND], + '"': [IN_DOUBLE_QUOTE, APPEND], + '[': [IN_SUB_PATH, INC_SUB_PATH_DEPTH], + ']': [IN_PATH, PUSH_SUB_PATH], + 'eof': ERROR, + 'else': [IN_SUB_PATH, APPEND] + }; + + pathStateMachine[IN_SINGLE_QUOTE] = { + "'": [IN_SUB_PATH, APPEND], + 'eof': ERROR, + 'else': [IN_SINGLE_QUOTE, APPEND] + }; + + pathStateMachine[IN_DOUBLE_QUOTE] = { + '"': [IN_SUB_PATH, APPEND], + 'eof': ERROR, + 'else': [IN_DOUBLE_QUOTE, APPEND] + }; + + /** + * Determine the type of a character in a keypath. + * + * @param {Char} ch + * @return {String} type + */ + + function getPathCharType(ch) { + if (ch === undefined) { + return 'eof'; + } + + var code = ch.charCodeAt(0); + + switch (code) { + case 0x5B: // [ + case 0x5D: // ] + case 0x2E: // . + case 0x22: // " + case 0x27: // ' + case 0x30: + // 0 + return ch; + + case 0x5F: // _ + case 0x24: + // $ + return 'ident'; + + case 0x20: // Space + case 0x09: // Tab + case 0x0A: // Newline + case 0x0D: // Return + case 0xA0: // No-break space + case 0xFEFF: // Byte Order Mark + case 0x2028: // Line Separator + case 0x2029: + // Paragraph Separator + return 'ws'; + } + + // a-z, A-Z + if (code >= 0x61 && code <= 0x7A || code >= 0x41 && code <= 0x5A) { + return 'ident'; + } + + // 1-9 + if (code >= 0x31 && code <= 0x39) { + return 'number'; + } + + return 'else'; + } + + /** + * Format a subPath, return its plain form if it is + * a literal string or number. Otherwise prepend the + * dynamic indicator (*). + * + * @param {String} path + * @return {String} + */ + + function formatSubPath(path) { + var trimmed = path.trim(); + // invalid leading 0 + if (path.charAt(0) === '0' && isNaN(path)) { + return false; + } + return isLiteral(trimmed) ? stripQuotes(trimmed) : '*' + trimmed; + } + + /** + * Parse a string path into an array of segments + * + * @param {String} path + * @return {Array|undefined} + */ + + function parse(path) { + var keys = []; + var index = -1; + var mode = BEFORE_PATH; + var subPathDepth = 0; + var c, newChar, key, type, transition, action, typeMap; + + var actions = []; + + actions[PUSH] = function () { + if (key !== undefined) { + keys.push(key); + key = undefined; + } + }; + + actions[APPEND] = function () { + if (key === undefined) { + key = newChar; + } else { + key += newChar; + } + }; + + actions[INC_SUB_PATH_DEPTH] = function () { + actions[APPEND](); + subPathDepth++; + }; + + actions[PUSH_SUB_PATH] = function () { + if (subPathDepth > 0) { + subPathDepth--; + mode = IN_SUB_PATH; + actions[APPEND](); + } else { + subPathDepth = 0; + key = formatSubPath(key); + if (key === false) { + return false; + } else { + actions[PUSH](); + } + } + }; + + function maybeUnescapeQuote() { + var nextChar = path[index + 1]; + if (mode === IN_SINGLE_QUOTE && nextChar === "'" || mode === IN_DOUBLE_QUOTE && nextChar === '"') { + index++; + newChar = '\\' + nextChar; + actions[APPEND](); + return true; + } + } + + while (mode != null) { + index++; + c = path[index]; + + if (c === '\\' && maybeUnescapeQuote()) { + continue; + } + + type = getPathCharType(c); + typeMap = pathStateMachine[mode]; + transition = typeMap[type] || typeMap['else'] || ERROR; + + if (transition === ERROR) { + return; // parse error + } + + mode = transition[0]; + action = actions[transition[1]]; + if (action) { + newChar = transition[2]; + newChar = newChar === undefined ? c : newChar; + if (action() === false) { + return; + } + } + + if (mode === AFTER_PATH) { + keys.raw = path; + return keys; + } + } + } + + /** + * External parse that check for a cache hit first + * + * @param {String} path + * @return {Array|undefined} + */ + + function parsePath(path) { + var hit = pathCache.get(path); + if (!hit) { + hit = parse(path); + if (hit) { + pathCache.put(path, hit); + } + } + return hit; + } + + /** + * Get from an object from a path string + * + * @param {Object} obj + * @param {String} path + */ + + function getPath(obj, path) { + return parseExpression(path).get(obj); + } + + /** + * Warn against setting non-existent root path on a vm. + */ + + var warnNonExistent; + if ('development' !== 'production') { + warnNonExistent = function (path) { + 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.'); + }; + } + + /** + * Set on an object from a path + * + * @param {Object} obj + * @param {String | Array} path + * @param {*} val + */ + + function setPath(obj, path, val) { + var original = obj; + if (typeof path === 'string') { + path = parse(path); + } + if (!path || !isObject(obj)) { + return false; + } + var last, key; + for (var i = 0, l = path.length; i < l; i++) { + last = obj; + key = path[i]; + if (key.charAt(0) === '*') { + key = parseExpression(key.slice(1)).get.call(original, original); + } + if (i < l - 1) { + obj = obj[key]; + if (!isObject(obj)) { + obj = {}; + if ('development' !== 'production' && last._isVue) { + warnNonExistent(path); + } + set(last, key, obj); + } + } else { + if (isArray(obj)) { + obj.$set(key, val); + } else if (key in obj) { + obj[key] = val; + } else { + if ('development' !== 'production' && obj._isVue) { + warnNonExistent(path); + } + set(obj, key, val); + } + } + } + return true; + } + + var path = Object.freeze({ + parsePath: parsePath, + getPath: getPath, + setPath: setPath + }); + + var expressionCache = new Cache(1000); + + var allowedKeywords = 'Math,Date,this,true,false,null,undefined,Infinity,NaN,' + 'isNaN,isFinite,decodeURI,decodeURIComponent,encodeURI,' + 'encodeURIComponent,parseInt,parseFloat'; + var allowedKeywordsRE = new RegExp('^(' + allowedKeywords.replace(/,/g, '\\b|') + '\\b)'); + + // keywords that don't make sense inside expressions + 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,' + 'proctected,static,interface,private,public'; + var improperKeywordsRE = new RegExp('^(' + improperKeywords.replace(/,/g, '\\b|') + '\\b)'); + + var wsRE = /\s/g; + var newlineRE = /\n/g; + var saveRE = /[\{,]\s*[\w\$_]+\s*:|('(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")|new |typeof |void /g; + var restoreRE = /"(\d+)"/g; + var pathTestRE = /^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/; + var identRE = /[^\w$\.](?:[A-Za-z_$][\w$]*)/g; + var booleanLiteralRE = /^(?:true|false)$/; + + /** + * Save / Rewrite / Restore + * + * When rewriting paths found in an expression, it is + * possible for the same letter sequences to be found in + * strings and Object literal property keys. Therefore we + * remove and store these parts in a temporary array, and + * restore them after the path rewrite. + */ + + var saved = []; + + /** + * Save replacer + * + * The save regex can match two possible cases: + * 1. An opening object literal + * 2. A string + * If matched as a plain string, we need to escape its + * newlines, since the string needs to be preserved when + * generating the function body. + * + * @param {String} str + * @param {String} isString - str if matched as a string + * @return {String} - placeholder with index + */ + + function save(str, isString) { + var i = saved.length; + saved[i] = isString ? str.replace(newlineRE, '\\n') : str; + return '"' + i + '"'; + } + + /** + * Path rewrite replacer + * + * @param {String} raw + * @return {String} + */ + + function rewrite(raw) { + var c = raw.charAt(0); + var path = raw.slice(1); + if (allowedKeywordsRE.test(path)) { + return raw; + } else { + path = path.indexOf('"') > -1 ? path.replace(restoreRE, restore) : path; + return c + 'scope.' + path; + } + } + + /** + * Restore replacer + * + * @param {String} str + * @param {String} i - matched save index + * @return {String} + */ + + function restore(str, i) { + return saved[i]; + } + + /** + * Rewrite an expression, prefixing all path accessors with + * `scope.` and generate getter/setter functions. + * + * @param {String} exp + * @return {Function} + */ + + function compileGetter(exp) { + if (improperKeywordsRE.test(exp)) { + 'development' !== 'production' && warn('Avoid using reserved keywords in expression: ' + exp); + } + // reset state + saved.length = 0; + // save strings and object literal keys + var body = exp.replace(saveRE, save).replace(wsRE, ''); + // rewrite all paths + // pad 1 space here becaue the regex matches 1 extra char + body = (' ' + body).replace(identRE, rewrite).replace(restoreRE, restore); + return makeGetterFn(body); + } + + /** + * Build a getter function. Requires eval. + * + * We isolate the try/catch so it doesn't affect the + * optimization of the parse function when it is not called. + * + * @param {String} body + * @return {Function|undefined} + */ + + function makeGetterFn(body) { + try { + return new Function('scope', 'return ' + body + ';'); + } catch (e) { + 'development' !== 'production' && warn('Invalid expression. ' + 'Generated function body: ' + body); + } + } + + /** + * Compile a setter function for the expression. + * + * @param {String} exp + * @return {Function|undefined} + */ + + function compileSetter(exp) { + var path = parsePath(exp); + if (path) { + return function (scope, val) { + setPath(scope, path, val); + }; + } else { + 'development' !== 'production' && warn('Invalid setter expression: ' + exp); + } + } + + /** + * Parse an expression into re-written getter/setters. + * + * @param {String} exp + * @param {Boolean} needSet + * @return {Function} + */ + + function parseExpression(exp, needSet) { + exp = exp.trim(); + // try cache + var hit = expressionCache.get(exp); + if (hit) { + if (needSet && !hit.set) { + hit.set = compileSetter(hit.exp); + } + return hit; + } + var res = { exp: exp }; + res.get = isSimplePath(exp) && exp.indexOf('[') < 0 + // optimized super simple getter + ? makeGetterFn('scope.' + exp) + // dynamic getter + : compileGetter(exp); + if (needSet) { + res.set = compileSetter(exp); + } + expressionCache.put(exp, res); + return res; + } + + /** + * Check if an expression is a simple path. + * + * @param {String} exp + * @return {Boolean} + */ + + function isSimplePath(exp) { + return pathTestRE.test(exp) && + // don't treat true/false as paths + !booleanLiteralRE.test(exp) && + // Math constants e.g. Math.PI, Math.E etc. + exp.slice(0, 5) !== 'Math.'; + } + + var expression = Object.freeze({ + parseExpression: parseExpression, + isSimplePath: isSimplePath + }); + + // we have two separate queues: one for directive updates + // and one for user watcher registered via $watch(). + // we want to guarantee directive updates to be called + // before user watchers so that when user watchers are + // triggered, the DOM would have already been in updated + // state. + var queue = []; + var userQueue = []; + var has = {}; + var circular = {}; + var waiting = false; + var internalQueueDepleted = false; + + /** + * Reset the batcher's state. + */ + + function resetBatcherState() { + queue = []; + userQueue = []; + has = {}; + circular = {}; + waiting = internalQueueDepleted = false; + } + + /** + * Flush both queues and run the watchers. + */ + + function flushBatcherQueue() { + runBatcherQueue(queue); + internalQueueDepleted = true; + runBatcherQueue(userQueue); + // dev tool hook + /* istanbul ignore if */ + if ('development' !== 'production') { + if (inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__) { + window.__VUE_DEVTOOLS_GLOBAL_HOOK__.emit('flush'); + } + } + resetBatcherState(); + } + + /** + * Run the watchers in a single queue. + * + * @param {Array} queue + */ + + function runBatcherQueue(queue) { + // do not cache length because more watchers might be pushed + // as we run existing watchers + for (var i = 0; i < queue.length; i++) { + var watcher = queue[i]; + var id = watcher.id; + has[id] = null; + watcher.run(); + // in dev build, check and stop circular updates. + if ('development' !== 'production' && has[id] != null) { + circular[id] = (circular[id] || 0) + 1; + if (circular[id] > config._maxUpdateCount) { + queue.splice(has[id], 1); + warn('You may have an infinite update loop for watcher ' + 'with expression: ' + watcher.expression); + } + } + } + } + + /** + * Push a watcher into the watcher queue. + * Jobs with duplicate IDs will be skipped unless it's + * pushed when the queue is being flushed. + * + * @param {Watcher} watcher + * properties: + * - {Number} id + * - {Function} run + */ + + function pushWatcher(watcher) { + var id = watcher.id; + if (has[id] == null) { + // if an internal watcher is pushed, but the internal + // queue is already depleted, we run it immediately. + if (internalQueueDepleted && !watcher.user) { + watcher.run(); + return; + } + // push watcher into appropriate queue + var q = watcher.user ? userQueue : queue; + has[id] = q.length; + q.push(watcher); + // queue the flush + if (!waiting) { + waiting = true; + nextTick(flushBatcherQueue); + } + } + } + + var uid$2 = 0; + + /** + * A watcher parses an expression, collects dependencies, + * and fires callback when the expression value changes. + * This is used for both the $watch() api and directives. + * + * @param {Vue} vm + * @param {String} expression + * @param {Function} cb + * @param {Object} options + * - {Array} filters + * - {Boolean} twoWay + * - {Boolean} deep + * - {Boolean} user + * - {Boolean} sync + * - {Boolean} lazy + * - {Function} [preProcess] + * - {Function} [postProcess] + * @constructor + */ + function Watcher(vm, expOrFn, cb, options) { + // mix in options + if (options) { + extend(this, options); + } + var isFn = typeof expOrFn === 'function'; + this.vm = vm; + vm._watchers.push(this); + this.expression = isFn ? expOrFn.toString() : expOrFn; + this.cb = cb; + this.id = ++uid$2; // uid for batching + this.active = true; + this.dirty = this.lazy; // for lazy watchers + this.deps = Object.create(null); + this.newDeps = null; + this.prevError = null; // for async error stacks + // parse expression for getter/setter + if (isFn) { + this.getter = expOrFn; + this.setter = undefined; + } else { + var res = parseExpression(expOrFn, this.twoWay); + this.getter = res.get; + this.setter = res.set; + } + this.value = this.lazy ? undefined : this.get(); + // state for avoiding false triggers for deep and Array + // watchers during vm._digest() + this.queued = this.shallow = false; + } + + /** + * Add a dependency to this directive. + * + * @param {Dep} dep + */ + + Watcher.prototype.addDep = function (dep) { + var id = dep.id; + if (!this.newDeps[id]) { + this.newDeps[id] = dep; + if (!this.deps[id]) { + this.deps[id] = dep; + dep.addSub(this); + } + } + }; + + /** + * Evaluate the getter, and re-collect dependencies. + */ + + Watcher.prototype.get = function () { + this.beforeGet(); + var scope = this.scope || this.vm; + var value; + try { + value = this.getter.call(scope, scope); + } catch (e) { + if ('development' !== 'production' && config.warnExpressionErrors) { + warn('Error when evaluating expression "' + this.expression + '". ' + (config.debug ? '' : 'Turn on debug mode to see stack trace.'), e); + } + } + // "touch" every property so they are all tracked as + // dependencies for deep watching + if (this.deep) { + traverse(value); + } + if (this.preProcess) { + value = this.preProcess(value); + } + if (this.filters) { + value = scope._applyFilters(value, null, this.filters, false); + } + if (this.postProcess) { + value = this.postProcess(value); + } + this.afterGet(); + return value; + }; + + /** + * Set the corresponding value with the setter. + * + * @param {*} value + */ + + Watcher.prototype.set = function (value) { + var scope = this.scope || this.vm; + if (this.filters) { + value = scope._applyFilters(value, this.value, this.filters, true); + } + try { + this.setter.call(scope, scope, value); + } catch (e) { + if ('development' !== 'production' && config.warnExpressionErrors) { + warn('Error when evaluating setter "' + this.expression + '"', e); + } + } + // two-way sync for v-for alias + var forContext = scope.$forContext; + if (forContext && forContext.alias === this.expression) { + if (forContext.filters) { + '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.'); + return; + } + forContext._withLock(function () { + if (scope.$key) { + // original is an object + forContext.rawValue[scope.$key] = value; + } else { + forContext.rawValue.$set(scope.$index, value); + } + }); + } + }; + + /** + * Prepare for dependency collection. + */ + + Watcher.prototype.beforeGet = function () { + Dep.target = this; + this.newDeps = Object.create(null); + }; + + /** + * Clean up for dependency collection. + */ + + Watcher.prototype.afterGet = function () { + Dep.target = null; + var ids = Object.keys(this.deps); + var i = ids.length; + while (i--) { + var id = ids[i]; + if (!this.newDeps[id]) { + this.deps[id].removeSub(this); + } + } + this.deps = this.newDeps; + }; + + /** + * Subscriber interface. + * Will be called when a dependency changes. + * + * @param {Boolean} shallow + */ + + Watcher.prototype.update = function (shallow) { + if (this.lazy) { + this.dirty = true; + } else if (this.sync || !config.async) { + this.run(); + } else { + // if queued, only overwrite shallow with non-shallow, + // but not the other way around. + this.shallow = this.queued ? shallow ? this.shallow : false : !!shallow; + this.queued = true; + // record before-push error stack in debug mode + /* istanbul ignore if */ + if ('development' !== 'production' && config.debug) { + this.prevError = new Error('[vue] async stack trace'); + } + pushWatcher(this); + } + }; + + /** + * Batcher job interface. + * Will be called by the batcher. + */ + + Watcher.prototype.run = function () { + if (this.active) { + var value = this.get(); + if (value !== this.value || + // Deep watchers and watchers on Object/Arrays should fire even + // when the value is the same, because the value may + // have mutated; but only do so if this is a + // non-shallow update (caused by a vm digest). + (isObject(value) || this.deep) && !this.shallow) { + // set new value + var oldValue = this.value; + this.value = value; + // in debug + async mode, when a watcher callbacks + // throws, we also throw the saved before-push error + // so the full cross-tick stack trace is available. + var prevError = this.prevError; + /* istanbul ignore if */ + if ('development' !== 'production' && config.debug && prevError) { + this.prevError = null; + try { + this.cb.call(this.vm, value, oldValue); + } catch (e) { + nextTick(function () { + throw prevError; + }, 0); + throw e; + } + } else { + this.cb.call(this.vm, value, oldValue); + } + } + this.queued = this.shallow = false; + } + }; + + /** + * Evaluate the value of the watcher. + * This only gets called for lazy watchers. + */ + + Watcher.prototype.evaluate = function () { + // avoid overwriting another watcher that is being + // collected. + var current = Dep.target; + this.value = this.get(); + this.dirty = false; + Dep.target = current; + }; + + /** + * Depend on all deps collected by this watcher. + */ + + Watcher.prototype.depend = function () { + var depIds = Object.keys(this.deps); + var i = depIds.length; + while (i--) { + this.deps[depIds[i]].depend(); + } + }; + + /** + * Remove self from all dependencies' subcriber list. + */ + + Watcher.prototype.teardown = function () { + if (this.active) { + // remove self from vm's watcher list + // we can skip this if the vm if being destroyed + // which can improve teardown performance. + if (!this.vm._isBeingDestroyed) { + this.vm._watchers.$remove(this); + } + var depIds = Object.keys(this.deps); + var i = depIds.length; + while (i--) { + this.deps[depIds[i]].removeSub(this); + } + this.active = false; + this.vm = this.cb = this.value = null; + } + }; + + /** + * Recrusively traverse an object to evoke all converted + * getters, so that every nested property inside the object + * is collected as a "deep" dependency. + * + * @param {*} val + */ + + function traverse(val) { + var i, keys; + if (isArray(val)) { + i = val.length; + while (i--) traverse(val[i]); + } else if (isObject(val)) { + keys = Object.keys(val); + i = keys.length; + while (i--) traverse(val[keys[i]]); + } + } + + var cloak = { + bind: function bind() { + var el = this.el; + this.vm.$once('pre-hook:compiled', function () { + el.removeAttribute('v-cloak'); + }); + } + }; + + var ref = { + bind: function bind() { + 'development' !== 'production' && warn('v-ref:' + this.arg + ' must be used on a child ' + 'component. Found on <' + this.el.tagName.toLowerCase() + '>.'); + } + }; + + var ON = 700; + var MODEL = 800; + var BIND = 850; + var TRANSITION = 1100; + var EL = 1500; + var COMPONENT = 1500; + var PARTIAL = 1750; + var SLOT = 1750; + var FOR = 2000; + var IF = 2000; + + var el = { + + priority: EL, + + bind: function bind() { + /* istanbul ignore if */ + if (!this.arg) { + return; + } + var id = this.id = camelize(this.arg); + var refs = (this._scope || this.vm).$els; + if (hasOwn(refs, id)) { + refs[id] = this.el; + } else { + defineReactive(refs, id, this.el); + } + }, + + unbind: function unbind() { + var refs = (this._scope || this.vm).$els; + if (refs[this.id] === this.el) { + refs[this.id] = null; + } + } + }; + + var prefixes = ['-webkit-', '-moz-', '-ms-']; + var camelPrefixes = ['Webkit', 'Moz', 'ms']; + var importantRE = /!important;?$/; + var propCache = Object.create(null); + + var testEl = null; + + var style = { + + deep: true, + + update: function update(value) { + if (typeof value === 'string') { + this.el.style.cssText = value; + } else if (isArray(value)) { + this.handleObject(value.reduce(extend, {})); + } else { + this.handleObject(value || {}); + } + }, + + handleObject: function handleObject(value) { + // cache object styles so that only changed props + // are actually updated. + var cache = this.cache || (this.cache = {}); + var name, val; + for (name in cache) { + if (!(name in value)) { + this.handleSingle(name, null); + delete cache[name]; + } + } + for (name in value) { + val = value[name]; + if (val !== cache[name]) { + cache[name] = val; + this.handleSingle(name, val); + } + } + }, + + handleSingle: function handleSingle(prop, value) { + prop = normalize(prop); + if (!prop) return; // unsupported prop + // cast possible numbers/booleans into strings + if (value != null) value += ''; + if (value) { + var isImportant = importantRE.test(value) ? 'important' : ''; + if (isImportant) { + value = value.replace(importantRE, '').trim(); + } + this.el.style.setProperty(prop, value, isImportant); + } else { + this.el.style.removeProperty(prop); + } + } + + }; + + /** + * Normalize a CSS property name. + * - cache result + * - auto prefix + * - camelCase -> dash-case + * + * @param {String} prop + * @return {String} + */ + + function normalize(prop) { + if (propCache[prop]) { + return propCache[prop]; + } + var res = prefix(prop); + propCache[prop] = propCache[res] = res; + return res; + } + + /** + * Auto detect the appropriate prefix for a CSS property. + * https://gist.github.com/paulirish/523692 + * + * @param {String} prop + * @return {String} + */ + + function prefix(prop) { + prop = hyphenate(prop); + var camel = camelize(prop); + var upper = camel.charAt(0).toUpperCase() + camel.slice(1); + if (!testEl) { + testEl = document.createElement('div'); + } + if (camel in testEl.style) { + return prop; + } + var i = prefixes.length; + var prefixed; + while (i--) { + prefixed = camelPrefixes[i] + upper; + if (prefixed in testEl.style) { + return prefixes[i] + prop; + } + } + } + + // xlink + var xlinkNS = 'http://www.w3.org/1999/xlink'; + var xlinkRE = /^xlink:/; + + // check for attributes that prohibit interpolations + var disallowedInterpAttrRE = /^v-|^:|^@|^(is|transition|transition-mode|debounce|track-by|stagger|enter-stagger|leave-stagger)$/; + + // these attributes should also set their corresponding properties + // because they only affect the initial state of the element + var attrWithPropsRE = /^(value|checked|selected|muted)$/; + + // these attributes should set a hidden property for + // binding v-model to object values + var modelProps = { + value: '_value', + 'true-value': '_trueValue', + 'false-value': '_falseValue' + }; + + var bind = { + + priority: BIND, + + bind: function bind() { + var attr = this.arg; + var tag = this.el.tagName; + // should be deep watch on object mode + if (!attr) { + this.deep = true; + } + // handle interpolation bindings + var descriptor = this.descriptor; + var tokens = descriptor.interp; + if (tokens) { + // handle interpolations with one-time tokens + if (descriptor.hasOneTime) { + this.expression = tokensToExp(tokens, this._scope || this.vm); + } + + // only allow binding on native attributes + if (disallowedInterpAttrRE.test(attr) || attr === 'name' && (tag === 'PARTIAL' || tag === 'SLOT')) { + 'development' !== 'production' && warn(attr + '="' + descriptor.raw + '": ' + 'attribute interpolation is not allowed in Vue.js ' + 'directives and special attributes.'); + this.el.removeAttribute(attr); + this.invalid = true; + } + + /* istanbul ignore if */ + if ('development' !== 'production') { + var raw = attr + '="' + descriptor.raw + '": '; + // warn src + if (attr === 'src') { + warn(raw + 'interpolation in "src" attribute will cause ' + 'a 404 request. Use v-bind:src instead.'); + } + + // warn style + if (attr === 'style') { + warn(raw + 'interpolation in "style" attribute will cause ' + 'the attribute to be discarded in Internet Explorer. ' + 'Use v-bind:style instead.'); + } + } + } + }, + + update: function update(value) { + if (this.invalid) { + return; + } + var attr = this.arg; + if (this.arg) { + this.handleSingle(attr, value); + } else { + this.handleObject(value || {}); + } + }, + + // share object handler with v-bind:class + handleObject: style.handleObject, + + handleSingle: function handleSingle(attr, value) { + var el = this.el; + var interp = this.descriptor.interp; + if (!interp && attrWithPropsRE.test(attr) && attr in el) { + el[attr] = attr === 'value' ? value == null // IE9 will set input.value to "null" for null... + ? '' : value : value; + } + // set model props + var modelProp = modelProps[attr]; + if (!interp && modelProp) { + el[modelProp] = value; + // update v-model if present + var model = el.__v_model; + if (model) { + model.listener(); + } + } + // do not set value attribute for textarea + if (attr === 'value' && el.tagName === 'TEXTAREA') { + el.removeAttribute(attr); + return; + } + // update attribute + if (value != null && value !== false) { + if (attr === 'class') { + // handle edge case #1960: + // class interpolation should not overwrite Vue transition class + if (el.__v_trans) { + value += ' ' + el.__v_trans.id + '-transition'; + } + setClass(el, value); + } else if (xlinkRE.test(attr)) { + el.setAttributeNS(xlinkNS, attr, value); + } else { + el.setAttribute(attr, value); + } + } else { + el.removeAttribute(attr); + } + } + }; + + // keyCode aliases + var keyCodes = { + esc: 27, + tab: 9, + enter: 13, + space: 32, + 'delete': 46, + up: 38, + left: 37, + right: 39, + down: 40 + }; + + function keyFilter(handler, keys) { + var codes = keys.map(function (key) { + var charCode = key.charCodeAt(0); + if (charCode > 47 && charCode < 58) { + return parseInt(key, 10); + } + if (key.length === 1) { + charCode = key.toUpperCase().charCodeAt(0); + if (charCode > 64 && charCode < 91) { + return charCode; + } + } + return keyCodes[key]; + }); + return function keyHandler(e) { + if (codes.indexOf(e.keyCode) > -1) { + return handler.call(this, e); + } + }; + } + + function stopFilter(handler) { + return function stopHandler(e) { + e.stopPropagation(); + return handler.call(this, e); + }; + } + + function preventFilter(handler) { + return function preventHandler(e) { + e.preventDefault(); + return handler.call(this, e); + }; + } + + var on = { + + acceptStatement: true, + priority: ON, + + bind: function bind() { + // deal with iframes + if (this.el.tagName === 'IFRAME' && this.arg !== 'load') { + var self = this; + this.iframeBind = function () { + on$1(self.el.contentWindow, self.arg, self.handler); + }; + this.on('load', this.iframeBind); + } + }, + + update: function update(handler) { + // stub a noop for v-on with no value, + // e.g. @mousedown.prevent + if (!this.descriptor.raw) { + handler = function () {}; + } + + if (typeof handler !== 'function') { + 'development' !== 'production' && warn('v-on:' + this.arg + '="' + this.expression + '" expects a function value, ' + 'got ' + handler); + return; + } + + // apply modifiers + if (this.modifiers.stop) { + handler = stopFilter(handler); + } + if (this.modifiers.prevent) { + handler = preventFilter(handler); + } + // key filter + var keys = Object.keys(this.modifiers).filter(function (key) { + return key !== 'stop' && key !== 'prevent'; + }); + if (keys.length) { + handler = keyFilter(handler, keys); + } + + this.reset(); + this.handler = handler; + + if (this.iframeBind) { + this.iframeBind(); + } else { + on$1(this.el, this.arg, this.handler); + } + }, + + reset: function reset() { + var el = this.iframeBind ? this.el.contentWindow : this.el; + if (this.handler) { + off(el, this.arg, this.handler); + } + }, + + unbind: function unbind() { + this.reset(); + } + }; + + var checkbox = { + + bind: function bind() { + var self = this; + var el = this.el; + + this.getValue = function () { + return el.hasOwnProperty('_value') ? el._value : self.params.number ? toNumber(el.value) : el.value; + }; + + function getBooleanValue() { + var val = el.checked; + if (val && el.hasOwnProperty('_trueValue')) { + return el._trueValue; + } + if (!val && el.hasOwnProperty('_falseValue')) { + return el._falseValue; + } + return val; + } + + this.listener = function () { + var model = self._watcher.value; + if (isArray(model)) { + var val = self.getValue(); + if (el.checked) { + if (indexOf(model, val) < 0) { + model.push(val); + } + } else { + model.$remove(val); + } + } else { + self.set(getBooleanValue()); + } + }; + + this.on('change', this.listener); + if (el.hasAttribute('checked')) { + this.afterBind = this.listener; + } + }, + + update: function update(value) { + var el = this.el; + if (isArray(value)) { + el.checked = indexOf(value, this.getValue()) > -1; + } else { + if (el.hasOwnProperty('_trueValue')) { + el.checked = looseEqual(value, el._trueValue); + } else { + el.checked = !!value; + } + } + } + }; + + var select = { + + bind: function bind() { + var self = this; + var el = this.el; + + // method to force update DOM using latest value. + this.forceUpdate = function () { + if (self._watcher) { + self.update(self._watcher.get()); + } + }; + + // check if this is a multiple select + var multiple = this.multiple = el.hasAttribute('multiple'); + + // attach listener + this.listener = function () { + var value = getValue(el, multiple); + value = self.params.number ? isArray(value) ? value.map(toNumber) : toNumber(value) : value; + self.set(value); + }; + this.on('change', this.listener); + + // if has initial value, set afterBind + var initValue = getValue(el, multiple, true); + if (multiple && initValue.length || !multiple && initValue !== null) { + this.afterBind = this.listener; + } + + // All major browsers except Firefox resets + // selectedIndex with value -1 to 0 when the element + // is appended to a new parent, therefore we have to + // force a DOM update whenever that happens... + this.vm.$on('hook:attached', this.forceUpdate); + }, + + update: function update(value) { + var el = this.el; + el.selectedIndex = -1; + var multi = this.multiple && isArray(value); + var options = el.options; + var i = options.length; + var op, val; + while (i--) { + op = options[i]; + val = op.hasOwnProperty('_value') ? op._value : op.value; + /* eslint-disable eqeqeq */ + op.selected = multi ? indexOf$1(value, val) > -1 : looseEqual(value, val); + /* eslint-enable eqeqeq */ + } + }, + + unbind: function unbind() { + /* istanbul ignore next */ + this.vm.$off('hook:attached', this.forceUpdate); + } + }; + + /** + * Get select value + * + * @param {SelectElement} el + * @param {Boolean} multi + * @param {Boolean} init + * @return {Array|*} + */ + + function getValue(el, multi, init) { + var res = multi ? [] : null; + var op, val, selected; + for (var i = 0, l = el.options.length; i < l; i++) { + op = el.options[i]; + selected = init ? op.hasAttribute('selected') : op.selected; + if (selected) { + val = op.hasOwnProperty('_value') ? op._value : op.value; + if (multi) { + res.push(val); + } else { + return val; + } + } + } + return res; + } + + /** + * Native Array.indexOf uses strict equal, but in this + * case we need to match string/numbers with custom equal. + * + * @param {Array} arr + * @param {*} val + */ + + function indexOf$1(arr, val) { + var i = arr.length; + while (i--) { + if (looseEqual(arr[i], val)) { + return i; + } + } + return -1; + } + + var radio = { + + bind: function bind() { + var self = this; + var el = this.el; + + this.getValue = function () { + // value overwrite via v-bind:value + if (el.hasOwnProperty('_value')) { + return el._value; + } + var val = el.value; + if (self.params.number) { + val = toNumber(val); + } + return val; + }; + + this.listener = function () { + self.set(self.getValue()); + }; + this.on('change', this.listener); + + if (el.hasAttribute('checked')) { + this.afterBind = this.listener; + } + }, + + update: function update(value) { + this.el.checked = looseEqual(value, this.getValue()); + } + }; + + var text$2 = { + + bind: function bind() { + var self = this; + var el = this.el; + var isRange = el.type === 'range'; + var lazy = this.params.lazy; + var number = this.params.number; + var debounce = this.params.debounce; + + // handle composition events. + // http://blog.evanyou.me/2014/01/03/composition-event/ + // skip this for Android because it handles composition + // events quite differently. Android doesn't trigger + // composition events for language input methods e.g. + // Chinese, but instead triggers them for spelling + // suggestions... (see Discussion/#162) + var composing = false; + if (!isAndroid && !isRange) { + this.on('compositionstart', function () { + composing = true; + }); + this.on('compositionend', function () { + composing = false; + // in IE11 the "compositionend" event fires AFTER + // the "input" event, so the input handler is blocked + // at the end... have to call it here. + // + // #1327: in lazy mode this is unecessary. + if (!lazy) { + self.listener(); + } + }); + } + + // prevent messing with the input when user is typing, + // and force update on blur. + this.focused = false; + if (!isRange && !lazy) { + this.on('focus', function () { + self.focused = true; + }); + this.on('blur', function () { + self.focused = false; + // do not sync value after fragment removal (#2017) + if (!self._frag || self._frag.inserted) { + self.rawListener(); + } + }); + } + + // Now attach the main listener + this.listener = this.rawListener = function () { + if (composing || !self._bound) { + return; + } + var val = number || isRange ? toNumber(el.value) : el.value; + self.set(val); + // force update on next tick to avoid lock & same value + // also only update when user is not typing + nextTick(function () { + if (self._bound && !self.focused) { + self.update(self._watcher.value); + } + }); + }; + + // apply debounce + if (debounce) { + this.listener = _debounce(this.listener, debounce); + } + + // Support jQuery events, since jQuery.trigger() doesn't + // trigger native events in some cases and some plugins + // rely on $.trigger() + // + // We want to make sure if a listener is attached using + // jQuery, it is also removed with jQuery, that's why + // we do the check for each directive instance and + // store that check result on itself. This also allows + // easier test coverage control by unsetting the global + // jQuery variable in tests. + this.hasjQuery = typeof jQuery === 'function'; + if (this.hasjQuery) { + jQuery(el).on('change', this.listener); + if (!lazy) { + jQuery(el).on('input', this.listener); + } + } else { + this.on('change', this.listener); + if (!lazy) { + this.on('input', this.listener); + } + } + + // IE9 doesn't fire input event on backspace/del/cut + if (!lazy && isIE9) { + this.on('cut', function () { + nextTick(self.listener); + }); + this.on('keyup', function (e) { + if (e.keyCode === 46 || e.keyCode === 8) { + self.listener(); + } + }); + } + + // set initial value if present + if (el.hasAttribute('value') || el.tagName === 'TEXTAREA' && el.value.trim()) { + this.afterBind = this.listener; + } + }, + + update: function update(value) { + this.el.value = _toString(value); + }, + + unbind: function unbind() { + var el = this.el; + if (this.hasjQuery) { + jQuery(el).off('change', this.listener); + jQuery(el).off('input', this.listener); + } + } + }; + + var handlers = { + text: text$2, + radio: radio, + select: select, + checkbox: checkbox + }; + + var model = { + + priority: MODEL, + twoWay: true, + handlers: handlers, + params: ['lazy', 'number', 'debounce'], + + /** + * Possible elements: + *