From a06cd65e1c72beea48485a2a5a91c579c0752fa7 Mon Sep 17 00:00:00 2001 From: Evan You Date: Thu, 5 Feb 2015 16:26:28 -0500 Subject: [PATCH] [release] 0.11.5 --- bower.json | 2 +- component.json | 2 +- dist/vue.js | 11019 ++++++++++++++++++----------------- dist/vue.min.js | 8 +- package.json | 2 +- src/compiler/transclude.js | 3 + 6 files changed, 5590 insertions(+), 5446 deletions(-) diff --git a/bower.json b/bower.json index 2738ab0831c..325b2af388c 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "vue", - "version": "0.11.4", + "version": "0.11.5", "main": "dist/vue.js", "description": "Simple, Fast & Composable MVVM for building interative interfaces", "authors": ["Evan You "], diff --git a/component.json b/component.json index fe4f129d5af..1deb2ffce6a 100644 --- a/component.json +++ b/component.json @@ -1,6 +1,6 @@ { "name": "vue", - "version": "0.11.4", + "version": "0.11.5", "main": "src/vue.js", "author": "Evan You ", "description": "Simple, Fast & Composable MVVM for building interative interfaces", diff --git a/dist/vue.js b/dist/vue.js index 06ba8bfe7b5..e84fdc91b0b 100644 --- a/dist/vue.js +++ b/dist/vue.js @@ -1,6 +1,6 @@ /** - * Vue.js v0.11.4 - * (c) 2014 Evan You + * Vue.js v0.11.5 + * (c) 2015 Evan You * Released under the MIT License. */ @@ -60,7 +60,7 @@ return /******/ (function(modules) { // webpackBootstrap /* 0 */ /***/ function(module, exports, __webpack_require__) { - var _ = __webpack_require__(1) + var _ = __webpack_require__(11) var extend = _.extend /** @@ -85,7 +85,7 @@ return /******/ (function(modules) { // webpackBootstrap * Mixin global API */ - extend(Vue, __webpack_require__(2)) + extend(Vue, __webpack_require__(1)) /** * Vue and every constructor that extends Vue has an @@ -97,8 +97,8 @@ return /******/ (function(modules) { // webpackBootstrap */ Vue.options = { - directives : __webpack_require__(8), - filters : __webpack_require__(9), + directives : __webpack_require__(12), + filters : __webpack_require__(13), partials : {}, transitions : {}, components : {} @@ -128,20 +128,20 @@ return /******/ (function(modules) { // webpackBootstrap * Mixin internal instance methods */ - extend(p, __webpack_require__(10)) - extend(p, __webpack_require__(11)) - extend(p, __webpack_require__(12)) - extend(p, __webpack_require__(13)) + extend(p, __webpack_require__(2)) + extend(p, __webpack_require__(3)) + extend(p, __webpack_require__(4)) + extend(p, __webpack_require__(5)) /** * Mixin public API methods */ - extend(p, __webpack_require__(3)) - extend(p, __webpack_require__(4)) - extend(p, __webpack_require__(5)) extend(p, __webpack_require__(6)) extend(p, __webpack_require__(7)) + extend(p, __webpack_require__(8)) + extend(p, __webpack_require__(9)) + extend(p, __webpack_require__(10)) module.exports = _.Vue = Vue @@ -149,21 +149,8 @@ return /******/ (function(modules) { // webpackBootstrap /* 1 */ /***/ function(module, exports, __webpack_require__) { - var lang = __webpack_require__(14) - var extend = lang.extend - - extend(exports, lang) - extend(exports, __webpack_require__(15)) - extend(exports, __webpack_require__(16)) - extend(exports, __webpack_require__(17)) - extend(exports, __webpack_require__(18)) - -/***/ }, -/* 2 */ -/***/ function(module, exports, __webpack_require__) { - - var _ = __webpack_require__(1) - var mergeOptions = __webpack_require__(19) + var _ = __webpack_require__(11) + var mergeOptions = __webpack_require__(14) /** * Expose useful internals @@ -171,19 +158,19 @@ return /******/ (function(modules) { // webpackBootstrap exports.util = _ exports.nextTick = _.nextTick - exports.config = __webpack_require__(20) + exports.config = __webpack_require__(15) exports.compiler = { - compile: __webpack_require__(42), - transclude: __webpack_require__(43) + compile: __webpack_require__(16), + transclude: __webpack_require__(17) } exports.parsers = { - path: __webpack_require__(44), - text: __webpack_require__(45), - template: __webpack_require__(46), - directive: __webpack_require__(47), - expression: __webpack_require__(48) + path: __webpack_require__(18), + text: __webpack_require__(19), + template: __webpack_require__(20), + directive: __webpack_require__(21), + expression: __webpack_require__(22) } /** @@ -310,6246 +297,6621 @@ return /******/ (function(modules) { // webpackBootstrap createAssetRegisters(exports) /***/ }, -/* 3 */ +/* 2 */ /***/ function(module, exports, __webpack_require__) { - var _ = __webpack_require__(1) - var Watcher = __webpack_require__(21) - var Path = __webpack_require__(44) - var textParser = __webpack_require__(45) - var dirParser = __webpack_require__(47) - var expParser = __webpack_require__(48) - var filterRE = /[^|]\|[^|]/ + var mergeOptions = __webpack_require__(14) /** - * Get the value from an expression on this vm. + * The main init sequence. This is called for every + * instance, including ones that are created from extended + * constructors. * - * @param {String} exp - * @return {*} + * @param {Object} options - this options object should be + * the result of merging class + * options and the options passed + * in to the constructor. */ - exports.$get = function (exp) { - var res = expParser.parse(exp) - if (res) { - return res.get.call(this, this) + exports._init = function (options) { + + options = options || {} + + this.$el = null + this.$parent = options._parent + this.$root = options._root || this + this.$ = {} // child vm references + this.$$ = {} // element references + this._watcherList = [] // all watchers as an array + this._watchers = {} // internal watchers as a hash + this._userWatchers = {} // user watchers as a hash + this._directives = [] // all directives + + // a flag to avoid this being observed + this._isVue = true + + // events bookkeeping + this._events = {} // registered callbacks + this._eventsCount = {} // for $broadcast optimization + this._eventCancelled = false // for event cancellation + + // block instance properties + this._isBlock = false + this._blockStart = // @type {CommentNode} + this._blockEnd = null // @type {CommentNode} + + // lifecycle state + this._isCompiled = + this._isDestroyed = + this._isReady = + this._isAttached = + this._isBeingDestroyed = false + + // children + this._children = [] + this._childCtors = {} + // transcluded components that belong to the parent + this._transCpnts = null + + // merge options. + options = this.$options = mergeOptions( + this.constructor.options, + options, + this + ) + + // set data after merge. + this._data = options.data || {} + + // initialize data observation and scope inheritance. + this._initScope() + + // 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) } } +/***/ }, +/* 3 */ +/***/ function(module, exports, __webpack_require__) { + + var _ = __webpack_require__(11) + var inDoc = _.inDoc + + /** + * Setup the instance's option events & watchers. + * If the value is a string, we pull it from the + * instance's methods by name. + */ + + exports._initEvents = function () { + var options = this.$options + registerCallbacks(this, '$on', options.events) + registerCallbacks(this, '$watch', options.watch) + } + /** - * Set the value from an expression on this vm. - * The expression must be a valid left-hand - * expression in an assignment. + * Register callbacks for option events and watchers. * - * @param {String} exp - * @param {*} val + * @param {Vue} vm + * @param {String} action + * @param {Object} hash */ - exports.$set = function (exp, val) { - var res = expParser.parse(exp, true) - if (res && res.set) { - res.set.call(this, this, val) + function registerCallbacks (vm, action, hash) { + if (!hash) return + var handlers, key, i, j + for (key in hash) { + handlers = hash[key] + if (_.isArray(handlers)) { + for (i = 0, j = handlers.length; i < j; i++) { + register(vm, action, key, handlers[i]) + } + } else { + register(vm, action, key, handlers) + } } } /** - * Add a property on the VM + * Helper to register an event/watch callback. * + * @param {Vue} vm + * @param {String} action * @param {String} key - * @param {*} val + * @param {*} handler */ - exports.$add = function (key, val) { - this._data.$add(key, val) + function register (vm, action, key, handler) { + var type = typeof handler + if (type === 'function') { + vm[action](key, handler) + } else if (type === 'string') { + var methods = vm.$options.methods + var method = methods && methods[handler] + if (method) { + vm[action](key, method) + } else { + _.warn( + 'Unknown method: "' + handler + '" when ' + + 'registering callback for ' + action + + ': "' + key + '".' + ) + } + } } /** - * Delete a property on the VM - * - * @param {String} key + * Setup recursive attached/detached calls */ - exports.$delete = function (key) { - this._data.$delete(key) + exports._initDOMHooks = function () { + this.$on('hook:attached', onAttached) + this.$on('hook:detached', onDetached) } /** - * Watch an expression, trigger callback when its - * value changes. - * - * @param {String} exp - * @param {Function} cb - * @param {Boolean} [deep] - * @param {Boolean} [immediate] - * @return {Function} - unwatchFn + * Callback to recursively call attached hook on children */ - exports.$watch = function (exp, cb, deep, immediate) { - var vm = this - var key = deep ? exp + '**deep**' : exp - var watcher = vm._userWatchers[key] - var wrappedCb = function (val, oldVal) { - cb.call(vm, val, oldVal) - } - if (!watcher) { - watcher = vm._userWatchers[key] = - new Watcher(vm, exp, wrappedCb, { - deep: deep, - user: true - }) - } else { - watcher.addCb(wrappedCb) - } - if (immediate) { - wrappedCb(watcher.value) - } - return function unwatchFn () { - watcher.removeCb(wrappedCb) - if (!watcher.active) { - vm._userWatchers[key] = null - } + function onAttached () { + this._isAttached = true + this._children.forEach(callAttach) + if (this._transCpnts) { + this._transCpnts.forEach(callAttach) } } /** - * Evaluate a text directive, including filters. - * - * @param {String} text - * @return {String} + * Iterator to call attached hook + * + * @param {Vue} child */ - exports.$eval = function (text) { - // check for filters. - if (filterRE.test(text)) { - var dir = dirParser.parse(text)[0] - // the filter regex check might give false positive - // for pipes inside strings, so it's possible that - // we don't get any filters here - return dir.filters - ? _.applyFilters( - this.$get(dir.expression), - _.resolveFilters(this, dir.filters).read, - this - ) - : this.$get(dir.expression) - } else { - // no filter - return this.$get(text) + function callAttach (child) { + if (!child._isAttached && inDoc(child.$el)) { + child._callHook('attached') } } /** - * Interpolate a piece of template text. - * - * @param {String} text - * @return {String} + * Callback to recursively call detached hook on children */ - exports.$interpolate = function (text) { - var tokens = textParser.parse(text) - var vm = this - if (tokens) { - return tokens.length === 1 - ? vm.$eval(tokens[0].value) - : tokens.map(function (token) { - return token.tag - ? vm.$eval(token.value) - : token.value - }).join('') - } else { - return text + function onDetached () { + this._isAttached = false + this._children.forEach(callDetach) + if (this._transCpnts) { + this._transCpnts.forEach(callDetach) } } /** - * Log instance data as a plain JS object - * so that it is easier to inspect in console. - * This method assumes console is available. - * - * @param {String} [path] + * Iterator to call detached hook + * + * @param {Vue} child */ - exports.$log = function (path) { - var data = path - ? Path.get(this._data, path) - : this._data - if (data) { - data = JSON.parse(JSON.stringify(data)) + function callDetach (child) { + if (child._isAttached && !inDoc(child.$el)) { + child._callHook('detached') } - console.log(data) + } + + /** + * Trigger all handlers for a hook + * + * @param {String} hook + */ + + exports._callHook = function (hook) { + var handlers = this.$options[hook] + if (handlers) { + for (var i = 0, j = handlers.length; i < j; i++) { + handlers[i].call(this) + } + } + this.$emit('hook:' + hook) } /***/ }, /* 4 */ /***/ function(module, exports, __webpack_require__) { - var _ = __webpack_require__(1) - var transition = __webpack_require__(49) + var _ = __webpack_require__(11) + var Observer = __webpack_require__(49) + var Dep = __webpack_require__(23) /** - * Append instance to target - * - * @param {Node} target - * @param {Function} [cb] - * @param {Boolean} [withTransition] - defaults to true + * Setup the scope of an instance, which contains: + * - observed data + * - computed properties + * - user methods + * - meta properties */ - exports.$appendTo = function (target, cb, withTransition) { - return insert( - this, target, cb, withTransition, - append, transition.append - ) + exports._initScope = function () { + this._initData() + this._initComputed() + this._initMethods() + this._initMeta() } /** - * Prepend instance to target - * - * @param {Node} target - * @param {Function} [cb] - * @param {Boolean} [withTransition] - defaults to true + * Initialize the data. */ - exports.$prependTo = function (target, cb, withTransition) { - target = query(target) - if (target.hasChildNodes()) { - this.$before(target.firstChild, cb, withTransition) - } else { - this.$appendTo(target, cb, withTransition) + exports._initData = function () { + // proxy data on instance + var data = this._data + var keys = Object.keys(data) + var i = keys.length + var key + while (i--) { + key = keys[i] + if (!_.isReserved(key)) { + this._proxy(key) + } } - return this + // observe data + Observer.create(data).addVm(this) } /** - * Insert instance before target + * Swap the isntance's $data. Called in $data's setter. * - * @param {Node} target - * @param {Function} [cb] - * @param {Boolean} [withTransition] - defaults to true + * @param {Object} newData */ - exports.$before = function (target, cb, withTransition) { - return insert( - this, target, cb, withTransition, - before, transition.before - ) + exports._setData = function (newData) { + newData = newData || {} + var oldData = this._data + this._data = newData + var keys, key, i + // unproxy keys not present in new data + keys = Object.keys(oldData) + i = keys.length + while (i--) { + key = keys[i] + if (!_.isReserved(key) && !(key in newData)) { + this._unproxy(key) + } + } + // proxy keys not already proxied, + // and trigger change for changed values + keys = Object.keys(newData) + i = keys.length + while (i--) { + key = keys[i] + if (!this.hasOwnProperty(key) && !_.isReserved(key)) { + // new property + this._proxy(key) + } + } + oldData.__ob__.removeVm(this) + Observer.create(newData).addVm(this) + this._digest() } /** - * Insert instance after target + * Proxy a property, so that + * vm.prop === vm._data.prop * - * @param {Node} target - * @param {Function} [cb] - * @param {Boolean} [withTransition] - defaults to true + * @param {String} key */ - exports.$after = function (target, cb, withTransition) { - target = query(target) - if (target.nextSibling) { - this.$before(target.nextSibling, cb, withTransition) - } else { - this.$appendTo(target.parentNode, cb, withTransition) - } - return this + exports._proxy = function (key) { + // need to store ref to self here + // because these getter/setters might + // be called by child instances! + var self = this + Object.defineProperty(self, key, { + configurable: true, + enumerable: true, + get: function proxyGetter () { + return self._data[key] + }, + set: function proxySetter (val) { + self._data[key] = val + } + }) } /** - * Remove instance from DOM + * Unproxy a property. * - * @param {Function} [cb] - * @param {Boolean} [withTransition] - defaults to true + * @param {String} key */ - exports.$remove = function (cb, withTransition) { - var inDoc = this._isAttached && _.inDoc(this.$el) - // if we are not in document, no need to check - // for transitions - if (!inDoc) withTransition = false - var op - var self = this - var realCb = function () { - if (inDoc) self._callHook('detached') - if (cb) cb() - } - if ( - this._isBlock && - !this._blockFragment.hasChildNodes() - ) { - op = withTransition === false - ? append - : transition.removeThenAppend - blockOp(this, this._blockFragment, op, realCb) - } else { - op = withTransition === false - ? remove - : transition.remove - op(this.$el, this, realCb) - } - return this + exports._unproxy = function (key) { + delete this[key] } /** - * Shared DOM insertion function. - * - * @param {Vue} vm - * @param {Element} target - * @param {Function} [cb] - * @param {Boolean} [withTransition] - * @param {Function} op1 - op for non-transition insert - * @param {Function} op2 - op for transition insert - * @return vm + * Force update on every watcher in scope. */ - function insert (vm, target, cb, withTransition, op1, op2) { - target = query(target) - var targetIsDetached = !_.inDoc(target) - var op = withTransition === false || targetIsDetached - ? op1 - : op2 - var shouldCallHook = - !targetIsDetached && - !vm._isAttached && - !_.inDoc(vm.$el) - if (vm._isBlock) { - blockOp(vm, target, op, cb) - } else { - op(vm.$el, target, vm, cb) + exports._digest = function () { + var i = this._watcherList.length + while (i--) { + this._watcherList[i].update() } - if (shouldCallHook) { - vm._callHook('attached') + var children = this._children + i = children.length + while (i--) { + var child = children[i] + if (child.$options.inherit) { + child._digest() + } } - return vm } /** - * Execute a transition operation on a block instance, - * iterating through all its block nodes. - * - * @param {Vue} vm - * @param {Node} target - * @param {Function} op - * @param {Function} cb + * Setup computed properties. They are essentially + * special getter/setters */ - function blockOp (vm, target, op, cb) { - var current = vm._blockStart - var end = vm._blockEnd - var next - while (next !== end) { - next = current.nextSibling - op(current, target, vm) - current = next + function noop () {} + exports._initComputed = function () { + var computed = this.$options.computed + if (computed) { + for (var key in computed) { + var userDef = computed[key] + var def = { + enumerable: true, + configurable: true + } + if (typeof userDef === 'function') { + def.get = _.bind(userDef, this) + def.set = noop + } else { + def.get = userDef.get + ? _.bind(userDef.get, this) + : noop + def.set = userDef.set + ? _.bind(userDef.set, this) + : noop + } + Object.defineProperty(this, key, def) + } } - op(end, target, vm, cb) } /** - * Check for selectors - * - * @param {String|Element} el + * Setup instance methods. Methods must be bound to the + * instance since they might be called by children + * inheriting them. */ - function query (el) { - return typeof el === 'string' - ? document.querySelector(el) - : el - } - - /** - * Append operation that takes a callback. - * - * @param {Node} el - * @param {Node} target - * @param {Vue} vm - unused - * @param {Function} [cb] - */ - - function append (el, target, vm, cb) { - target.appendChild(el) - if (cb) cb() + exports._initMethods = function () { + var methods = this.$options.methods + if (methods) { + for (var key in methods) { + this[key] = _.bind(methods[key], this) + } + } } /** - * InsertBefore operation that takes a callback. - * - * @param {Node} el - * @param {Node} target - * @param {Vue} vm - unused - * @param {Function} [cb] + * Initialize meta information like $index, $key & $value. */ - function before (el, target, vm, cb) { - _.before(el, target) - if (cb) cb() + exports._initMeta = function () { + var metas = this.$options._meta + if (metas) { + for (var key in metas) { + this._defineMeta(key, metas[key]) + } + } } /** - * Remove operation that takes a callback. + * Define a meta property, e.g $index, $key, $value + * which only exists on the vm instance but not in $data. * - * @param {Node} el - * @param {Vue} vm - unused - * @param {Function} [cb] + * @param {String} key + * @param {*} value */ - function remove (el, vm, cb) { - _.remove(el) - if (cb) cb() + exports._defineMeta = function (key, value) { + var dep = new Dep() + Object.defineProperty(this, key, { + enumerable: true, + configurable: true, + get: function metaGetter () { + if (Observer.target) { + Observer.target.addDep(dep) + } + return value + }, + set: function metaSetter (val) { + if (val !== value) { + value = val + dep.notify() + } + } + }) } /***/ }, /* 5 */ /***/ function(module, exports, __webpack_require__) { - var _ = __webpack_require__(1) + var _ = __webpack_require__(11) + var Directive = __webpack_require__(24) + var compile = __webpack_require__(16) + var transclude = __webpack_require__(17) /** - * Listen on the given `event` with `fn`. + * Transclude, compile and link element. * - * @param {String} event - * @param {Function} fn - */ - - exports.$on = function (event, fn) { - (this._events[event] || (this._events[event] = [])) - .push(fn) - modifyListenerCount(this, event, 1) - return this - } - - /** - * Adds an `event` listener that will be invoked a single - * time then automatically removed. + * If a pre-compiled linker is available, that means the + * passed in element will be pre-transcluded and compiled + * as well - all we need to do is to call the linker. * - * @param {String} event - * @param {Function} fn - */ - - exports.$once = function (event, fn) { - var self = this - function on () { - self.$off(event, on) - fn.apply(this, arguments) - } - on.fn = fn - this.$on(event, on) - return this - } - - /** - * Remove the given callback for `event` or all - * registered callbacks. + * Otherwise we need to call transclude/compile/link here. * - * @param {String} event - * @param {Function} fn + * @param {Element} el + * @return {Element} */ - exports.$off = function (event, fn) { - var cbs - // all - if (!arguments.length) { - if (this.$parent) { - for (event in this._events) { - cbs = this._events[event] - if (cbs) { - modifyListenerCount(this, event, -cbs.length) - } + exports._compile = function (el) { + var options = this.$options + var parent = options._parent + if (options._linkFn) { + this._initElement(el) + options._linkFn(this, el) + } else { + var raw = el + if (options._asComponent) { + // separate container element and content + var content = options._content = _.extractContent(raw) + // create two separate linekrs for container and content + var parentOptions = parent.$options + + // hack: we need to skip the paramAttributes for this + // child instance when compiling its parent container + // linker. there could be a better way to do this. + parentOptions._skipAttrs = options.paramAttributes + var containerLinkFn = + compile(raw, parentOptions, true, true) + parentOptions._skipAttrs = null + + if (content) { + var ol = parent._children.length + var contentLinkFn = + compile(content, parentOptions, true) + // call content linker now, before transclusion + this._contentUnlinkFn = contentLinkFn(parent, content) + this._transCpnts = parent._children.slice(ol) } + // tranclude, this possibly replaces original + el = transclude(el, options) + this._initElement(el) + // now call the container linker on the resolved el + this._containerUnlinkFn = containerLinkFn(parent, el) + } else { + // simply transclude + el = transclude(el, options) + this._initElement(el) } - this._events = {} - return this - } - // specific event - cbs = this._events[event] - if (!cbs) { - return this - } - if (arguments.length === 1) { - modifyListenerCount(this, event, -cbs.length) - this._events[event] = null - return this - } - // specific handler - var cb - var i = cbs.length - while (i--) { - cb = cbs[i] - if (cb === fn || cb.fn === fn) { - modifyListenerCount(this, event, -1) - cbs.splice(i, 1) - break + var linkFn = compile(el, options) + linkFn(this, el) + if (options.replace) { + _.replace(raw, el) } } - return this + return el } /** - * Trigger an event on self. + * Initialize instance element. Called in the public + * $mount() method. * - * @param {String} event + * @param {Element} el */ - exports.$emit = function (event) { - this._eventCancelled = false - var cbs = this._events[event] - if (cbs) { - // avoid leaking arguments: - // http://jsperf.com/closure-with-arguments - var i = arguments.length - 1 - var args = new Array(i) - while (i--) { - args[i] = arguments[i + 1] - } - i = 0 - cbs = cbs.length > 1 - ? _.toArray(cbs) - : cbs - for (var l = cbs.length; i < l; i++) { - if (cbs[i].apply(this, args) === false) { - this._eventCancelled = true - } - } + exports._initElement = function (el) { + if (el instanceof DocumentFragment) { + this._isBlock = true + this.$el = this._blockStart = el.firstChild + this._blockEnd = el.lastChild + this._blockFragment = el + } else { + this.$el = el } - return this + this.$el.__vue__ = this + this._callHook('beforeCompile') } /** - * Recursively broadcast an event to all children instances. + * Create and bind a directive to an element. * - * @param {String} event - * @param {...*} additional arguments + * @param {String} name - directive name + * @param {Node} node - target node + * @param {Object} desc - parsed directive descriptor + * @param {Object} def - directive definition object */ - exports.$broadcast = function (event) { - // if no child has registered for this event, - // then there's no need to broadcast. - if (!this._eventsCount[event]) return - var children = this._children - if (children) { - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i] - child.$emit.apply(child, arguments) - if (!child._eventCancelled) { - child.$broadcast.apply(child, arguments) - } - } - } - return this + exports._bindDir = function (name, node, desc, def) { + this._directives.push( + new Directive(name, node, this, desc, def) + ) } /** - * Recursively propagate an event up the parent chain. + * Teardown an instance, unobserves the data, unbind all the + * directives, turn off all the event listeners, etc. * - * @param {String} event - * @param {...*} additional arguments + * @param {Boolean} remove - whether to remove the DOM node. + * @param {Boolean} deferCleanup - if true, defer cleanup to + * be called later */ - exports.$dispatch = function () { - var parent = this.$parent - while (parent) { - parent.$emit.apply(parent, arguments) - parent = parent._eventCancelled - ? null - : parent.$parent + exports._destroy = function (remove, deferCleanup) { + if (this._isBeingDestroyed) { + return } - return this - } - - /** - * Modify the listener counts on all parents. - * This bookkeeping allows $broadcast to return early when - * no child has listened to a certain event. - * - * @param {Vue} vm - * @param {String} event - * @param {Number} count - */ - - var hookRE = /^hook:/ - function modifyListenerCount (vm, event, count) { - var parent = vm.$parent - // hooks do not get broadcasted so no need - // to do bookkeeping for them - if (!parent || !count || hookRE.test(event)) return - while (parent) { - parent._eventsCount[event] = - (parent._eventsCount[event] || 0) + count - parent = parent.$parent + this._callHook('beforeDestroy') + this._isBeingDestroyed = true + var i + // remove self from parent. only necessary + // if parent is not being destroyed as well. + var parent = this.$parent + if (parent && !parent._isBeingDestroyed) { + i = parent._children.indexOf(this) + parent._children.splice(i, 1) + } + // destroy all children. + i = this._children.length + while (i--) { + this._children[i].$destroy() + } + // teardown parent linkers + if (this._containerUnlinkFn) { + this._containerUnlinkFn() + } + if (this._contentUnlinkFn) { + this._contentUnlinkFn() + } + // teardown all directives. this also tearsdown all + // directive-owned watchers. intentionally check for + // directives array length on every loop since directives + // that manages partial compilation can splice ones out + for (i = 0; i < this._directives.length; i++) { + this._directives[i]._teardown() + } + // teardown all user watchers. + for (i in this._userWatchers) { + this._userWatchers[i].teardown() + } + // remove reference to self on $el + if (this.$el) { + this.$el.__vue__ = null + } + // remove DOM element + var self = this + if (remove && this.$el) { + this.$remove(function () { + self._cleanup() + }) + } else if (!deferCleanup) { + this._cleanup() } } -/***/ }, -/* 6 */ -/***/ function(module, exports, __webpack_require__) { - - var _ = __webpack_require__(1) - /** - * Create a child instance that prototypally inehrits - * data on parent. To achieve that we create an intermediate - * constructor with its prototype pointing to parent. - * - * @param {Object} opts - * @param {Function} [BaseCtor] - * @return {Vue} - * @public + * Clean up to ensure garbage collection. + * This is called after the leave transition if there + * is any. */ - exports.$addChild = function (opts, BaseCtor) { - BaseCtor = BaseCtor || _.Vue - opts = opts || {} - var parent = this - var ChildVue - var inherit = opts.inherit !== undefined - ? opts.inherit - : BaseCtor.options.inherit - if (inherit) { - var ctors = parent._childCtors - if (!ctors) { - ctors = parent._childCtors = {} - } - ChildVue = ctors[BaseCtor.cid] - if (!ChildVue) { - var optionName = BaseCtor.options.name - var className = optionName - ? _.camelize(optionName, true) - : 'VueComponent' - ChildVue = new Function( - 'return function ' + className + ' (options) {' + - 'this.constructor = ' + className + ';' + - 'this._init(options) }' - )() - ChildVue.options = BaseCtor.options - ChildVue.prototype = this - ctors[BaseCtor.cid] = ChildVue - } - } else { - ChildVue = BaseCtor - } - opts._parent = parent - opts._root = parent.$root - var child = new ChildVue(opts) - if (!this._children) { - this._children = [] - } - this._children.push(child) - return child + exports._cleanup = function () { + // remove reference from data ob + this._data.__ob__.removeVm(this) + this._data = + this._watchers = + this._userWatchers = + this._watcherList = + this.$el = + this.$parent = + this.$root = + this._children = + this._transCpnts = + this._directives = null + // call the last hook... + this._isDestroyed = true + this._callHook('destroyed') + // turn off all instance listeners. + this.$off() } /***/ }, -/* 7 */ +/* 6 */ /***/ function(module, exports, __webpack_require__) { - var _ = __webpack_require__(1) - var compile = __webpack_require__(42) + var _ = __webpack_require__(11) + var Watcher = __webpack_require__(25) + var Path = __webpack_require__(18) + var textParser = __webpack_require__(19) + var dirParser = __webpack_require__(21) + var expParser = __webpack_require__(22) + var filterRE = /[^|]\|[^|]/ /** - * Set instance target element and kick off the compilation - * process. The passed in `el` can be a selector string, an - * existing Element, or a DocumentFragment (for block - * instances). + * Get the value from an expression on this vm. * - * @param {Element|DocumentFragment|string} el - * @public + * @param {String} exp + * @return {*} */ - exports.$mount = function (el) { - if (this._isCompiled) { - _.warn('$mount() should be called only once.') - return - } - if (!el) { - el = document.createElement('div') - } else if (typeof el === 'string') { - var selector = el - el = document.querySelector(el) - if (!el) { - _.warn('Cannot find element: ' + selector) - return - } - } - this._compile(el) - this._isCompiled = true - this._callHook('compiled') - if (_.inDoc(this.$el)) { - this._callHook('attached') - this._initDOMHooks() - ready.call(this) - } else { - this._initDOMHooks() - this.$once('hook:attached', ready) + exports.$get = function (exp) { + var res = expParser.parse(exp) + if (res) { + return res.get.call(this, this) } - return this } /** - * Mark an instance as ready. + * Set the value from an expression on this vm. + * The expression must be a valid left-hand + * expression in an assignment. + * + * @param {String} exp + * @param {*} val */ - function ready () { - this._isAttached = true - this._isReady = true - this._callHook('ready') + exports.$set = function (exp, val) { + var res = expParser.parse(exp, true) + if (res && res.set) { + res.set.call(this, this, val) + } } /** - * Teardown the instance, simply delegate to the internal - * _destroy. + * Add a property on the VM + * + * @param {String} key + * @param {*} val */ - exports.$destroy = function (remove, deferCleanup) { - this._destroy(remove, deferCleanup) + exports.$add = function (key, val) { + this._data.$add(key, val) } /** - * Partially compile a piece of DOM and return a - * decompile function. + * Delete a property on the VM * - * @param {Element|DocumentFragment} el - * @return {Function} + * @param {String} key */ - exports.$compile = function (el) { - return compile(el, this.$options, true)(this, el) + exports.$delete = function (key) { + this._data.$delete(key) } -/***/ }, -/* 8 */ -/***/ function(module, exports, __webpack_require__) { - - // manipulation directives - exports.text = __webpack_require__(22) - exports.html = __webpack_require__(23) - exports.attr = __webpack_require__(24) - exports.show = __webpack_require__(25) - exports['class'] = __webpack_require__(26) - exports.el = __webpack_require__(27) - exports.ref = __webpack_require__(28) - exports.cloak = __webpack_require__(29) - exports.style = __webpack_require__(30) - exports.partial = __webpack_require__(31) - exports.transition = __webpack_require__(32) - - // event listener directives - exports.on = __webpack_require__(33) - exports.model = __webpack_require__(50) - - // child vm directives - exports.component = __webpack_require__(34) - exports.repeat = __webpack_require__(35) - exports['if'] = __webpack_require__(36) - - // child vm communication directives - exports['with'] = __webpack_require__(37) - exports.events = __webpack_require__(38) - -/***/ }, -/* 9 */ -/***/ function(module, exports, __webpack_require__) { - - var _ = __webpack_require__(1) - /** - * Stringify value. + * Watch an expression, trigger callback when its + * value changes. * - * @param {Number} indent + * @param {String} exp + * @param {Function} cb + * @param {Boolean} [deep] + * @param {Boolean} [immediate] + * @return {Function} - unwatchFn */ - exports.json = { - read: function (value, indent) { - return typeof value === 'string' - ? value - : JSON.stringify(value, null, Number(indent) || 2) - }, - write: function (value) { - try { - return JSON.parse(value) - } catch (e) { - return value - } + exports.$watch = function (exp, cb, deep, immediate) { + var vm = this + var key = deep ? exp + '**deep**' : exp + var watcher = vm._userWatchers[key] + var wrappedCb = function (val, oldVal) { + cb.call(vm, val, oldVal) + } + if (!watcher) { + watcher = vm._userWatchers[key] = + new Watcher(vm, exp, wrappedCb, { + deep: deep, + user: true + }) + } else { + watcher.addCb(wrappedCb) + } + if (immediate) { + wrappedCb(watcher.value) + } + return function unwatchFn () { + watcher.removeCb(wrappedCb) + if (!watcher.active) { + vm._userWatchers[key] = null + } } } /** - * 'abc' => 'Abc' - */ - - exports.capitalize = function (value) { - if (!value && value !== 0) return '' - value = value.toString() - return value.charAt(0).toUpperCase() + value.slice(1) - } - - /** - * 'abc' => 'ABC' + * Evaluate a text directive, including filters. + * + * @param {String} text + * @return {String} */ - exports.uppercase = function (value) { - return (value || value === 0) - ? value.toString().toUpperCase() - : '' + exports.$eval = function (text) { + // check for filters. + if (filterRE.test(text)) { + var dir = dirParser.parse(text)[0] + // the filter regex check might give false positive + // for pipes inside strings, so it's possible that + // we don't get any filters here + return dir.filters + ? _.applyFilters( + this.$get(dir.expression), + _.resolveFilters(this, dir.filters).read, + this + ) + : this.$get(dir.expression) + } else { + // no filter + return this.$get(text) + } } /** - * 'AbC' => 'abc' + * Interpolate a piece of template text. + * + * @param {String} text + * @return {String} */ - exports.lowercase = function (value) { - return (value || value === 0) - ? value.toString().toLowerCase() - : '' + exports.$interpolate = function (text) { + var tokens = textParser.parse(text) + var vm = this + if (tokens) { + return tokens.length === 1 + ? vm.$eval(tokens[0].value) + : tokens.map(function (token) { + return token.tag + ? vm.$eval(token.value) + : token.value + }).join('') + } else { + return text + } } /** - * 12345 => $12,345.00 + * Log instance data as a plain JS object + * so that it is easier to inspect in console. + * This method assumes console is available. * - * @param {String} sign + * @param {String} [path] */ - var digitsRE = /(\d{3})(?=\d)/g - - exports.currency = function (value, sign) { - value = parseFloat(value) - if (!value && value !== 0) return '' - sign = sign || '$' - var s = Math.floor(Math.abs(value)).toString(), - i = s.length % 3, - h = i > 0 - ? (s.slice(0, i) + (s.length > 3 ? ',' : '')) - : '', - f = '.' + value.toFixed(2).slice(-2) - return (value < 0 ? '-' : '') + - sign + h + s.slice(i).replace(digitsRE, '$1,') + f + exports.$log = function (path) { + var data = path + ? Path.get(this._data, path) + : this._data + if (data) { + data = JSON.parse(JSON.stringify(data)) + } + console.log(data) } +/***/ }, +/* 7 */ +/***/ function(module, exports, __webpack_require__) { + + var _ = __webpack_require__(11) + var transition = __webpack_require__(50) + /** - * 'item' => 'items' - * - * @params - * an array of strings corresponding to - * the single, double, triple ... forms of the word to - * be pluralized. When the number to be pluralized - * exceeds the length of the args, it will use the last - * entry in the array. + * Append instance to target * - * e.g. ['single', 'double', 'triple', 'multiple'] + * @param {Node} target + * @param {Function} [cb] + * @param {Boolean} [withTransition] - defaults to true */ - exports.pluralize = function (value) { - var args = _.toArray(arguments, 1) - return args.length > 1 - ? (args[value % 10 - 1] || args[args.length - 1]) - : (args[0] + (value === 1 ? '' : 's')) + exports.$appendTo = function (target, cb, withTransition) { + return insert( + this, target, cb, withTransition, + append, transition.append + ) } /** - * A special filter that takes a handler function, - * wraps it so it only gets triggered on specific - * keypresses. v-on only. + * Prepend instance to target * - * @param {String} key + * @param {Node} target + * @param {Function} [cb] + * @param {Boolean} [withTransition] - defaults to true */ - var keyCodes = { - enter : 13, - tab : 9, - 'delete' : 46, - up : 38, - left : 37, - right : 39, - down : 40, - esc : 27 - } - - exports.key = function (handler, key) { - if (!handler) return - var code = keyCodes[key] - if (!code) { - code = parseInt(key, 10) - } - return function (e) { - if (e.keyCode === code) { - return handler.call(this, e) - } + exports.$prependTo = function (target, cb, withTransition) { + target = query(target) + if (target.hasChildNodes()) { + this.$before(target.firstChild, cb, withTransition) + } else { + this.$appendTo(target, cb, withTransition) } + return this } - // expose keycode hash - exports.key.keyCodes = keyCodes - /** - * Install special array filters + * Insert instance before target + * + * @param {Node} target + * @param {Function} [cb] + * @param {Boolean} [withTransition] - defaults to true */ - _.extend(exports, __webpack_require__(39)) + exports.$before = function (target, cb, withTransition) { + return insert( + this, target, cb, withTransition, + before, transition.before + ) + } -/***/ }, -/* 10 */ -/***/ function(module, exports, __webpack_require__) { + /** + * Insert instance after target + * + * @param {Node} target + * @param {Function} [cb] + * @param {Boolean} [withTransition] - defaults to true + */ - var mergeOptions = __webpack_require__(19) + exports.$after = function (target, cb, withTransition) { + target = query(target) + if (target.nextSibling) { + this.$before(target.nextSibling, cb, withTransition) + } else { + this.$appendTo(target.parentNode, cb, withTransition) + } + return this + } /** - * The main init sequence. This is called for every - * instance, including ones that are created from extended - * constructors. + * Remove instance from DOM * - * @param {Object} options - this options object should be - * the result of merging class - * options and the options passed - * in to the constructor. + * @param {Function} [cb] + * @param {Boolean} [withTransition] - defaults to true */ - exports._init = function (options) { + exports.$remove = function (cb, withTransition) { + var inDoc = this._isAttached && _.inDoc(this.$el) + // if we are not in document, no need to check + // for transitions + if (!inDoc) withTransition = false + var op + var self = this + var realCb = function () { + if (inDoc) self._callHook('detached') + if (cb) cb() + } + if ( + this._isBlock && + !this._blockFragment.hasChildNodes() + ) { + op = withTransition === false + ? append + : transition.removeThenAppend + blockOp(this, this._blockFragment, op, realCb) + } else { + op = withTransition === false + ? remove + : transition.remove + op(this.$el, this, realCb) + } + return this + } - options = options || {} + /** + * Shared DOM insertion function. + * + * @param {Vue} vm + * @param {Element} target + * @param {Function} [cb] + * @param {Boolean} [withTransition] + * @param {Function} op1 - op for non-transition insert + * @param {Function} op2 - op for transition insert + * @return vm + */ - this.$el = null - this.$parent = options._parent - this.$root = options._root || this - this.$ = {} // child vm references - this.$$ = {} // element references - this._watcherList = [] // all watchers as an array - this._watchers = {} // internal watchers as a hash - this._userWatchers = {} // user watchers as a hash - this._directives = [] // all directives - - // a flag to avoid this being observed - this._isVue = true - - // events bookkeeping - this._events = {} // registered callbacks - this._eventsCount = {} // for $broadcast optimization - this._eventCancelled = false // for event cancellation - - // block instance properties - this._isBlock = false - this._blockStart = // @type {CommentNode} - this._blockEnd = null // @type {CommentNode} - - // lifecycle state - this._isCompiled = - this._isDestroyed = - this._isReady = - this._isAttached = - this._isBeingDestroyed = false - - // children - this._children = // @type {Array} - this._childCtors = null // @type {Object} - hash to cache - // child constructors - - // merge options. - options = this.$options = mergeOptions( - this.constructor.options, - options, - this - ) - - // set data after merge. - this._data = options.data || {} - - // initialize data observation and scope inheritance. - this._initScope() - - // 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) + function insert (vm, target, cb, withTransition, op1, op2) { + target = query(target) + var targetIsDetached = !_.inDoc(target) + var op = withTransition === false || targetIsDetached + ? op1 + : op2 + var shouldCallHook = + !targetIsDetached && + !vm._isAttached && + !_.inDoc(vm.$el) + if (vm._isBlock) { + blockOp(vm, target, op, cb) + } else { + op(vm.$el, target, vm, cb) } - } - -/***/ }, -/* 11 */ -/***/ function(module, exports, __webpack_require__) { - - var _ = __webpack_require__(1) - var inDoc = _.inDoc - - /** - * Setup the instance's option events & watchers. - * If the value is a string, we pull it from the - * instance's methods by name. - */ - - exports._initEvents = function () { - var options = this.$options - registerCallbacks(this, '$on', options.events) - registerCallbacks(this, '$watch', options.watch) - } - - /** - * Register callbacks for option events and watchers. - * - * @param {Vue} vm - * @param {String} action - * @param {Object} hash - */ - - function registerCallbacks (vm, action, hash) { - if (!hash) return - var handlers, key, i, j - for (key in hash) { - handlers = hash[key] - if (_.isArray(handlers)) { - for (i = 0, j = handlers.length; i < j; i++) { - register(vm, action, key, handlers[i]) - } - } else { - register(vm, action, key, handlers) - } + if (shouldCallHook) { + vm._callHook('attached') } + return vm } /** - * Helper to register an event/watch callback. + * Execute a transition operation on a block instance, + * iterating through all its block nodes. * * @param {Vue} vm - * @param {String} action - * @param {String} key - * @param {*} handler + * @param {Node} target + * @param {Function} op + * @param {Function} cb */ - function register (vm, action, key, handler) { - var type = typeof handler - if (type === 'function') { - vm[action](key, handler) - } else if (type === 'string') { - var methods = vm.$options.methods - var method = methods && methods[handler] - if (method) { - vm[action](key, method) - } else { - _.warn( - 'Unknown method: "' + handler + '" when ' + - 'registering callback for ' + action + - ': "' + key + '".' - ) - } + function blockOp (vm, target, op, cb) { + var current = vm._blockStart + var end = vm._blockEnd + var next + while (next !== end) { + next = current.nextSibling + op(current, target, vm) + current = next } + op(end, target, vm, cb) } /** - * Setup recursive attached/detached calls + * Check for selectors + * + * @param {String|Element} el */ - exports._initDOMHooks = function () { - this.$on('hook:attached', onAttached) - this.$on('hook:detached', onDetached) + function query (el) { + return typeof el === 'string' + ? document.querySelector(el) + : el } /** - * Callback to recursively call attached hook on children + * Append operation that takes a callback. + * + * @param {Node} el + * @param {Node} target + * @param {Vue} vm - unused + * @param {Function} [cb] */ - function onAttached () { - this._isAttached = true - var children = this._children - if (!children) return - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i] - if (!child._isAttached && inDoc(child.$el)) { - child._callHook('attached') - } - } + function append (el, target, vm, cb) { + target.appendChild(el) + if (cb) cb() } /** - * Callback to recursively call detached hook on children + * InsertBefore operation that takes a callback. + * + * @param {Node} el + * @param {Node} target + * @param {Vue} vm - unused + * @param {Function} [cb] */ - function onDetached () { - this._isAttached = false - var children = this._children - if (!children) return - for (var i = 0, l = children.length; i < l; i++) { - var child = children[i] - if (child._isAttached && !inDoc(child.$el)) { - child._callHook('detached') - } - } + function before (el, target, vm, cb) { + _.before(el, target) + if (cb) cb() } /** - * Trigger all handlers for a hook + * Remove operation that takes a callback. * - * @param {String} hook + * @param {Node} el + * @param {Vue} vm - unused + * @param {Function} [cb] */ - exports._callHook = function (hook) { - var handlers = this.$options[hook] - if (handlers) { - for (var i = 0, j = handlers.length; i < j; i++) { - handlers[i].call(this) - } - } - this.$emit('hook:' + hook) + function remove (el, vm, cb) { + _.remove(el) + if (cb) cb() } /***/ }, -/* 12 */ +/* 8 */ /***/ function(module, exports, __webpack_require__) { - var _ = __webpack_require__(1) - var Observer = __webpack_require__(51) - var Dep = __webpack_require__(40) + var _ = __webpack_require__(11) /** - * Setup the scope of an instance, which contains: - * - observed data - * - computed properties - * - user methods - * - meta properties + * Listen on the given `event` with `fn`. + * + * @param {String} event + * @param {Function} fn */ - exports._initScope = function () { - this._initData() - this._initComputed() - this._initMethods() - this._initMeta() + exports.$on = function (event, fn) { + (this._events[event] || (this._events[event] = [])) + .push(fn) + modifyListenerCount(this, event, 1) + return this } /** - * Initialize the data. + * Adds an `event` listener that will be invoked a single + * time then automatically removed. + * + * @param {String} event + * @param {Function} fn */ - exports._initData = function () { - // proxy data on instance - var data = this._data - var keys = Object.keys(data) - var i = keys.length - var key - while (i--) { - key = keys[i] - if (!_.isReserved(key)) { - this._proxy(key) - } + exports.$once = function (event, fn) { + var self = this + function on () { + self.$off(event, on) + fn.apply(this, arguments) } - // observe data - Observer.create(data).addVm(this) + on.fn = fn + this.$on(event, on) + return this } /** - * Swap the isntance's $data. Called in $data's setter. + * Remove the given callback for `event` or all + * registered callbacks. * - * @param {Object} newData + * @param {String} event + * @param {Function} fn */ - exports._setData = function (newData) { - newData = newData || {} - var oldData = this._data - this._data = newData - var keys, key, i - // unproxy keys not present in new data - keys = Object.keys(oldData) - i = keys.length - while (i--) { - key = keys[i] - if (!_.isReserved(key) && !(key in newData)) { - this._unproxy(key) + exports.$off = function (event, fn) { + var cbs + // all + if (!arguments.length) { + if (this.$parent) { + for (event in this._events) { + cbs = this._events[event] + if (cbs) { + modifyListenerCount(this, event, -cbs.length) + } + } } + this._events = {} + return this } - // proxy keys not already proxied, - // and trigger change for changed values - keys = Object.keys(newData) - i = keys.length + // specific event + cbs = this._events[event] + if (!cbs) { + return this + } + if (arguments.length === 1) { + modifyListenerCount(this, event, -cbs.length) + this._events[event] = null + return this + } + // specific handler + var cb + var i = cbs.length while (i--) { - key = keys[i] - if (!this.hasOwnProperty(key) && !_.isReserved(key)) { - // new property - this._proxy(key) + cb = cbs[i] + if (cb === fn || cb.fn === fn) { + modifyListenerCount(this, event, -1) + cbs.splice(i, 1) + break } } - oldData.__ob__.removeVm(this) - Observer.create(newData).addVm(this) - this._digest() + return this } /** - * Proxy a property, so that - * vm.prop === vm._data.prop + * Trigger an event on self. * - * @param {String} key + * @param {String} event */ - exports._proxy = function (key) { - // need to store ref to self here - // because these getter/setters might - // be called by child instances! - var self = this - Object.defineProperty(self, key, { - configurable: true, - enumerable: true, - get: function proxyGetter () { - return self._data[key] - }, - set: function proxySetter (val) { - self._data[key] = val + exports.$emit = function (event) { + this._eventCancelled = false + var cbs = this._events[event] + if (cbs) { + // avoid leaking arguments: + // http://jsperf.com/closure-with-arguments + var i = arguments.length - 1 + var args = new Array(i) + while (i--) { + args[i] = arguments[i + 1] } - }) + i = 0 + cbs = cbs.length > 1 + ? _.toArray(cbs) + : cbs + for (var l = cbs.length; i < l; i++) { + if (cbs[i].apply(this, args) === false) { + this._eventCancelled = true + } + } + } + return this } /** - * Unproxy a property. + * Recursively broadcast an event to all children instances. * - * @param {String} key - */ - - exports._unproxy = function (key) { - delete this[key] - } - - /** - * Force update on every watcher in scope. + * @param {String} event + * @param {...*} additional arguments */ - exports._digest = function () { - var i = this._watcherList.length - while (i--) { - this._watcherList[i].update() - } + exports.$broadcast = function (event) { + // if no child has registered for this event, + // then there's no need to broadcast. + if (!this._eventsCount[event]) return var children = this._children - var child - if (children) { - i = children.length - while (i--) { - child = children[i] - if (child.$options.inherit) { - child._digest() - } + for (var i = 0, l = children.length; i < l; i++) { + var child = children[i] + child.$emit.apply(child, arguments) + if (!child._eventCancelled) { + child.$broadcast.apply(child, arguments) } } + return this } /** - * Setup computed properties. They are essentially - * special getter/setters + * Recursively propagate an event up the parent chain. + * + * @param {String} event + * @param {...*} additional arguments */ - function noop () {} - exports._initComputed = function () { - var computed = this.$options.computed - if (computed) { - for (var key in computed) { - var userDef = computed[key] - var def = { - enumerable: true, - configurable: true - } - if (typeof userDef === 'function') { - def.get = _.bind(userDef, this) - def.set = noop - } else { - def.get = userDef.get - ? _.bind(userDef.get, this) - : noop - def.set = userDef.set - ? _.bind(userDef.set, this) - : noop - } - Object.defineProperty(this, key, def) - } + exports.$dispatch = function () { + var parent = this.$parent + while (parent) { + parent.$emit.apply(parent, arguments) + parent = parent._eventCancelled + ? null + : parent.$parent } + return this } /** - * Setup instance methods. Methods must be bound to the - * instance since they might be called by children - * inheriting them. + * Modify the listener counts on all parents. + * This bookkeeping allows $broadcast to return early when + * no child has listened to a certain event. + * + * @param {Vue} vm + * @param {String} event + * @param {Number} count */ - exports._initMethods = function () { - var methods = this.$options.methods - if (methods) { - for (var key in methods) { - this[key] = _.bind(methods[key], this) - } + var hookRE = /^hook:/ + function modifyListenerCount (vm, event, count) { + var parent = vm.$parent + // hooks do not get broadcasted so no need + // to do bookkeeping for them + if (!parent || !count || hookRE.test(event)) return + while (parent) { + parent._eventsCount[event] = + (parent._eventsCount[event] || 0) + count + parent = parent.$parent } } +/***/ }, +/* 9 */ +/***/ function(module, exports, __webpack_require__) { + + var _ = __webpack_require__(11) + /** - * Initialize meta information like $index, $key & $value. + * Create a child instance that prototypally inehrits + * data on parent. To achieve that we create an intermediate + * constructor with its prototype pointing to parent. + * + * @param {Object} opts + * @param {Function} [BaseCtor] + * @return {Vue} + * @public */ - exports._initMeta = function () { - var metas = this.$options._meta - if (metas) { - for (var key in metas) { - this._defineMeta(key, metas[key]) + exports.$addChild = function (opts, BaseCtor) { + BaseCtor = BaseCtor || _.Vue + opts = opts || {} + var parent = this + var ChildVue + var inherit = opts.inherit !== undefined + ? opts.inherit + : BaseCtor.options.inherit + if (inherit) { + var ctors = parent._childCtors + ChildVue = ctors[BaseCtor.cid] + if (!ChildVue) { + var optionName = BaseCtor.options.name + var className = optionName + ? _.camelize(optionName, true) + : 'VueComponent' + ChildVue = new Function( + 'return function ' + className + ' (options) {' + + 'this.constructor = ' + className + ';' + + 'this._init(options) }' + )() + ChildVue.options = BaseCtor.options + ChildVue.prototype = this + ctors[BaseCtor.cid] = ChildVue } + } else { + ChildVue = BaseCtor } + opts._parent = parent + opts._root = parent.$root + var child = new ChildVue(opts) + this._children.push(child) + return child } +/***/ }, +/* 10 */ +/***/ function(module, exports, __webpack_require__) { + + var _ = __webpack_require__(11) + var compile = __webpack_require__(16) + /** - * Define a meta property, e.g $index, $key, $value - * which only exists on the vm instance but not in $data. + * Set instance target element and kick off the compilation + * process. The passed in `el` can be a selector string, an + * existing Element, or a DocumentFragment (for block + * instances). * - * @param {String} key - * @param {*} value + * @param {Element|DocumentFragment|string} el + * @public */ - exports._defineMeta = function (key, value) { - var dep = new Dep() - Object.defineProperty(this, key, { - enumerable: true, - configurable: true, - get: function metaGetter () { - if (Observer.target) { - Observer.target.addDep(dep) - } - return value - }, - set: function metaSetter (val) { - if (val !== value) { - value = val - dep.notify() - } + exports.$mount = function (el) { + if (this._isCompiled) { + _.warn('$mount() should be called only once.') + return + } + if (!el) { + el = document.createElement('div') + } else if (typeof el === 'string') { + var selector = el + el = document.querySelector(el) + if (!el) { + _.warn('Cannot find element: ' + selector) + return } - }) + } + this._compile(el) + this._isCompiled = true + this._callHook('compiled') + if (_.inDoc(this.$el)) { + this._callHook('attached') + this._initDOMHooks() + ready.call(this) + } else { + this._initDOMHooks() + this.$once('hook:attached', ready) + } + return this + } + + /** + * Mark an instance as ready. + */ + + function ready () { + this._isAttached = true + this._isReady = true + this._callHook('ready') + } + + /** + * Teardown the instance, simply delegate to the internal + * _destroy. + */ + + exports.$destroy = function (remove, deferCleanup) { + this._destroy(remove, deferCleanup) + } + + /** + * Partially compile a piece of DOM and return a + * decompile function. + * + * @param {Element|DocumentFragment} el + * @return {Function} + */ + + exports.$compile = function (el) { + return compile(el, this.$options, true)(this, el) } +/***/ }, +/* 11 */ +/***/ function(module, exports, __webpack_require__) { + + var lang = __webpack_require__(26) + var extend = lang.extend + + extend(exports, lang) + extend(exports, __webpack_require__(27)) + extend(exports, __webpack_require__(28)) + extend(exports, __webpack_require__(29)) + extend(exports, __webpack_require__(30)) + +/***/ }, +/* 12 */ +/***/ function(module, exports, __webpack_require__) { + + // manipulation directives + exports.text = __webpack_require__(31) + exports.html = __webpack_require__(32) + exports.attr = __webpack_require__(33) + exports.show = __webpack_require__(34) + exports['class'] = __webpack_require__(35) + exports.el = __webpack_require__(36) + exports.ref = __webpack_require__(37) + exports.cloak = __webpack_require__(38) + exports.style = __webpack_require__(39) + exports.partial = __webpack_require__(40) + exports.transition = __webpack_require__(41) + + // event listener directives + exports.on = __webpack_require__(42) + exports.model = __webpack_require__(51) + + // child vm directives + exports.component = __webpack_require__(43) + exports.repeat = __webpack_require__(44) + exports['if'] = __webpack_require__(45) + + // child vm communication directives + exports['with'] = __webpack_require__(46) + exports.events = __webpack_require__(47) + /***/ }, /* 13 */ /***/ function(module, exports, __webpack_require__) { - var _ = __webpack_require__(1) - var Directive = __webpack_require__(41) - var compile = __webpack_require__(42) - var transclude = __webpack_require__(43) + var _ = __webpack_require__(11) /** - * Transclude, compile and link element. - * - * If a pre-compiled linker is available, that means the - * passed in element will be pre-transcluded and compiled - * as well - all we need to do is to call the linker. - * - * Otherwise we need to call transclude/compile/link here. + * Stringify value. * - * @param {Element} el - * @return {Element} + * @param {Number} indent */ - exports._compile = function (el) { - var options = this.$options - var parent = options._parent - if (options._linkFn) { - this._initElement(el) - options._linkFn(this, el) - } else { - var raw = el - if (options._asComponent) { - // separate container element and content - var content = options._content = _.extractContent(raw) - // create two separate linekrs for container and content - var parentOptions = parent.$options - - // hack: we need to skip the paramAttributes for this - // child instance when compiling its parent container - // linker. there could be a better way to do this. - parentOptions._skipAttrs = options.paramAttributes - var containerLinkFn = - compile(raw, parentOptions, true, true) - parentOptions._skipAttrs = null - - if (content) { - var contentLinkFn = - compile(content, parentOptions, true) - // call content linker now, before transclusion - this._contentUnlinkFn = contentLinkFn(parent, content) - } - // tranclude, this possibly replaces original - el = transclude(el, options) - this._initElement(el) - // now call the container linker on the resolved el - this._containerUnlinkFn = containerLinkFn(parent, el) - } else { - // simply transclude - el = transclude(el, options) - this._initElement(el) - } - var linkFn = compile(el, options) - linkFn(this, el) - if (options.replace) { - _.replace(raw, el) + exports.json = { + read: function (value, indent) { + return typeof value === 'string' + ? value + : JSON.stringify(value, null, Number(indent) || 2) + }, + write: function (value) { + try { + return JSON.parse(value) + } catch (e) { + return value } } - return el } /** - * Initialize instance element. Called in the public - * $mount() method. - * - * @param {Element} el + * 'abc' => 'Abc' */ - exports._initElement = function (el) { - if (el instanceof DocumentFragment) { - this._isBlock = true - this.$el = this._blockStart = el.firstChild - this._blockEnd = el.lastChild - this._blockFragment = el - } else { - this.$el = el - } - this.$el.__vue__ = this - this._callHook('beforeCompile') + exports.capitalize = function (value) { + if (!value && value !== 0) return '' + value = value.toString() + return value.charAt(0).toUpperCase() + value.slice(1) } /** - * Create and bind a directive to an element. - * - * @param {String} name - directive name - * @param {Node} node - target node - * @param {Object} desc - parsed directive descriptor - * @param {Object} def - directive definition object + * 'abc' => 'ABC' */ - exports._bindDir = function (name, node, desc, def) { - this._directives.push( - new Directive(name, node, this, desc, def) - ) + exports.uppercase = function (value) { + return (value || value === 0) + ? value.toString().toUpperCase() + : '' } /** - * Teardown an instance, unobserves the data, unbind all the - * directives, turn off all the event listeners, etc. - * - * @param {Boolean} remove - whether to remove the DOM node. - * @param {Boolean} deferCleanup - if true, defer cleanup to - * be called later + * 'AbC' => 'abc' */ - exports._destroy = function (remove, deferCleanup) { - if (this._isBeingDestroyed) { - return - } - this._callHook('beforeDestroy') - this._isBeingDestroyed = true - var i - // remove self from parent. only necessary - // if parent is not being destroyed as well. - var parent = this.$parent - if (parent && !parent._isBeingDestroyed) { - i = parent._children.indexOf(this) - parent._children.splice(i, 1) - } - // destroy all children. - if (this._children) { - i = this._children.length - while (i--) { - this._children[i].$destroy() - } - } - // teardown parent linkers - if (this._containerUnlinkFn) { - this._containerUnlinkFn() - } - if (this._contentUnlinkFn) { - this._contentUnlinkFn() - } - // teardown all directives. this also tearsdown all - // directive-owned watchers. intentionally check for - // directives array length on every loop since directives - // that manages partial compilation can splice ones out - for (i = 0; i < this._directives.length; i++) { - this._directives[i]._teardown() - } - // teardown all user watchers. - for (i in this._userWatchers) { - this._userWatchers[i].teardown() - } - // remove reference to self on $el - if (this.$el) { - this.$el.__vue__ = null - } - // remove DOM element - var self = this - if (remove && this.$el) { - this.$remove(function () { - self._cleanup() - }) - } else if (!deferCleanup) { - this._cleanup() - } + exports.lowercase = function (value) { + return (value || value === 0) + ? value.toString().toLowerCase() + : '' } /** - * Clean up to ensure garbage collection. - * This is called after the leave transition if there - * is any. + * 12345 => $12,345.00 + * + * @param {String} sign */ - exports._cleanup = function () { - // remove reference from data ob - this._data.__ob__.removeVm(this) - this._data = - this._watchers = - this._userWatchers = - this._watcherList = - this.$el = - this.$parent = - this.$root = - this._children = - this._directives = null - // call the last hook... - this._isDestroyed = true - this._callHook('destroyed') - // turn off all instance listeners. - this.$off() - } - -/***/ }, -/* 14 */ -/***/ function(module, exports, __webpack_require__) { - - /** - * Check is a string starts with $ or _ - * - * @param {String} str - * @return {Boolean} - */ + var digitsRE = /(\d{3})(?=\d)/g - exports.isReserved = function (str) { - var c = str.charCodeAt(0) - return c === 0x24 || c === 0x5F + exports.currency = function (value, sign) { + value = parseFloat(value) + if (!value && value !== 0) return '' + sign = sign || '$' + var s = Math.floor(Math.abs(value)).toString(), + i = s.length % 3, + h = i > 0 + ? (s.slice(0, i) + (s.length > 3 ? ',' : '')) + : '', + f = '.' + value.toFixed(2).slice(-2) + return (value < 0 ? '-' : '') + + sign + h + s.slice(i).replace(digitsRE, '$1,') + f } /** - * Guard text output, make sure undefined outputs - * empty string + * 'item' => 'items' * - * @param {*} value - * @return {String} + * @params + * an array of strings corresponding to + * the single, double, triple ... forms of the word to + * be pluralized. When the number to be pluralized + * exceeds the length of the args, it will use the last + * entry in the array. + * + * e.g. ['single', 'double', 'triple', 'multiple'] */ - exports.toString = function (value) { - return value == null - ? '' - : value.toString() + exports.pluralize = function (value) { + var args = _.toArray(arguments, 1) + return args.length > 1 + ? (args[value % 10 - 1] || args[args.length - 1]) + : (args[0] + (value === 1 ? '' : 's')) } /** - * Check and convert possible numeric numbers before - * setting back to data + * A special filter that takes a handler function, + * wraps it so it only gets triggered on specific + * keypresses. v-on only. * - * @param {*} value - * @return {*|Number} + * @param {String} key */ - exports.toNumber = function (value) { - return ( - isNaN(value) || - value === null || - typeof value === 'boolean' - ) ? value - : Number(value) + var keyCodes = { + enter : 13, + tab : 9, + 'delete' : 46, + up : 38, + left : 37, + right : 39, + down : 40, + esc : 27 } - /** - * Strip quotes from a string - * - * @param {String} str - * @return {String | false} - */ - - exports.stripQuotes = function (str) { - var a = str.charCodeAt(0) - var b = str.charCodeAt(str.length - 1) - return a === b && (a === 0x22 || a === 0x27) - ? str.slice(1, -1) - : false + exports.key = function (handler, key) { + if (!handler) return + var code = keyCodes[key] + if (!code) { + code = parseInt(key, 10) + } + return function (e) { + if (e.keyCode === code) { + return handler.call(this, e) + } + } } + // expose keycode hash + exports.key.keyCodes = keyCodes + /** - * Camelize a hyphen-delmited string. - * - * @param {String} str - * @return {String} + * Install special array filters */ - var camelRE = /[-_](\w)/g - var capitalCamelRE = /(?:^|[-_])(\w)/g + _.extend(exports, __webpack_require__(48)) - exports.camelize = function (str, cap) { - var RE = cap ? capitalCamelRE : camelRE - return str.replace(RE, function (_, c) { - return c ? c.toUpperCase () : '' - }) - } +/***/ }, +/* 14 */ +/***/ function(module, exports, __webpack_require__) { + + var _ = __webpack_require__(11) + var extend = _.extend /** - * Simple bind, faster than native + * Option overwriting strategies are functions that handle + * how to merge a parent option value and a child option + * value into the final value. * - * @param {Function} fn - * @param {Object} ctx - * @return {Function} + * All strategy functions follow the same signature: + * + * @param {*} parentVal + * @param {*} childVal + * @param {Vue} [vm] */ - exports.bind = function (fn, ctx) { - return function () { - return fn.apply(ctx, arguments) - } - } + var strats = Object.create(null) /** - * Convert an Array-like object to a real Array. - * - * @param {Array-like} list - * @param {Number} [start] - start index - * @return {Array} + * Helper that recursively merges two data objects together. */ - exports.toArray = function (list, start) { - start = start || 0 - var i = list.length - start - var ret = new Array(i) - while (i--) { - ret[i] = list[i + start] + function mergeData (to, from) { + var key, toVal, fromVal + for (key in from) { + toVal = to[key] + fromVal = from[key] + if (!to.hasOwnProperty(key)) { + to.$add(key, fromVal) + } else if (_.isObject(toVal) && _.isObject(fromVal)) { + mergeData(toVal, fromVal) + } } - return ret + return to } /** - * Mix properties into target object. - * - * @param {Object} to - * @param {Object} from + * Data */ - exports.extend = function (to, from) { - for (var key in from) { - to[key] = from[key] + 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') { + _.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 { + // instance merge, return raw object + 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 + } } - 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} + * El */ - exports.isObject = function (obj) { - return obj && typeof obj === 'object' + strats.el = function (parentVal, childVal, vm) { + if (!vm && childVal && typeof childVal !== 'function') { + _.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 } /** - * Strict object type check. Only returns true - * for plain JavaScript objects. - * - * @param {*} obj - * @return {Boolean} + * Hooks and param attributes are merged as arrays. */ - var toString = Object.prototype.toString - exports.isPlainObject = function (obj) { - return toString.call(obj) === '[object Object]' + strats.created = + strats.ready = + strats.attached = + strats.detached = + strats.beforeCompile = + strats.compiled = + strats.beforeDestroy = + strats.destroyed = + strats.paramAttributes = function (parentVal, childVal) { + return childVal + ? parentVal + ? parentVal.concat(childVal) + : _.isArray(childVal) + ? childVal + : [childVal] + : parentVal } /** - * Array type check. + * Assets * - * @param {*} obj - * @return {Boolean} + * When a vm is present (instance creation), we need to do + * a three-way merge between constructor options, instance + * options and parent options. */ - exports.isArray = function (obj) { - return Array.isArray(obj) + strats.directives = + strats.filters = + strats.partials = + strats.transitions = + strats.components = function (parentVal, childVal, vm, key) { + var ret = Object.create( + vm && vm.$parent + ? vm.$parent.$options[key] + : _.Vue.options[key] + ) + if (parentVal) { + var keys = Object.keys(parentVal) + var i = keys.length + var field + while (i--) { + field = keys[i] + ret[field] = parentVal[field] + } + } + if (childVal) extend(ret, childVal) + return ret } /** - * Define a non-enumerable property + * Events & Watchers. * - * @param {Object} obj - * @param {String} key - * @param {*} val - * @param {Boolean} [enumerable] + * Events & watchers hashes should not overwrite one + * another, so we merge them as arrays. */ - exports.define = function (obj, key, val, enumerable) { - Object.defineProperty(obj, key, { - value : val, - enumerable : !!enumerable, - writable : true, - configurable : true - }) + 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 } -/***/ }, -/* 15 */ -/***/ function(module, exports, __webpack_require__) { - /** - * Can we use __proto__? - * - * @type {Boolean} + * Other object hashes. */ - exports.hasProto = '__proto__' in {} + strats.methods = + strats.computed = function (parentVal, childVal) { + if (!childVal) return parentVal + if (!parentVal) return childVal + var ret = Object.create(parentVal) + extend(ret, childVal) + return ret + } /** - * Indicates we have a window - * - * @type {Boolean} + * Default strategy. */ - var toString = Object.prototype.toString - var inBrowser = exports.inBrowser = - typeof window !== 'undefined' && - toString.call(window) !== '[object Object]' + var defaultStrat = function (parentVal, childVal) { + return childVal === undefined + ? parentVal + : childVal + } /** - * Defer a task to the start of the next event loop + * Make sure component options get converted to actual + * constructors. * - * @param {Function} cb - * @param {Object} ctx + * @param {Object} components */ - var defer = inBrowser - ? (window.requestAnimationFrame || - window.webkitRequestAnimationFrame || - setTimeout) - : setTimeout - - exports.nextTick = function (cb, ctx) { - if (ctx) { - defer(function () { cb.call(ctx) }, 0) - } else { - defer(cb, 0) + function guardComponents (components) { + if (components) { + var def + for (var key in components) { + def = components[key] + if (_.isPlainObject(def)) { + def.name = key + components[key] = _.Vue.extend(def) + } + } } } /** - * Detect if we are in IE9... + * Merge two option objects into a new one. + * Core utility used in both instantiation and inheritance. * - * @type {Boolean} - */ - - exports.isIE9 = - inBrowser && - navigator.userAgent.indexOf('MSIE 9.0') > 0 - - /** - * Sniff transition/animation events + * @param {Object} parent + * @param {Object} child + * @param {Vue} [vm] - if vm is present, indicates this is + * an instantiation merge. */ - if (inBrowser && !exports.isIE9) { - var isWebkitTrans = - window.ontransitionend === undefined && - window.onwebkittransitionend !== undefined - var isWebkitAnim = - window.onanimationend === undefined && - window.onwebkitanimationend !== undefined - exports.transitionProp = isWebkitTrans - ? 'WebkitTransition' - : 'transition' - exports.transitionEndEvent = isWebkitTrans - ? 'webkitTransitionEnd' - : 'transitionend' - exports.animationProp = isWebkitAnim - ? 'WebkitAnimation' - : 'animation' - exports.animationEndEvent = isWebkitAnim - ? 'webkitAnimationEnd' - : 'animationend' + module.exports = function mergeOptions (parent, child, vm) { + guardComponents(child.components) + 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) { + merge(key) + } + for (key in child) { + if (!(parent.hasOwnProperty(key))) { + merge(key) + } + } + function merge (key) { + var strat = strats[key] || defaultStrat + options[key] = strat(parent[key], child[key], vm, key) + } + return options } /***/ }, -/* 16 */ +/* 15 */ /***/ function(module, exports, __webpack_require__) { - var config = __webpack_require__(20) + module.exports = { - /** - * Check if a node is in the document. - * - * @param {Node} node - * @return {Boolean} - */ + /** + * The prefix to look for when parsing directives. + * + * @type {String} + */ - var doc = - typeof document !== 'undefined' && - document.documentElement + prefix: 'v-', - exports.inDoc = function (node) { - return doc && doc.contains(node) - } + /** + * Whether to print debug messages. + * Also enables stack trace for warnings. + * + * @type {Boolean} + */ - /** - * Extract an attribute from a node. - * - * @param {Node} node - * @param {String} attr - */ + debug: false, - exports.attr = function (node, attr) { - attr = config.prefix + attr - var val = node.getAttribute(attr) - if (val !== null) { - node.removeAttribute(attr) - } - return val - } + /** + * Whether to suppress warnings. + * + * @type {Boolean} + */ - /** - * Insert el before target - * - * @param {Element} el - * @param {Element} target - */ + silent: false, - exports.before = function (el, target) { - target.parentNode.insertBefore(el, target) - } + /** + * Whether allow observer to alter data objects' + * __proto__. + * + * @type {Boolean} + */ - /** - * Insert el after target - * - * @param {Element} el - * @param {Element} target - */ + proto: true, - exports.after = function (el, target) { - if (target.nextSibling) { - exports.before(el, target.nextSibling) - } else { - target.parentNode.appendChild(el) - } - } + /** + * Whether to parse mustache tags in templates. + * + * @type {Boolean} + */ - /** - * Remove el from DOM - * - * @param {Element} el - */ + interpolate: true, - exports.remove = function (el) { - el.parentNode.removeChild(el) - } + /** + * Whether to use async rendering. + */ - /** - * Prepend el to target - * - * @param {Element} el - * @param {Element} target - */ + async: true, + + /** + * Whether to warn against errors caught when evaluating + * expressions. + */ + + warnExpressionErrors: true, + + /** + * Internal flag to indicate the delimiters have been + * changed. + * + * @type {Boolean} + */ + + _delimitersChanged: true - exports.prepend = function (el, target) { - if (target.firstChild) { - exports.before(el, target.firstChild) - } else { - target.appendChild(el) - } } /** - * Replace target with el + * Interpolation delimiters. + * We need to mark the changed flag so that the text parser + * knows it needs to recompile the regex. * - * @param {Element} target - * @param {Element} el + * @type {Array} */ - exports.replace = function (target, el) { - var parent = target.parentNode - if (parent) { - parent.replaceChild(el, target) + var delimiters = ['{{', '}}'] + Object.defineProperty(module.exports, 'delimiters', { + get: function () { + return delimiters + }, + set: function (val) { + delimiters = val + this._delimitersChanged = true } - } + }) + +/***/ }, +/* 16 */ +/***/ function(module, exports, __webpack_require__) { + + var _ = __webpack_require__(11) + var config = __webpack_require__(15) + var textParser = __webpack_require__(19) + var dirParser = __webpack_require__(21) + var templateParser = __webpack_require__(20) /** - * Copy attributes from one element to another. + * Compile a template and return a reusable composite link + * function, which recursively contains more link functions + * inside. This top level compile function should only be + * called on instance root nodes. * - * @param {Element} from - * @param {Element} to + * When the `asParent` flag is true, this means we are doing + * a partial compile for a component's parent scope markup + * (See #502). This could **only** be triggered during + * compilation of `v-component`, and we need to skip v-with, + * v-ref & v-component in this situation. + * + * @param {Element|DocumentFragment} el + * @param {Object} options + * @param {Boolean} partial + * @param {Boolean} asParent - compiling a component + * container as its parent. + * @return {Function} */ - exports.copyAttributes = function (from, to) { - if (from.hasAttributes()) { - var attrs = from.attributes - for (var i = 0, l = attrs.length; i < l; i++) { - var attr = attrs[i] - to.setAttribute(attr.name, attr.value) + module.exports = function compile (el, options, partial, asParent) { + var params = !partial && options.paramAttributes + var paramsLinkFn = params + ? compileParamAttributes(el, params, options) + : null + var nodeLinkFn = el instanceof DocumentFragment + ? null + : compileNode(el, options, asParent) + var childLinkFn = + !(nodeLinkFn && nodeLinkFn.terminal) && + el.tagName !== 'SCRIPT' && + el.hasChildNodes() + ? compileNodeList(el.childNodes, options) + : null + + /** + * A linker function to be called on a already compiled + * piece of DOM, which instantiates all directive + * instances. + * + * @param {Vue} vm + * @param {Element|DocumentFragment} el + * @return {Function|undefined} + */ + + return function link (vm, el) { + var originalDirCount = vm._directives.length + if (paramsLinkFn) paramsLinkFn(vm, el) + // cache childNodes before linking parent, fix #657 + var childNodes = _.toArray(el.childNodes) + if (nodeLinkFn) nodeLinkFn(vm, el) + if (childLinkFn) childLinkFn(vm, childNodes) + + /** + * If this is a partial compile, the linker function + * returns an unlink function that tearsdown all + * directives instances generated during the partial + * linking. + */ + + if (partial) { + var dirs = vm._directives.slice(originalDirCount) + return function unlink () { + var i = dirs.length + while (i--) { + dirs[i]._teardown() + } + i = vm._directives.indexOf(dirs[0]) + vm._directives.splice(i, dirs.length) + } } } } /** - * Add event listener shorthand. + * Compile a node and return a nodeLinkFn based on the + * node type. * - * @param {Element} el - * @param {String} event - * @param {Function} cb + * @param {Node} node + * @param {Object} options + * @param {Boolean} asParent + * @return {Function|undefined} */ - exports.on = function (el, event, cb) { - el.addEventListener(event, cb) + function compileNode (node, options, asParent) { + var type = node.nodeType + if (type === 1 && node.tagName !== 'SCRIPT') { + return compileElement(node, options, asParent) + } else if (type === 3 && config.interpolate) { + return compileTextNode(node, options) + } } /** - * Remove event listener shorthand. + * Compile an element and return a nodeLinkFn. * * @param {Element} el - * @param {String} event - * @param {Function} cb + * @param {Object} options + * @param {Boolean} asParent + * @return {Function|null} */ - exports.off = function (el, event, cb) { - el.removeEventListener(event, cb) + function compileElement (el, options, asParent) { + var linkFn, tag, component + // check custom element component, but only on non-root + if (!asParent && !el.__vue__) { + tag = el.tagName.toLowerCase() + component = + tag.indexOf('-') > 0 && + options.components[tag] + if (component) { + el.setAttribute(config.prefix + 'component', tag) + } + } + if (component || el.hasAttributes()) { + // check terminal direcitves + if (!asParent) { + linkFn = checkTerminalDirectives(el, options) + } + // if not terminal, build normal link function + if (!linkFn) { + var dirs = collectDirectives(el, options, asParent) + linkFn = dirs.length + ? makeDirectivesLinkFn(dirs) + : null + } + } + // if the element is a textarea, we need to interpolate + // its content on initial render. + if (el.tagName === 'TEXTAREA') { + var realLinkFn = linkFn + linkFn = function (vm, el) { + el.value = vm.$interpolate(el.value) + if (realLinkFn) realLinkFn(vm, el) + } + linkFn.terminal = true + } + return linkFn } /** - * Add class with compatibility for IE & SVG + * Build a multi-directive link function. * - * @param {Element} el - * @param {Strong} cls + * @param {Array} directives + * @return {Function} directivesLinkFn */ - exports.addClass = function (el, cls) { - if (el.classList) { - el.classList.add(cls) - } else { - var cur = ' ' + (el.getAttribute('class') || '') + ' ' - if (cur.indexOf(' ' + cls + ' ') < 0) { - el.setAttribute('class', (cur + cls).trim()) + function makeDirectivesLinkFn (directives) { + return function directivesLinkFn (vm, el) { + // reverse apply because it's sorted low to high + var i = directives.length + var dir, j, k + while (i--) { + dir = directives[i] + if (dir._link) { + // custom link fn + dir._link(vm, el) + } else { + k = dir.descriptors.length + for (j = 0; j < k; j++) { + vm._bindDir(dir.name, el, + dir.descriptors[j], dir.def) + } + } } } } /** - * Remove class with compatibility for IE & SVG + * Compile a textNode and return a nodeLinkFn. * - * @param {Element} el - * @param {Strong} cls + * @param {TextNode} node + * @param {Object} options + * @return {Function|null} textNodeLinkFn */ - exports.removeClass = function (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, ' ') - } - el.setAttribute('class', cur.trim()) + function compileTextNode (node, options) { + var tokens = textParser.parse(node.nodeValue) + if (!tokens) { + return null + } + var frag = document.createDocumentFragment() + var el, token + for (var i = 0, l = tokens.length; i < l; i++) { + token = tokens[i] + el = token.tag + ? processTextToken(token, options) + : document.createTextNode(token.value) + frag.appendChild(el) } + return makeTextNodeLinkFn(tokens, frag, options) } /** - * Extract raw content inside an element into a temporary - * container div + * Process a single text token. * - * @param {Element} el - * @return {Element} + * @param {Object} token + * @param {Object} options + * @return {Node} */ - exports.extractContent = function (el) { - var child - var rawContent - if (el.hasChildNodes()) { - rawContent = document.createElement('div') - /* jshint boss:true */ - while (child = el.firstChild) { - rawContent.appendChild(child) + function processTextToken (token, options) { + var el + if (token.oneTime) { + el = document.createTextNode(token.value) + } else { + if (token.html) { + el = document.createComment('v-html') + setTokenType('html') + } else if (token.partial) { + el = document.createComment('v-partial') + setTokenType('partial') + } else { + // IE will clean up empty textNodes during + // frag.cloneNode(true), so we have to give it + // something here... + el = document.createTextNode(' ') + setTokenType('text') } } - return rawContent + function setTokenType (type) { + token.type = type + token.def = options.directives[type] + token.descriptor = dirParser.parse(token.value)[0] + } + return el } -/***/ }, -/* 17 */ -/***/ function(module, exports, __webpack_require__) { - - var _ = __webpack_require__(18) - /** - * Resolve read & write filters for a vm instance. The - * filters descriptor Array comes from the directive parser. - * - * This is extracted into its own utility so it can - * be used in multiple scenarios. + * Build a function that processes a textNode. * - * @param {Vue} vm - * @param {Array} filters - * @param {Object} [target] - * @return {Object} + * @param {Array} tokens + * @param {DocumentFragment} frag */ - exports.resolveFilters = function (vm, filters, target) { - if (!filters) { - return - } - var res = target || {} - // var registry = vm.$options.filters - filters.forEach(function (f) { - var def = vm.$options.filters[f.name] - _.assertAsset(def, 'filter', f.name) - if (!def) return - var args = f.args - var reader, writer - if (typeof def === 'function') { - reader = def - } else { - reader = def.read - writer = def.write - } - if (reader) { - if (!res.read) res.read = [] - res.read.push(function (value) { - return args - ? reader.apply(vm, [value].concat(args)) - : reader.call(vm, value) - }) - } - if (writer) { - if (!res.write) res.write = [] - res.write.push(function (value, oldVal) { - return args - ? writer.apply(vm, [value, oldVal].concat(args)) - : writer.call(vm, value, oldVal) - }) + function makeTextNodeLinkFn (tokens, frag) { + return function textNodeLinkFn (vm, el) { + var fragClone = frag.cloneNode(true) + var childNodes = _.toArray(fragClone.childNodes) + var token, value, node + for (var i = 0, l = tokens.length; i < l; i++) { + token = tokens[i] + value = token.value + if (token.tag) { + node = childNodes[i] + if (token.oneTime) { + value = vm.$eval(value) + if (token.html) { + _.replace(node, templateParser.parse(value, true)) + } else { + node.nodeValue = value + } + } else { + vm._bindDir(token.type, node, + token.descriptor, token.def) + } + } } - }) - return res + _.replace(el, fragClone) + } } /** - * Apply filters to a value + * Compile a node list and return a childLinkFn. * - * @param {*} value - * @param {Array} filters - * @param {Vue} vm - * @param {*} oldVal - * @return {*} + * @param {NodeList} nodeList + * @param {Object} options + * @return {Function|undefined} */ - exports.applyFilters = function (value, filters, vm, oldVal) { - if (!filters) { - return value - } - for (var i = 0, l = filters.length; i < l; i++) { - value = filters[i].call(vm, value, oldVal) + function compileNodeList (nodeList, options) { + var linkFns = [] + var nodeLinkFn, childLinkFn, node + for (var i = 0, l = nodeList.length; i < l; i++) { + node = nodeList[i] + nodeLinkFn = compileNode(node, options) + childLinkFn = + !(nodeLinkFn && nodeLinkFn.terminal) && + node.tagName !== 'SCRIPT' && + node.hasChildNodes() + ? compileNodeList(node.childNodes, options) + : null + linkFns.push(nodeLinkFn, childLinkFn) } - return value + return linkFns.length + ? makeChildLinkFn(linkFns) + : null } -/***/ }, -/* 18 */ -/***/ function(module, exports, __webpack_require__) { - - var config = __webpack_require__(20) - /** - * Enable debug utilities. The enableDebug() function and - * all _.log() & _.warn() calls will be dropped in the - * minified production build. + * Make a child link function for a node's childNodes. + * + * @param {Array} linkFns + * @return {Function} childLinkFn */ - enableDebug() - - function enableDebug () { - var hasConsole = typeof console !== 'undefined' - - /** - * Log a message. - * - * @param {String} msg - */ - - exports.log = function (msg) { - if (hasConsole && config.debug) { - console.log('[Vue info]: ' + msg) - } - } - - /** - * We've got a problem here. - * - * @param {String} msg - */ - - exports.warn = function (msg) { - if (hasConsole && !config.silent) { - console.warn('[Vue warn]: ' + msg) - /* istanbul ignore if */ - if (config.debug) { - /* jshint debug: true */ - debugger - } else { - console.log( - 'Set `Vue.config.debug = true` to enable debug mode.' - ) + function makeChildLinkFn (linkFns) { + return function childLinkFn (vm, nodes) { + var node, nodeLinkFn, childrenLinkFn + for (var i = 0, n = 0, l = linkFns.length; i < l; n++) { + node = nodes[n] + nodeLinkFn = linkFns[i++] + childrenLinkFn = linkFns[i++] + // cache childNodes before linking parent, fix #657 + var childNodes = _.toArray(node.childNodes) + if (nodeLinkFn) { + nodeLinkFn(vm, node) + } + if (childrenLinkFn) { + childrenLinkFn(vm, childNodes) } - } - } - - /** - * Assert asset exists - */ - - exports.assertAsset = function (val, type, id) { - if (!val) { - exports.warn('Failed to resolve ' + type + ': ' + id) } } } -/***/ }, -/* 19 */ -/***/ function(module, exports, __webpack_require__) { - - var _ = __webpack_require__(1) - var extend = _.extend - /** - * 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: + * Compile param attributes on a root element and return + * a paramAttributes link function. * - * @param {*} parentVal - * @param {*} childVal - * @param {Vue} [vm] + * @param {Element} el + * @param {Array} attrs + * @param {Object} options + * @return {Function} paramsLinkFn */ - var strats = 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 (!to.hasOwnProperty(key)) { - to.$add(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') { + function compileParamAttributes (el, attrs, options) { + var params = [] + var i = attrs.length + var name, value, param + while (i--) { + name = attrs[i] + if (/[A-Z]/.test(name)) { _.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) + 'You seem to be using camelCase for a paramAttribute, ' + + 'but HTML doesn\'t differentiate between upper and ' + + 'lower case. You should use hyphen-delimited ' + + 'attribute names. For more info see ' + + 'http://vuejs.org/api/options.html#paramAttributes' ) } - } else { - // instance merge, return raw object - 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 + value = el.getAttribute(name) + if (value !== null) { + param = { + name: name, + value: value + } + var tokens = textParser.parse(value) + if (tokens) { + el.removeAttribute(name) + if (tokens.length > 1) { + _.warn( + 'Invalid param attribute binding: "' + + name + '="' + value + '"' + + '\nDon\'t mix binding tags with plain text ' + + 'in param attribute bindings.' + ) + continue + } else { + param.dynamic = true + param.value = tokens[0].value + } + } + params.push(param) } } + return makeParamsLinkFn(params, options) } /** - * El + * Build a function that applies param attributes to a vm. + * + * @param {Array} params + * @param {Object} options + * @return {Function} paramsLinkFn */ - strats.el = function (parentVal, childVal, vm) { - if (!vm && childVal && typeof childVal !== 'function') { - _.warn( - 'The "el" option should be a function ' + - 'that returns a per-instance value in component ' + - 'definitions.' - ) - return + var dataAttrRE = /^data-/ + + function makeParamsLinkFn (params, options) { + var def = options.directives['with'] + return function paramsLinkFn (vm, el) { + var i = params.length + var param, path + while (i--) { + param = params[i] + // params could contain dashes, which will be + // interpreted as minus calculations by the parser + // so we need to wrap the path here + path = _.camelize(param.name.replace(dataAttrRE, '')) + if (param.dynamic) { + // dynamic param attribtues are bound as v-with. + // we can directly duck the descriptor here beacuse + // param attributes cannot use expressions or + // filters. + vm._bindDir('with', el, { + arg: path, + expression: param.value + }, def) + } else { + // just set once + vm.$set(path, param.value) + } + } } - 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. + * Check an element for terminal directives in fixed order. + * If it finds one, return a terminal link function. + * + * @param {Element} el + * @param {Object} options + * @return {Function} terminalLinkFn */ - strats.created = - strats.ready = - strats.attached = - strats.detached = - strats.beforeCompile = - strats.compiled = - strats.beforeDestroy = - strats.destroyed = - strats.paramAttributes = function (parentVal, childVal) { - return childVal - ? parentVal - ? parentVal.concat(childVal) - : _.isArray(childVal) - ? childVal - : [childVal] - : parentVal - } + var terminalDirectives = [ + 'repeat', + 'if', + 'component' + ] - /** - * 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 skip () {} + skip.terminal = true - strats.directives = - strats.filters = - strats.partials = - strats.transitions = - strats.components = function (parentVal, childVal, vm, key) { - var ret = Object.create( - vm && vm.$parent - ? vm.$parent.$options[key] - : _.Vue.options[key] - ) - if (parentVal) { - var keys = Object.keys(parentVal) - var i = keys.length - var field - while (i--) { - field = keys[i] - ret[field] = parentVal[field] + function checkTerminalDirectives (el, options) { + if (_.attr(el, 'pre') !== null) { + return skip + } + var value, dirName + /* jshint boss: true */ + for (var i = 0; i < 3; i++) { + dirName = terminalDirectives[i] + if (value = _.attr(el, dirName)) { + return makeTeriminalLinkFn(el, dirName, value, options) } } - if (childVal) extend(ret, childVal) - return ret } /** - * Events & Watchers. + * Build a link function for a terminal directive. * - * Events & watchers hashes should not overwrite one - * another, so we merge them as arrays. + * @param {Element} el + * @param {String} dirName + * @param {String} value + * @param {Object} options + * @return {Function} terminalLinkFn */ - 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] - ret[key] = parent - ? parent.concat(child) - : [child] + function makeTeriminalLinkFn (el, dirName, value, options) { + var descriptor = dirParser.parse(value)[0] + var def = options.directives[dirName] + var terminalLinkFn = function (vm, el) { + vm._bindDir(dirName, el, descriptor, def) } - return ret - } - - /** - * Other object hashes. - */ - - strats.methods = - strats.computed = function (parentVal, childVal) { - if (!childVal) return parentVal - if (!parentVal) return childVal - var ret = Object.create(parentVal) - extend(ret, childVal) - return ret + terminalLinkFn.terminal = true + return terminalLinkFn } /** - * Default strategy. + * Collect the directives on an element. + * + * @param {Element} el + * @param {Object} options + * @param {Boolean} asParent + * @return {Array} */ - var defaultStrat = function (parentVal, childVal) { - return childVal === undefined - ? parentVal - : childVal - } - - /** - * Make sure component options get converted to actual - * constructors. - * - * @param {Object} components - */ - - function guardComponents (components) { - if (components) { - var def - for (var key in components) { - def = components[key] - if (_.isPlainObject(def)) { - def.name = key - components[key] = _.Vue.extend(def) + function collectDirectives (el, options, asParent) { + var attrs = _.toArray(el.attributes) + var i = attrs.length + var dirs = [] + var attr, attrName, dir, dirName, dirDef + while (i--) { + attr = attrs[i] + attrName = attr.name + if (attrName.indexOf(config.prefix) === 0) { + dirName = attrName.slice(config.prefix.length) + if (asParent && + (dirName === 'with' || + dirName === 'component')) { + continue + } + dirDef = options.directives[dirName] + _.assertAsset(dirDef, 'directive', dirName) + if (dirDef) { + dirs.push({ + name: dirName, + descriptors: dirParser.parse(attr.value), + def: dirDef + }) + } + } else if (config.interpolate) { + dir = collectAttrDirective(el, attrName, attr.value, + options) + if (dir) { + dirs.push(dir) } } } + // sort by priority, LOW to HIGH + dirs.sort(directiveComparator) + return dirs } /** - * Merge two option objects into a new one. - * Core utility used in both instantiation and inheritance. + * Check an attribute for potential dynamic bindings, + * and return a directive object. * - * @param {Object} parent - * @param {Object} child - * @param {Vue} [vm] - if vm is present, indicates this is - * an instantiation merge. + * @param {Element} el + * @param {String} name + * @param {String} value + * @param {Object} options + * @return {Object} */ - module.exports = function mergeOptions (parent, child, vm) { - guardComponents(child.components) - 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) { - merge(key) + function collectAttrDirective (el, name, value, options) { + if (options._skipAttrs && + options._skipAttrs.indexOf(name) > -1) { + return } - for (key in child) { - if (!(parent.hasOwnProperty(key))) { - merge(key) + var tokens = textParser.parse(value) + if (tokens) { + var def = options.directives.attr + var i = tokens.length + var allOneTime = true + while (i--) { + var token = tokens[i] + if (token.tag && !token.oneTime) { + allOneTime = false + } + } + return { + def: def, + _link: allOneTime + ? function (vm, el) { + el.setAttribute(name, vm.$interpolate(value)) + } + : function (vm, el) { + var value = textParser.tokensToExp(tokens, vm) + var desc = dirParser.parse(name + ':' + value)[0] + vm._bindDir('attr', el, desc, def) + } } } - function merge (key) { - var strat = strats[key] || defaultStrat - options[key] = strat(parent[key], child[key], vm, key) - } - return options - } - -/***/ }, -/* 20 */ -/***/ function(module, exports, __webpack_require__) { - - module.exports = { - - /** - * The prefix to look for when parsing directives. - * - * @type {String} - */ - - prefix: 'v-', - - /** - * Whether to print debug messages. - * Also enables stack trace for warnings. - * - * @type {Boolean} - */ - - debug: false, - - /** - * Whether to suppress warnings. - * - * @type {Boolean} - */ - - silent: false, - - /** - * Whether allow observer to alter data objects' - * __proto__. - * - * @type {Boolean} - */ - - proto: true, - - /** - * Whether to parse mustache tags in templates. - * - * @type {Boolean} - */ - - interpolate: true, - - /** - * Whether to use async rendering. - */ - - async: true, - - /** - * Internal flag to indicate the delimiters have been - * changed. - * - * @type {Boolean} - */ - - _delimitersChanged: true - } /** - * Interpolation delimiters. - * We need to mark the changed flag so that the text parser - * knows it needs to recompile the regex. + * Directive priority sort comparator * - * @type {Array} + * @param {Object} a + * @param {Object} b */ - var delimiters = ['{{', '}}'] - Object.defineProperty(module.exports, 'delimiters', { - get: function () { - return delimiters - }, - set: function (val) { - delimiters = val - this._delimitersChanged = true - } - }) + function directiveComparator (a, b) { + a = a.def.priority || 0 + b = b.def.priority || 0 + return a > b ? 1 : -1 + } /***/ }, -/* 21 */ +/* 17 */ /***/ function(module, exports, __webpack_require__) { - var _ = __webpack_require__(1) - var config = __webpack_require__(20) - var Observer = __webpack_require__(51) - var expParser = __webpack_require__(48) - var batcher = __webpack_require__(52) - var uid = 0 + var _ = __webpack_require__(11) + var templateParser = __webpack_require__(20) /** - * 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. + * Process an element or a DocumentFragment based on a + * instance option object. This allows us to transclude + * a template node/fragment before the instance is created, + * so the processed fragment can then be cloned and reused + * in v-repeat. * - * @param {Vue} vm - * @param {String} expression - * @param {Function} cb + * @param {Element} el * @param {Object} options - * - {Array} filters - * - {Boolean} twoWay - * - {Boolean} deep - * - {Boolean} user - * @constructor + * @return {Element|DocumentFragment} */ - function Watcher (vm, expression, cb, options) { - this.vm = vm - vm._watcherList.push(this) - this.expression = expression - this.cbs = [cb] - this.id = ++uid // uid for batching - this.active = true - options = options || {} - this.deep = options.deep - this.user = options.user - this.deps = Object.create(null) - // setup filters if any. - // We delegate directive filters here to the watcher - // because they need to be included in the dependency - // collection process. - if (options.filters) { - this.readFilters = options.filters.read - this.writeFilters = options.filters.write + module.exports = function transclude (el, options) { + // for template tags, what we want is its content as + // a documentFragment (for block instances) + if (el.tagName === 'TEMPLATE') { + el = templateParser.parse(el) } - // parse expression for getter/setter - var res = expParser.parse(expression, options.twoWay) - this.getter = res.get - this.setter = res.set - this.value = this.get() + if (options && options.template) { + el = transcludeTemplate(el, options) + } + if (el instanceof DocumentFragment) { + _.prepend(document.createComment('v-start'), el) + el.appendChild(document.createComment('v-end')) + } + return el } - var p = Watcher.prototype - /** - * Add a dependency to this directive. + * Process the template option. + * If the replace option is true this will swap the $el. * - * @param {Dep} dep - */ - - p.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) + * @param {Element} el + * @param {Object} options + * @return {Element|DocumentFragment} + */ + + function transcludeTemplate (el, options) { + var template = options.template + var frag = templateParser.parse(template, true) + if (!frag) { + _.warn('Invalid template option: ' + template) + } else { + var rawContent = options._content || _.extractContent(el) + if (options.replace) { + if (frag.childNodes.length > 1) { + transcludeContent(frag, rawContent) + // TODO: store directives on placeholder node + // and compile it somehow + // probably only check for v-with, v-ref & paramAttributes + return frag + } else { + var replacer = frag.firstChild + _.copyAttributes(el, replacer) + transcludeContent(replacer, rawContent) + return replacer + } + } else { + el.appendChild(frag) + transcludeContent(el, rawContent) + return el } } } /** - * Evaluate the getter, and re-collect dependencies. + * Resolve insertion points mimicking the behavior + * of the Shadow DOM spec: + * + * http://w3c.github.io/webcomponents/spec/shadow/#insertion-points + * + * @param {Element|DocumentFragment} el + * @param {Element} raw */ - p.get = function () { - this.beforeGet() - var vm = this.vm - var value - try { - value = this.getter.call(vm, vm) - } catch (e) { - _.warn( - 'Error when evaluating expression "' + - this.expression + '":\n ' + e - ) + function transcludeContent (el, raw) { + var outlets = getOutlets(el) + var i = outlets.length + if (!i) return + var outlet, select, selected, j, main + // first pass, collect corresponding content + // for each outlet. + while (i--) { + outlet = outlets[i] + if (raw) { + select = outlet.getAttribute('select') + if (select) { // select content + selected = raw.querySelectorAll(select) + outlet.content = _.toArray( + selected.length + ? selected + : outlet.childNodes + ) + } else { // default content + main = outlet + } + } else { // fallback content + outlet.content = _.toArray(outlet.childNodes) + } } - // "touch" every property so they are all tracked as - // dependencies for deep watching - if (this.deep) { - traverse(value) + // second pass, actually insert the contents + for (i = 0, j = outlets.length; i < j; i++) { + outlet = outlets[i] + if (outlet !== main) { + insertContentAt(outlet, outlet.content) + } + } + // finally insert the main content + if (main) { + insertContentAt(main, _.toArray(raw.childNodes)) } - value = _.applyFilters(value, this.readFilters, vm) - this.afterGet() - return value } /** - * Set the corresponding value with the setter. + * Get outlets from the element/list * - * @param {*} value + * @param {Element|Array} el + * @return {Array} */ - p.set = function (value) { - var vm = this.vm - value = _.applyFilters( - value, this.writeFilters, vm, this.value - ) - try { - this.setter.call(vm, vm, value) - } catch (e) { - _.warn( - 'Error when evaluating setter "' + - this.expression + '":\n ' + e - ) - } + var concat = [].concat + function getOutlets (el) { + return _.isArray(el) + ? concat.apply([], el.map(getOutlets)) + : el.querySelectorAll + ? _.toArray(el.querySelectorAll('content')) + : [] } /** - * Prepare for dependency collection. + * Insert an array of nodes at outlet, + * then remove the outlet. + * + * @param {Element} outlet + * @param {Array} contents */ - p.beforeGet = function () { - Observer.target = this - this.newDeps = {} + function insertContentAt (outlet, contents) { + // not using util DOM methods here because + // parentNode can be cached + var parent = outlet.parentNode + for (var i = 0, j = contents.length; i < j; i++) { + parent.insertBefore(contents[i], outlet) + } + parent.removeChild(outlet) } - /** - * Clean up for dependency collection. - */ +/***/ }, +/* 18 */ +/***/ function(module, exports, __webpack_require__) { - p.afterGet = function () { - Observer.target = null - for (var id in this.deps) { - if (!this.newDeps[id]) { - this.deps[id].removeSub(this) - } - } - this.deps = this.newDeps - } + var _ = __webpack_require__(11) + var Cache = __webpack_require__(52) + var pathCache = new Cache(1000) + var identRE = /^[$_a-zA-Z]+[\w$]*$/ /** - * Subscriber interface. - * Will be called when a dependency changes. + * Path-parsing algorithm scooped from Polymer/observe-js */ - p.update = function () { - if (!config.async || config.debug) { - this.run() - } else { - batcher.push(this) - } - } + var pathStateMachine = { + 'beforePath': { + 'ws': ['beforePath'], + 'ident': ['inIdent', 'append'], + '[': ['beforeElement'], + 'eof': ['afterPath'] + }, - /** - * Batcher job interface. - * Will be called by the batcher. - */ + 'inPath': { + 'ws': ['inPath'], + '.': ['beforeIdent'], + '[': ['beforeElement'], + 'eof': ['afterPath'] + }, - p.run = function () { - if (this.active) { - var value = this.get() - if ( - (typeof value === 'object' && value !== null) || - value !== this.value - ) { - var oldValue = this.value - this.value = value - var cbs = this.cbs - for (var i = 0, l = cbs.length; i < l; i++) { - cbs[i](value, oldValue) - // if a callback also removed other callbacks, - // we need to adjust the loop accordingly. - var removed = l - cbs.length - if (removed) { - i -= removed - l -= removed - } - } - } + 'beforeIdent': { + 'ws': ['beforeIdent'], + 'ident': ['inIdent', 'append'] + }, + + 'inIdent': { + 'ident': ['inIdent', 'append'], + '0': ['inIdent', 'append'], + 'number': ['inIdent', 'append'], + 'ws': ['inPath', 'push'], + '.': ['beforeIdent', 'push'], + '[': ['beforeElement', 'push'], + 'eof': ['afterPath', 'push'] + }, + + 'beforeElement': { + 'ws': ['beforeElement'], + '0': ['afterZero', 'append'], + 'number': ['inIndex', 'append'], + "'": ['inSingleQuote', 'append', ''], + '"': ['inDoubleQuote', 'append', ''] + }, + + 'afterZero': { + 'ws': ['afterElement', 'push'], + ']': ['inPath', 'push'] + }, + + 'inIndex': { + '0': ['inIndex', 'append'], + 'number': ['inIndex', 'append'], + 'ws': ['afterElement'], + ']': ['inPath', 'push'] + }, + + 'inSingleQuote': { + "'": ['afterElement'], + 'eof': 'error', + 'else': ['inSingleQuote', 'append'] + }, + + 'inDoubleQuote': { + '"': ['afterElement'], + 'eof': 'error', + 'else': ['inDoubleQuote', 'append'] + }, + + 'afterElement': { + 'ws': ['afterElement'], + ']': ['inPath', 'push'] } } + function noop () {} + /** - * Add a callback. + * Determine the type of a character in a keypath. * - * @param {Function} cb + * @param {Char} char + * @return {String} type */ - p.addCb = function (cb) { - this.cbs.push(cb) - } - - /** - * Remove a callback. - * - * @param {Function} cb - */ + function getPathCharType (char) { + if (char === undefined) { + return 'eof' + } - p.removeCb = function (cb) { - var cbs = this.cbs - if (cbs.length > 1) { - var i = cbs.indexOf(cb) - if (i > -1) { - cbs.splice(i, 1) - } - } else if (cb === cbs[0]) { - this.teardown() + var code = char.charCodeAt(0) + + switch(code) { + case 0x5B: // [ + case 0x5D: // ] + case 0x2E: // . + case 0x22: // " + case 0x27: // ' + case 0x30: // 0 + return char + + 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' } - } - /** - * Remove self from all dependencies' subcriber list. - */ + // a-z, A-Z + if ((0x61 <= code && code <= 0x7A) || + (0x41 <= code && code <= 0x5A)) { + return 'ident' + } - p.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) { - var list = this.vm._watcherList - list.splice(list.indexOf(this)) - } - for (var id in this.deps) { - this.deps[id].removeSub(this) - } - this.active = false - this.vm = this.cbs = this.value = null + // 1-9 + if (0x31 <= code && code <= 0x39) { + return 'number' } - } + return 'else' + } /** - * Recrusively traverse an object to evoke all converted - * getters, so that every nested property inside the object - * is collected as a "deep" dependency. + * Parse a string path into an array of segments + * Todo implement cache * - * @param {Object} obj + * @param {String} path + * @return {Array|undefined} */ - function traverse (obj) { - var key, val, i - for (key in obj) { - val = obj[key] - if (_.isArray(val)) { - i = val.length - while (i--) traverse(val[i]) - } else if (_.isObject(val)) { - traverse(val) + function parsePath (path) { + var keys = [] + var index = -1 + var mode = 'beforePath' + var c, newChar, key, type, transition, action, typeMap + + var actions = { + push: function() { + if (key === undefined) { + return + } + keys.push(key) + key = undefined + }, + append: function() { + if (key === undefined) { + key = newChar + } else { + key += newChar + } } } - } - - module.exports = Watcher - -/***/ }, -/* 22 */ -/***/ function(module, exports, __webpack_require__) { - - var _ = __webpack_require__(1) - - module.exports = { - - bind: function () { - this.attr = this.el.nodeType === 3 - ? 'nodeValue' - : 'textContent' - }, - update: function (value) { - this.el[this.attr] = _.toString(value) + function maybeUnescapeQuote () { + var nextChar = path[index + 1] + if ((mode === 'inSingleQuote' && nextChar === "'") || + (mode === 'inDoubleQuote' && nextChar === '"')) { + index++ + newChar = nextChar + actions.append() + return true + } } - - } -/***/ }, -/* 23 */ -/***/ function(module, exports, __webpack_require__) { + while (mode) { + index++ + c = path[index] - var _ = __webpack_require__(1) - var templateParser = __webpack_require__(46) + if (c === '\\' && maybeUnescapeQuote()) { + continue + } - module.exports = { + type = getPathCharType(c) + typeMap = pathStateMachine[mode] + transition = typeMap[type] || typeMap['else'] || 'error' - bind: function () { - // a comment node means this is a binding for - // {{{ inline unescaped html }}} - if (this.el.nodeType === 8) { - // hold nodes - this.nodes = [] + if (transition === 'error') { + return // parse error } - }, - update: function (value) { - value = _.toString(value) - if (this.nodes) { - this.swap(value) - } else { - this.el.innerHTML = value - } - }, + mode = transition[0] + action = actions[transition[1]] || noop + newChar = transition[2] === undefined + ? c + : transition[2] + action() - swap: function (value) { - // remove old nodes - var i = this.nodes.length - while (i--) { - _.remove(this.nodes[i]) + if (mode === 'afterPath') { + return keys } - // convert new value to a fragment - // do not attempt to retrieve from id selector - var frag = templateParser.parse(value, true, true) - // save a reference to these nodes so we can remove later - this.nodes = _.toArray(frag.childNodes) - _.before(frag, this.el) } - } -/***/ }, -/* 24 */ -/***/ function(module, exports, __webpack_require__) { + /** + * Format a accessor segment based on its type. + * + * @param {String} key + * @return {Boolean} + */ - // xlink - var xlinkNS = 'http://www.w3.org/1999/xlink' - var xlinkRE = /^xlink:/ + function formatAccessor(key) { + if (identRE.test(key)) { // identifier + return '.' + key + } else if (+key === key >>> 0) { // bracket index + return '[' + key + ']' + } else { // bracket string + return '["' + key.replace(/"/g, '\\"') + '"]' + } + } - module.exports = { + /** + * Compiles a getter function with a fixed path. + * + * @param {Array} path + * @return {Function} + */ - priority: 850, + exports.compileGetter = function (path) { + var body = 'return o' + path.map(formatAccessor).join('') + return new Function('o', body) + } - bind: function () { - var name = this.arg - this.update = xlinkRE.test(name) - ? xlinkHandler - : defaultHandler - } + /** + * External parse that check for a cache hit first + * + * @param {String} path + * @return {Array|undefined} + */ + exports.parse = function (path) { + var hit = pathCache.get(path) + if (!hit) { + hit = parsePath(path) + if (hit) { + hit.get = exports.compileGetter(hit) + pathCache.put(path, hit) + } + } + return hit } - function defaultHandler (value) { - if (value || value === 0) { - this.el.setAttribute(this.arg, value) - } else { - this.el.removeAttribute(this.arg) + /** + * Get from an object from a path string + * + * @param {Object} obj + * @param {String} path + */ + + exports.get = function (obj, path) { + path = exports.parse(path) + if (path) { + return path.get(obj) } } - function xlinkHandler (value) { - if (value != null) { - this.el.setAttributeNS(xlinkNS, this.arg, value) + /** + * Set on an object from a path + * + * @param {Object} obj + * @param {String | Array} path + * @param {*} val + */ + + exports.set = function (obj, path, val) { + if (typeof path === 'string') { + path = exports.parse(path) + } + if (!path || !_.isObject(obj)) { + return false + } + var last, key + for (var i = 0, l = path.length - 1; i < l; i++) { + last = obj + key = path[i] + obj = obj[key] + if (!_.isObject(obj)) { + obj = {} + last.$add(key, obj) + } + } + key = path[i] + if (key in obj) { + obj[key] = val } else { - this.el.removeAttributeNS(xlinkNS, 'href') + obj.$add(key, val) } + return true } /***/ }, -/* 25 */ +/* 19 */ /***/ function(module, exports, __webpack_require__) { - var transition = __webpack_require__(49) - - module.exports = function (value) { - var el = this.el - transition.apply(el, value ? 1 : -1, function () { - el.style.display = value ? '' : 'none' - }, this.vm) - } - -/***/ }, -/* 26 */ -/***/ function(module, exports, __webpack_require__) { + var Cache = __webpack_require__(52) + var config = __webpack_require__(15) + var dirParser = __webpack_require__(21) + var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g + var cache, tagRE, htmlRE, firstChar, lastChar - var _ = __webpack_require__(1) - var addClass = _.addClass - var removeClass = _.removeClass + /** + * Escape a string so it can be used in a RegExp + * constructor. + * + * @param {String} str + */ - module.exports = function (value) { - if (this.arg) { - var method = value ? addClass : removeClass - method(this.el, this.arg) - } else { - if (this.lastVal) { - removeClass(this.el, this.lastVal) - } - if (value) { - addClass(this.el, value) - this.lastVal = value - } - } + function escapeRegex (str) { + return str.replace(regexEscapeRE, '\\$&') } -/***/ }, -/* 27 */ -/***/ function(module, exports, __webpack_require__) { - - module.exports = { + /** + * Compile the interpolation tag regex. + * + * @return {RegExp} + */ - isLiteral: true, + function compileRegex () { + config._delimitersChanged = false + var open = config.delimiters[0] + var close = config.delimiters[1] + firstChar = open.charAt(0) + lastChar = close.charAt(close.length - 1) + var firstCharRE = escapeRegex(firstChar) + var lastCharRE = escapeRegex(lastChar) + var openRE = escapeRegex(open) + var closeRE = escapeRegex(close) + tagRE = new RegExp( + firstCharRE + '?' + openRE + + '(.+?)' + + closeRE + lastCharRE + '?', + 'g' + ) + htmlRE = new RegExp( + '^' + firstCharRE + openRE + + '.*' + + closeRE + lastCharRE + '$' + ) + // reset cache + cache = new Cache(1000) + } - bind: function () { - this.vm.$$[this.expression] = this.el - }, + /** + * Parse a template text string into an array of tokens. + * + * @param {String} text + * @return {Array | null} + * - {String} type + * - {String} value + * - {Boolean} [html] + * - {Boolean} [oneTime] + */ - unbind: function () { - delete this.vm.$$[this.expression] + exports.parse = function (text) { + if (config._delimitersChanged) { + compileRegex() } - + var hit = cache.get(text) + if (hit) { + return hit + } + if (!tagRE.test(text)) { + return null + } + var tokens = [] + var lastIndex = tagRE.lastIndex = 0 + var match, index, value, first, oneTime, partial + /* jshint boss:true */ + while (match = tagRE.exec(text)) { + index = match.index + // push text token + if (index > lastIndex) { + tokens.push({ + value: text.slice(lastIndex, index) + }) + } + // tag token + first = match[1].charCodeAt(0) + oneTime = first === 0x2A // * + partial = first === 0x3E // > + value = (oneTime || partial) + ? match[1].slice(1) + : match[1] + tokens.push({ + tag: true, + value: value.trim(), + html: htmlRE.test(match[0]), + oneTime: oneTime, + partial: partial + }) + lastIndex = index + match[0].length + } + if (lastIndex < text.length) { + tokens.push({ + value: text.slice(lastIndex) + }) + } + cache.put(text, tokens) + return tokens } -/***/ }, -/* 28 */ -/***/ function(module, exports, __webpack_require__) { + /** + * 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} + */ - var _ = __webpack_require__(1) + exports.tokensToExp = function (tokens, vm) { + return tokens.length > 1 + ? tokens.map(function (token) { + return formatToken(token, vm) + }).join('+') + : formatToken(tokens[0], vm, true) + } - module.exports = { + /** + * Format a single token. + * + * @param {Object} token + * @param {Vue} [vm] + * @param {Boolean} single + * @return {String} + */ - isLiteral: true, + function formatToken (token, vm, single) { + return token.tag + ? vm && token.oneTime + ? '"' + vm.$eval(token.value) + '"' + : single + ? token.value + : inlineFilters(token.value) + : '"' + token.value + '"' + } - bind: function () { - var child = this.el.__vue__ - if (!child || this.vm !== child.$parent) { - _.warn( - 'v-ref should only be used on a child component ' + - 'from the parent template.' - ) - return - } - this.vm.$[this.expression] = child - }, + /** + * 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 + * @return {String} + */ - unbind: function () { - if (this.vm.$[this.expression] === this.el.__vue__) { - delete this.vm.$[this.expression] + var filterRE = /[^|]\|[^|]/ + function inlineFilters (exp) { + if (!filterRE.test(exp)) { + return '(' + exp + ')' + } else { + var dir = dirParser.parse(exp)[0] + if (!dir.filters) { + return '(' + exp + ')' + } else { + exp = dir.expression + for (var i = 0, l = dir.filters.length; i < l; i++) { + var filter = dir.filters[i] + var args = filter.args + ? ',"' + filter.args.join('","') + '"' + : '' + exp = 'this.$options.filters["' + filter.name + '"]' + + '.apply(this,[' + exp + args + '])' + } + return exp } } - } /***/ }, -/* 29 */ +/* 20 */ /***/ function(module, exports, __webpack_require__) { - var config = __webpack_require__(20) - - module.exports = { - - bind: function () { - var el = this.el - this.vm.$once('hook:compiled', function () { - el.removeAttribute(config.prefix + 'cloak') - }) - } + var _ = __webpack_require__(11) + var Cache = __webpack_require__(52) + var templateCache = new Cache(1000) + var idSelectorCache = new Cache(1000) + var map = { + _default : [0, '', ''], + legend : [1, '
', '
'], + tr : [2, '', '
'], + col : [ + 2, + '', + '
' + ] } -/***/ }, -/* 30 */ -/***/ function(module, exports, __webpack_require__) { + map.td = + map.th = [ + 3, + '', + '
' + ] - var _ = __webpack_require__(1) - var prefixes = ['-webkit-', '-moz-', '-ms-'] - var camelPrefixes = ['Webkit', 'Moz', 'ms'] - var importantRE = /!important;?$/ - var camelRE = /([a-z])([A-Z])/g - var testEl = null - var propCache = {} + map.option = + map.optgroup = [ + 1, + '' + ] - module.exports = { + map.thead = + map.tbody = + map.colgroup = + map.caption = + map.tfoot = [1, '', '
'] - deep: true, + map.g = + map.defs = + map.symbol = + map.use = + map.image = + map.text = + map.circle = + map.ellipse = + map.line = + map.path = + map.polygon = + map.polyline = + map.rect = [ + 1, + '', + '' + ] - update: function (value) { - if (this.arg) { - this.setProp(this.arg, value) - } else { - if (typeof value === 'object') { - // cache object styles so that only changed props - // are actually updated. - if (!this.cache) this.cache = {} - for (var prop in value) { - this.setProp(prop, value[prop]) - /* jshint eqeqeq: false */ - if (value[prop] != this.cache[prop]) { - this.cache[prop] = value[prop] - this.setProp(prop, value[prop]) - } - } - } else { - this.el.style.cssText = value - } + var tagRE = /<([\w:]+)/ + var entityRE = /&\w+;/ + + /** + * Convert a string template to a DocumentFragment. + * Determines correct wrapping by tag types. Wrapping + * strategy found in jQuery & component/domify. + * + * @param {String} templateString + * @return {DocumentFragment} + */ + + function stringToFragment (templateString) { + // try a cache hit first + var hit = templateCache.get(templateString) + if (hit) { + return hit + } + + var frag = document.createDocumentFragment() + var tagMatch = templateString.match(tagRE) + var entityMatch = entityRE.test(templateString) + + if (!tagMatch && !entityMatch) { + // text only, return a single text node. + frag.appendChild( + document.createTextNode(templateString) + ) + } else { + + var tag = tagMatch && tagMatch[1] + var wrap = map[tag] || map._default + var depth = wrap[0] + var prefix = wrap[1] + var suffix = wrap[2] + var node = document.createElement('div') + + node.innerHTML = prefix + templateString.trim() + suffix + while (depth--) { + node = node.lastChild } - }, - setProp: function (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) + var child + /* jshint boss:true */ + while (child = node.firstChild) { + frag.appendChild(child) } } + templateCache.put(templateString, frag) + return frag } /** - * Normalize a CSS property name. - * - cache result - * - auto prefix - * - camelCase -> dash-case + * Convert a template node to a DocumentFragment. * - * @param {String} prop - * @return {String} + * @param {Node} node + * @return {DocumentFragment} */ - function normalize (prop) { - if (propCache[prop]) { - return propCache[prop] + function nodeToFragment (node) { + var tag = node.tagName + // if its a template tag and the browser supports it, + // its content is already a document fragment. + if ( + tag === 'TEMPLATE' && + node.content instanceof DocumentFragment + ) { + return node.content } - var res = prefix(prop) - propCache[prop] = propCache[res] = res - return res + return tag === 'SCRIPT' + ? stringToFragment(node.textContent) + : stringToFragment(node.innerHTML) } + // Test for the presence of the Safari template cloning bug + // https://bugs.webkit.org/show_bug.cgi?id=137755 + var hasBrokenTemplate = _.inBrowser + ? (function () { + var a = document.createElement('div') + a.innerHTML = '' + return !a.cloneNode(true).firstChild.innerHTML + })() + : false + + // Test for IE10/11 textarea placeholder clone bug + var hasTextareaCloneBug = _.inBrowser + ? (function () { + var t = document.createElement('textarea') + t.placeholder = 't' + return t.cloneNode(true).value === 't' + })() + : false + /** - * Auto detect the appropriate prefix for a CSS property. - * https://gist.github.com/paulirish/523692 + * 1. Deal with Safari cloning nested