Created
May 12, 2014 13:36
-
-
Save rafaelrinaldi/3eb562e0732a88794b32 to your computer and use it in GitHub Desktop.
Own Vue release
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Vue.js v0.10.4 | |
(c) 2014 Evan You | |
License: MIT | |
*/ | |
;(function(){ | |
'use strict'; | |
/** | |
* Require the given path. | |
* | |
* @param {String} path | |
* @return {Object} exports | |
* @api public | |
*/ | |
function require(path, parent, orig) { | |
var resolved = require.resolve(path); | |
// lookup failed | |
if (null == resolved) { | |
throwError() | |
return | |
} | |
var module = require.modules[resolved]; | |
// perform real require() | |
// by invoking the module's | |
// registered function | |
if (!module._resolving && !module.exports) { | |
var mod = {}; | |
mod.exports = {}; | |
mod.client = mod.component = true; | |
module._resolving = true; | |
module.call(this, mod.exports, require.relative(resolved), mod); | |
delete module._resolving; | |
module.exports = mod.exports; | |
} | |
function throwError () { | |
orig = orig || path; | |
parent = parent || 'root'; | |
var err = new Error('Failed to require "' + orig + '" from "' + parent + '"'); | |
err.path = orig; | |
err.parent = parent; | |
err.require = true; | |
throw err; | |
} | |
return module.exports; | |
} | |
/** | |
* Registered modules. | |
*/ | |
require.modules = {}; | |
/** | |
* Registered aliases. | |
*/ | |
require.aliases = {}; | |
/** | |
* Resolve `path`. | |
* | |
* Lookup: | |
* | |
* - PATH/index.js | |
* - PATH.js | |
* - PATH | |
* | |
* @param {String} path | |
* @return {String} path or null | |
* @api private | |
*/ | |
require.exts = [ | |
'', | |
'.js', | |
'.json', | |
'/index.js', | |
'/index.json' | |
]; | |
require.resolve = function(path) { | |
if (path.charAt(0) === '/') path = path.slice(1); | |
for (var i = 0; i < 5; i++) { | |
var fullPath = path + require.exts[i]; | |
if (require.modules.hasOwnProperty(fullPath)) return fullPath; | |
if (require.aliases.hasOwnProperty(fullPath)) return require.aliases[fullPath]; | |
} | |
}; | |
/** | |
* Normalize `path` relative to the current path. | |
* | |
* @param {String} curr | |
* @param {String} path | |
* @return {String} | |
* @api private | |
*/ | |
require.normalize = function(curr, path) { | |
var segs = []; | |
if ('.' != path.charAt(0)) return path; | |
curr = curr.split('/'); | |
path = path.split('/'); | |
for (var i = 0; i < path.length; ++i) { | |
if ('..' === path[i]) { | |
curr.pop(); | |
} else if ('.' != path[i] && '' != path[i]) { | |
segs.push(path[i]); | |
} | |
} | |
return curr.concat(segs).join('/'); | |
}; | |
/** | |
* Register module at `path` with callback `definition`. | |
* | |
* @param {String} path | |
* @param {Function} definition | |
* @api private | |
*/ | |
require.register = function(path, definition) { | |
require.modules[path] = definition; | |
}; | |
/** | |
* Alias a module definition. | |
* | |
* @param {String} from | |
* @param {String} to | |
* @api private | |
*/ | |
require.alias = function(from, to) { | |
if (!require.modules.hasOwnProperty(from)) { | |
throwError() | |
return | |
} | |
require.aliases[to] = from; | |
function throwError () { | |
throw new Error('Failed to alias "' + from + '", it does not exist'); | |
} | |
}; | |
/** | |
* Return a require function relative to the `parent` path. | |
* | |
* @param {String} parent | |
* @return {Function} | |
* @api private | |
*/ | |
require.relative = function(parent) { | |
var p = require.normalize(parent, '..'); | |
/** | |
* The relative require() itself. | |
*/ | |
function localRequire(path) { | |
var resolved = localRequire.resolve(path); | |
return require(resolved, parent, path); | |
} | |
/** | |
* Resolve relative to the parent. | |
*/ | |
localRequire.resolve = function(path) { | |
var c = path.charAt(0); | |
if ('/' === c) return path.slice(1); | |
if ('.' === c) return require.normalize(p, path); | |
// resolve deps by returning | |
// the dep in the nearest "deps" | |
// directory | |
var segs = parent.split('/'); | |
var i = segs.length; | |
while (i--) { | |
if (segs[i] === 'deps') { | |
break; | |
} | |
} | |
path = segs.slice(0, i + 2).join('/') + '/deps/' + path; | |
return path; | |
}; | |
/** | |
* Check if module is defined at `path`. | |
*/ | |
localRequire.exists = function(path) { | |
return require.modules.hasOwnProperty(localRequire.resolve(path)); | |
}; | |
return localRequire; | |
}; | |
require.register("vue/src/main.js", function(exports, require, module){ | |
var config = require('./config'), | |
ViewModel = require('./viewmodel'), | |
utils = require('./utils'), | |
makeHash = utils.hash, | |
assetTypes = ['directive', 'filter', 'partial', 'effect', 'component'] | |
// require these so Browserify can catch them | |
// so they can be used in Vue.require | |
require('./observer') | |
require('./transition') | |
ViewModel.options = config.globalAssets = { | |
directives : require('./directives'), | |
filters : require('./filters'), | |
partials : makeHash(), | |
effects : makeHash(), | |
components : makeHash() | |
} | |
/** | |
* Expose asset registration methods | |
*/ | |
assetTypes.forEach(function (type) { | |
ViewModel[type] = function (id, value) { | |
var hash = this.options[type + 's'] | |
if (!hash) { | |
hash = this.options[type + 's'] = makeHash() | |
} | |
if (!value) return hash[id] | |
if (type === 'partial') { | |
value = utils.toFragment(value) | |
} else if (type === 'component') { | |
value = utils.toConstructor(value) | |
} else if (type === 'filter') { | |
utils.checkFilter(value) | |
} | |
hash[id] = value | |
return this | |
} | |
}) | |
/** | |
* Set config options | |
*/ | |
ViewModel.config = function (opts, val) { | |
if (typeof opts === 'string') { | |
if (val === undefined) { | |
return config[opts] | |
} else { | |
config[opts] = val | |
} | |
} else { | |
utils.extend(config, opts) | |
} | |
return this | |
} | |
/** | |
* Expose an interface for plugins | |
*/ | |
ViewModel.use = function (plugin) { | |
if (typeof plugin === 'string') { | |
try { | |
plugin = require(plugin) | |
} catch (e) { | |
utils.warn('Cannot find plugin: ' + plugin) | |
return | |
} | |
} | |
// additional parameters | |
var args = [].slice.call(arguments, 1) | |
args.unshift(this) | |
if (typeof plugin.install === 'function') { | |
plugin.install.apply(plugin, args) | |
} else { | |
plugin.apply(null, args) | |
} | |
return this | |
} | |
/** | |
* Expose internal modules for plugins | |
*/ | |
ViewModel.require = function (path) { | |
return require('./' + path) | |
} | |
ViewModel.extend = extend | |
ViewModel.nextTick = utils.nextTick | |
/** | |
* Expose the main ViewModel class | |
* and add extend method | |
*/ | |
function extend (options) { | |
var ParentVM = this | |
// extend data options need to be copied | |
// on instantiation | |
if (options.data) { | |
options.defaultData = options.data | |
delete options.data | |
} | |
// inherit options | |
// but only when the super class is not the native Vue. | |
if (ParentVM !== ViewModel) { | |
options = inheritOptions(options, ParentVM.options, true) | |
} | |
utils.processOptions(options) | |
var ExtendedVM = function (opts, asParent) { | |
if (!asParent) { | |
opts = inheritOptions(opts, options, true) | |
} | |
ParentVM.call(this, opts, true) | |
} | |
// inherit prototype props | |
var proto = ExtendedVM.prototype = Object.create(ParentVM.prototype) | |
utils.defProtected(proto, 'constructor', ExtendedVM) | |
// allow extended VM to be further extended | |
ExtendedVM.extend = extend | |
ExtendedVM.super = ParentVM | |
ExtendedVM.options = options | |
// allow extended VM to add its own assets | |
assetTypes.forEach(function (type) { | |
ExtendedVM[type] = ViewModel[type] | |
}) | |
// allow extended VM to use plugins | |
ExtendedVM.use = ViewModel.use | |
ExtendedVM.require = ViewModel.require | |
return ExtendedVM | |
} | |
/** | |
* Inherit options | |
* | |
* For options such as `data`, `vms`, `directives`, 'partials', | |
* they should be further extended. However extending should only | |
* be done at top level. | |
* | |
* `proto` is an exception because it's handled directly on the | |
* prototype. | |
* | |
* `el` is an exception because it's not allowed as an | |
* extension option, but only as an instance option. | |
*/ | |
function inheritOptions (child, parent, topLevel) { | |
child = child || {} | |
if (!parent) return child | |
for (var key in parent) { | |
if (key === 'el') continue | |
var val = child[key], | |
parentVal = parent[key] | |
if (topLevel && typeof val === 'function' && parentVal) { | |
// merge hook functions into an array | |
child[key] = [val] | |
if (Array.isArray(parentVal)) { | |
child[key] = child[key].concat(parentVal) | |
} else { | |
child[key].push(parentVal) | |
} | |
} else if ( | |
topLevel && | |
(utils.isTrueObject(val) || utils.isTrueObject(parentVal)) | |
&& !(parentVal instanceof ViewModel) | |
) { | |
// merge toplevel object options | |
child[key] = inheritOptions(val, parentVal) | |
} else if (val === undefined) { | |
// inherit if child doesn't override | |
child[key] = parentVal | |
} | |
} | |
return child | |
} | |
module.exports = ViewModel | |
}); | |
require.register("vue/src/emitter.js", function(exports, require, module){ | |
var slice = [].slice | |
function Emitter (ctx) { | |
this._ctx = ctx || this | |
} | |
var EmitterProto = Emitter.prototype | |
EmitterProto.on = function (event, fn) { | |
this._cbs = this._cbs || {} | |
;(this._cbs[event] = this._cbs[event] || []) | |
.push(fn) | |
return this | |
} | |
EmitterProto.once = function (event, fn) { | |
var self = this | |
this._cbs = this._cbs || {} | |
function on () { | |
self.off(event, on) | |
fn.apply(this, arguments) | |
} | |
on.fn = fn | |
this.on(event, on) | |
return this | |
} | |
EmitterProto.off = function (event, fn) { | |
this._cbs = this._cbs || {} | |
// all | |
if (!arguments.length) { | |
this._cbs = {} | |
return this | |
} | |
// specific event | |
var callbacks = this._cbs[event] | |
if (!callbacks) return this | |
// remove all handlers | |
if (arguments.length === 1) { | |
delete this._cbs[event] | |
return this | |
} | |
// remove specific handler | |
var cb | |
for (var i = 0; i < callbacks.length; i++) { | |
cb = callbacks[i] | |
if (cb === fn || cb.fn === fn) { | |
callbacks.splice(i, 1) | |
break | |
} | |
} | |
return this | |
} | |
/** | |
* The internal, faster emit with fixed amount of arguments | |
* using Function.call | |
*/ | |
EmitterProto.emit = function (event, a, b, c) { | |
this._cbs = this._cbs || {} | |
var callbacks = this._cbs[event] | |
if (callbacks) { | |
callbacks = callbacks.slice(0) | |
for (var i = 0, len = callbacks.length; i < len; i++) { | |
callbacks[i].call(this._ctx, a, b, c) | |
} | |
} | |
return this | |
} | |
/** | |
* The external emit using Function.apply | |
*/ | |
EmitterProto.applyEmit = function (event) { | |
this._cbs = this._cbs || {} | |
var callbacks = this._cbs[event], args | |
if (callbacks) { | |
callbacks = callbacks.slice(0) | |
args = slice.call(arguments, 1) | |
for (var i = 0, len = callbacks.length; i < len; i++) { | |
callbacks[i].apply(this._ctx, args) | |
} | |
} | |
return this | |
} | |
module.exports = Emitter | |
}); | |
require.register("vue/src/config.js", function(exports, require, module){ | |
var TextParser = require('./text-parser') | |
module.exports = { | |
prefix : 'v', | |
debug : false, | |
silent : false, | |
enterClass : 'v-enter', | |
leaveClass : 'v-leave', | |
interpolate : true | |
} | |
Object.defineProperty(module.exports, 'delimiters', { | |
get: function () { | |
return TextParser.delimiters | |
}, | |
set: function (delimiters) { | |
TextParser.setDelimiters(delimiters) | |
} | |
}) | |
}); | |
require.register("vue/src/utils.js", function(exports, require, module){ | |
var config = require('./config'), | |
toString = ({}).toString, | |
win = window, | |
console = win.console, | |
def = Object.defineProperty, | |
OBJECT = 'object', | |
THIS_RE = /[^\w]this[^\w]/, | |
hasClassList = 'classList' in document.documentElement, | |
ViewModel // late def | |
var defer = | |
win.requestAnimationFrame || | |
win.webkitRequestAnimationFrame || | |
win.setTimeout | |
var utils = module.exports = { | |
/** | |
* Convert a string template to a dom fragment | |
*/ | |
toFragment: require('./fragment'), | |
/** | |
* get a value from an object keypath | |
*/ | |
get: function (obj, key) { | |
/* jshint eqeqeq: false */ | |
if (key.indexOf('.') < 0) { | |
return obj[key] | |
} | |
var path = key.split('.'), | |
d = -1, l = path.length | |
while (++d < l && obj != null) { | |
obj = obj[path[d]] | |
} | |
return obj | |
}, | |
/** | |
* set a value to an object keypath | |
*/ | |
set: function (obj, key, val) { | |
/* jshint eqeqeq: false */ | |
if (key.indexOf('.') < 0) { | |
obj[key] = val | |
return | |
} | |
var path = key.split('.'), | |
d = -1, l = path.length - 1 | |
while (++d < l) { | |
if (obj[path[d]] == null) { | |
obj[path[d]] = {} | |
} | |
obj = obj[path[d]] | |
} | |
obj[path[d]] = val | |
}, | |
/** | |
* return the base segment of a keypath | |
*/ | |
baseKey: function (key) { | |
return key.indexOf('.') > 0 | |
? key.split('.')[0] | |
: key | |
}, | |
/** | |
* Create a prototype-less object | |
* which is a better hash/map | |
*/ | |
hash: function () { | |
return Object.create(null) | |
}, | |
/** | |
* get an attribute and remove it. | |
*/ | |
attr: function (el, type) { | |
var attr = config.prefix + '-' + type, | |
val = el.getAttribute(attr) | |
if (val !== null) { | |
el.removeAttribute(attr) | |
} | |
return val | |
}, | |
/** | |
* Define an ienumerable property | |
* This avoids it being included in JSON.stringify | |
* or for...in loops. | |
*/ | |
defProtected: function (obj, key, val, enumerable, writable) { | |
def(obj, key, { | |
value : val, | |
enumerable : enumerable, | |
writable : writable, | |
configurable : true | |
}) | |
}, | |
/** | |
* A less bullet-proof but more efficient type check | |
* than Object.prototype.toString | |
*/ | |
isObject: function (obj) { | |
return typeof obj === OBJECT && obj && !Array.isArray(obj) | |
}, | |
/** | |
* A more accurate but less efficient type check | |
*/ | |
isTrueObject: function (obj) { | |
return toString.call(obj) === '[object Object]' | |
}, | |
/** | |
* Most simple bind | |
* enough for the usecase and fast than native bind() | |
*/ | |
bind: function (fn, ctx) { | |
return function (arg) { | |
return fn.call(ctx, arg) | |
} | |
}, | |
/** | |
* Make sure null and undefined output empty string | |
*/ | |
guard: function (value) { | |
/* jshint eqeqeq: false, eqnull: true */ | |
return value == null | |
? '' | |
: (typeof value == 'object') | |
? JSON.stringify(value) | |
: value | |
}, | |
/** | |
* When setting value on the VM, parse possible numbers | |
*/ | |
checkNumber: function (value) { | |
return (isNaN(value) || value === null || typeof value === 'boolean') | |
? value | |
: Number(value) | |
}, | |
/** | |
* simple extend | |
*/ | |
extend: function (obj, ext) { | |
for (var key in ext) { | |
if (obj[key] !== ext[key]) { | |
obj[key] = ext[key] | |
} | |
} | |
return obj | |
}, | |
/** | |
* filter an array with duplicates into uniques | |
*/ | |
unique: function (arr) { | |
var hash = utils.hash(), | |
i = arr.length, | |
key, res = [] | |
while (i--) { | |
key = arr[i] | |
if (hash[key]) continue | |
hash[key] = 1 | |
res.push(key) | |
} | |
return res | |
}, | |
/** | |
* Convert the object to a ViewModel constructor | |
* if it is not already one | |
*/ | |
toConstructor: function (obj) { | |
ViewModel = ViewModel || require('./viewmodel') | |
return utils.isObject(obj) | |
? ViewModel.extend(obj) | |
: typeof obj === 'function' | |
? obj | |
: null | |
}, | |
/** | |
* Check if a filter function contains references to `this` | |
* If yes, mark it as a computed filter. | |
*/ | |
checkFilter: function (filter) { | |
if (THIS_RE.test(filter.toString())) { | |
filter.computed = true | |
} | |
}, | |
/** | |
* convert certain option values to the desired format. | |
*/ | |
processOptions: function (options) { | |
var components = options.components, | |
partials = options.partials, | |
template = options.template, | |
filters = options.filters, | |
key | |
if (components) { | |
for (key in components) { | |
components[key] = utils.toConstructor(components[key]) | |
} | |
} | |
if (partials) { | |
for (key in partials) { | |
partials[key] = utils.toFragment(partials[key]) | |
} | |
} | |
if (filters) { | |
for (key in filters) { | |
utils.checkFilter(filters[key]) | |
} | |
} | |
if (template) { | |
options.template = utils.toFragment(template) | |
} | |
}, | |
/** | |
* used to defer batch updates | |
*/ | |
nextTick: function (cb) { | |
defer(cb, 0) | |
}, | |
/** | |
* add class for IE9 | |
* uses classList if available | |
*/ | |
addClass: function (el, cls) { | |
if (hasClassList) { | |
el.classList.add(cls) | |
} else { | |
var cur = ' ' + el.className + ' ' | |
if (cur.indexOf(' ' + cls + ' ') < 0) { | |
el.className = (cur + cls).trim() | |
} | |
} | |
}, | |
/** | |
* remove class for IE9 | |
*/ | |
removeClass: function (el, cls) { | |
if (hasClassList) { | |
el.classList.remove(cls) | |
} else { | |
var cur = ' ' + el.className + ' ', | |
tar = ' ' + cls + ' ' | |
while (cur.indexOf(tar) >= 0) { | |
cur = cur.replace(tar, ' ') | |
} | |
el.className = cur.trim() | |
} | |
}, | |
/** | |
* Convert an object to Array | |
* used in v-repeat and array filters | |
*/ | |
objectToArray: function (obj) { | |
var res = [], val, data | |
for (var key in obj) { | |
val = obj[key] | |
data = utils.isObject(val) | |
? val | |
: { $value: val } | |
data.$key = key | |
res.push(data) | |
} | |
return res | |
} | |
} | |
enableDebug() | |
function enableDebug () { | |
/** | |
* log for debugging | |
*/ | |
utils.log = function (msg) { | |
if (config.debug && console) { | |
console.log(msg) | |
} | |
} | |
/** | |
* warnings, traces by default | |
* can be suppressed by `silent` option. | |
*/ | |
utils.warn = function (msg) { | |
if (!config.silent && console) { | |
console.warn(msg) | |
if (config.debug && console.trace) { | |
console.trace() | |
} | |
} | |
} | |
} | |
}); | |
require.register("vue/src/fragment.js", function(exports, require, module){ | |
// string -> DOM conversion | |
// wrappers originally from jQuery, scooped from component/domify | |
var map = { | |
legend : [1, '<fieldset>', '</fieldset>'], | |
tr : [2, '<table><tbody>', '</tbody></table>'], | |
col : [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'], | |
_default : [0, '', ''] | |
} | |
map.td = | |
map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>'] | |
map.option = | |
map.optgroup = [1, '<select multiple="multiple">', '</select>'] | |
map.thead = | |
map.tbody = | |
map.colgroup = | |
map.caption = | |
map.tfoot = [1, '<table>', '</table>'] | |
map.text = | |
map.circle = | |
map.ellipse = | |
map.line = | |
map.path = | |
map.polygon = | |
map.polyline = | |
map.rect = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>'] | |
var TAG_RE = /<([\w:]+)/ | |
module.exports = function (template) { | |
if (typeof template !== 'string') { | |
return template | |
} | |
// template by ID | |
if (template.charAt(0) === '#') { | |
var templateNode = document.getElementById(template.slice(1)) | |
if (!templateNode) return | |
// if its a template tag and the browser supports it, | |
// its content is already a document fragment! | |
if (templateNode.tagName === 'TEMPLATE' && templateNode.content) { | |
return templateNode.content | |
} | |
template = templateNode.innerHTML | |
} | |
var frag = document.createDocumentFragment(), | |
m = TAG_RE.exec(template) | |
// text only | |
if (!m) { | |
frag.appendChild(document.createTextNode(template)) | |
return frag | |
} | |
var tag = m[1], | |
wrap = map[tag] || map._default, | |
depth = wrap[0], | |
prefix = wrap[1], | |
suffix = wrap[2], | |
node = document.createElement('div') | |
node.innerHTML = prefix + template.trim() + suffix | |
while (depth--) node = node.lastChild | |
// one element | |
if (node.firstChild === node.lastChild) { | |
frag.appendChild(node.firstChild) | |
return frag | |
} | |
// multiple nodes, return a fragment | |
var child | |
/* jshint boss: true */ | |
while (child = node.firstChild) { | |
if (node.nodeType === 1) { | |
frag.appendChild(child) | |
} | |
} | |
return frag | |
} | |
}); | |
require.register("vue/src/compiler.js", function(exports, require, module){ | |
var Emitter = require('./emitter'), | |
Observer = require('./observer'), | |
config = require('./config'), | |
utils = require('./utils'), | |
Binding = require('./binding'), | |
Directive = require('./directive'), | |
TextParser = require('./text-parser'), | |
DepsParser = require('./deps-parser'), | |
ExpParser = require('./exp-parser'), | |
ViewModel, | |
// cache methods | |
slice = [].slice, | |
extend = utils.extend, | |
hasOwn = ({}).hasOwnProperty, | |
def = Object.defineProperty, | |
// hooks to register | |
hooks = [ | |
'created', 'ready', | |
'beforeDestroy', 'afterDestroy', | |
'attached', 'detached' | |
], | |
// list of priority directives | |
// that needs to be checked in specific order | |
priorityDirectives = [ | |
'if', | |
'repeat', | |
'view', | |
'component' | |
] | |
/** | |
* The DOM compiler | |
* scans a DOM node and compile bindings for a ViewModel | |
*/ | |
function Compiler (vm, options) { | |
var compiler = this, | |
key, i | |
// default state | |
compiler.init = true | |
compiler.destroyed = false | |
// process and extend options | |
options = compiler.options = options || {} | |
utils.processOptions(options) | |
// copy compiler options | |
extend(compiler, options.compilerOptions) | |
// repeat indicates this is a v-repeat instance | |
compiler.repeat = compiler.repeat || false | |
// expCache will be shared between v-repeat instances | |
compiler.expCache = compiler.expCache || {} | |
// initialize element | |
var el = compiler.el = compiler.setupElement(options) | |
utils.log('\nnew VM instance: ' + el.tagName + '\n') | |
// set other compiler properties | |
compiler.vm = el.vue_vm = vm | |
compiler.bindings = utils.hash() | |
compiler.dirs = [] | |
compiler.deferred = [] | |
compiler.computed = [] | |
compiler.children = [] | |
compiler.emitter = new Emitter(vm) | |
// create bindings for computed properties | |
if (options.methods) { | |
for (key in options.methods) { | |
compiler.createBinding(key) | |
} | |
} | |
// create bindings for methods | |
if (options.computed) { | |
for (key in options.computed) { | |
compiler.createBinding(key) | |
} | |
} | |
// VM --------------------------------------------------------------------- | |
// set VM properties | |
vm.$ = {} | |
vm.$el = el | |
vm.$options = options | |
vm.$compiler = compiler | |
vm.$event = null | |
// set parent & root | |
var parentVM = options.parent | |
if (parentVM) { | |
compiler.parent = parentVM.$compiler | |
parentVM.$compiler.children.push(compiler) | |
vm.$parent = parentVM | |
} | |
vm.$root = getRoot(compiler).vm | |
// DATA ------------------------------------------------------------------- | |
// setup observer | |
// this is necesarry for all hooks and data observation events | |
compiler.setupObserver() | |
// initialize data | |
var data = compiler.data = options.data || {}, | |
defaultData = options.defaultData | |
if (defaultData) { | |
for (key in defaultData) { | |
if (!hasOwn.call(data, key)) { | |
data[key] = defaultData[key] | |
} | |
} | |
} | |
// copy paramAttributes | |
var params = options.paramAttributes | |
if (params) { | |
i = params.length | |
while (i--) { | |
data[params[i]] = utils.checkNumber( | |
compiler.eval( | |
el.getAttribute(params[i]) | |
) | |
) | |
} | |
} | |
// copy data properties to vm | |
// so user can access them in the created hook | |
extend(vm, data) | |
vm.$data = data | |
// beforeCompile hook | |
compiler.execHook('created') | |
// the user might have swapped the data ... | |
data = compiler.data = vm.$data | |
// user might also set some properties on the vm | |
// in which case we should copy back to $data | |
var vmProp | |
for (key in vm) { | |
vmProp = vm[key] | |
if ( | |
key.charAt(0) !== '$' && | |
data[key] !== vmProp && | |
typeof vmProp !== 'function' | |
) { | |
data[key] = vmProp | |
} | |
} | |
// now we can observe the data. | |
// this will convert data properties to getter/setters | |
// and emit the first batch of set events, which will | |
// in turn create the corresponding bindings. | |
compiler.observeData(data) | |
// COMPILE ---------------------------------------------------------------- | |
// before compiling, resolve content insertion points | |
if (options.template) { | |
this.resolveContent() | |
} | |
// now parse the DOM and bind directives. | |
// During this stage, we will also create bindings for | |
// encountered keypaths that don't have a binding yet. | |
compiler.compile(el, true) | |
// Any directive that creates child VMs are deferred | |
// so that when they are compiled, all bindings on the | |
// parent VM have been created. | |
i = compiler.deferred.length | |
while (i--) { | |
compiler.bindDirective(compiler.deferred[i]) | |
} | |
compiler.deferred = null | |
// extract dependencies for computed properties. | |
// this will evaluated all collected computed bindings | |
// and collect get events that are emitted. | |
if (this.computed.length) { | |
DepsParser.parse(this.computed) | |
} | |
// done! | |
compiler.init = false | |
// post compile / ready hook | |
compiler.execHook('ready') | |
} | |
var CompilerProto = Compiler.prototype | |
/** | |
* Initialize the VM/Compiler's element. | |
* Fill it in with the template if necessary. | |
*/ | |
CompilerProto.setupElement = function (options) { | |
// create the node first | |
var el = typeof options.el === 'string' | |
? document.querySelector(options.el) | |
: options.el || document.createElement(options.tagName || 'div') | |
var template = options.template, | |
child, replacer, i, attr, attrs | |
if (template) { | |
// collect anything already in there | |
if (el.hasChildNodes()) { | |
this.rawContent = document.createElement('div') | |
/* jshint boss: true */ | |
while (child = el.firstChild) { | |
this.rawContent.appendChild(child) | |
} | |
} | |
// replace option: use the first node in | |
// the template directly | |
if (options.replace && template.firstChild === template.lastChild) { | |
replacer = template.firstChild.cloneNode(true) | |
if (el.parentNode) { | |
el.parentNode.insertBefore(replacer, el) | |
el.parentNode.removeChild(el) | |
} | |
// copy over attributes | |
if (el.hasAttributes()) { | |
i = el.attributes.length | |
while (i--) { | |
attr = el.attributes[i] | |
replacer.setAttribute(attr.name, attr.value) | |
} | |
} | |
// replace | |
el = replacer | |
} else { | |
el.appendChild(template.cloneNode(true)) | |
} | |
} | |
// apply element options | |
if (options.id) el.id = options.id | |
if (options.className) el.className = options.className | |
attrs = options.attributes | |
if (attrs) { | |
for (attr in attrs) { | |
el.setAttribute(attr, attrs[attr]) | |
} | |
} | |
return el | |
} | |
/** | |
* Deal with <content> insertion points | |
* per the Web Components spec | |
*/ | |
CompilerProto.resolveContent = function () { | |
var outlets = slice.call(this.el.getElementsByTagName('content')), | |
raw = this.rawContent, | |
outlet, select, i, j, main | |
i = outlets.length | |
if (i) { | |
// first pass, collect corresponding content | |
// for each outlet. | |
while (i--) { | |
outlet = outlets[i] | |
if (raw) { | |
select = outlet.getAttribute('select') | |
if (select) { // select content | |
outlet.content = | |
slice.call(raw.querySelectorAll(select)) | |
} else { // default content | |
main = outlet | |
} | |
} else { // fallback content | |
outlet.content = | |
slice.call(outlet.childNodes) | |
} | |
} | |
// second pass, actually insert the contents | |
for (i = 0, j = outlets.length; i < j; i++) { | |
outlet = outlets[i] | |
if (outlet === main) continue | |
insert(outlet, outlet.content) | |
} | |
// finally insert the main content | |
if (raw && main) { | |
insert(main, slice.call(raw.childNodes)) | |
} | |
} | |
function insert (outlet, contents) { | |
var parent = outlet.parentNode, | |
i = 0, j = contents.length | |
for (; i < j; i++) { | |
parent.insertBefore(contents[i], outlet) | |
} | |
parent.removeChild(outlet) | |
} | |
this.rawContent = null | |
} | |
/** | |
* Setup observer. | |
* The observer listens for get/set/mutate events on all VM | |
* values/objects and trigger corresponding binding updates. | |
* It also listens for lifecycle hooks. | |
*/ | |
CompilerProto.setupObserver = function () { | |
var compiler = this, | |
bindings = compiler.bindings, | |
options = compiler.options, | |
observer = compiler.observer = new Emitter(compiler.vm) | |
// a hash to hold event proxies for each root level key | |
// so they can be referenced and removed later | |
observer.proxies = {} | |
// add own listeners which trigger binding updates | |
observer | |
.on('get', onGet) | |
.on('set', onSet) | |
.on('mutate', onSet) | |
// register hooks | |
var i = hooks.length, j, hook, fns | |
while (i--) { | |
hook = hooks[i] | |
fns = options[hook] | |
if (Array.isArray(fns)) { | |
j = fns.length | |
// since hooks were merged with child at head, | |
// we loop reversely. | |
while (j--) { | |
registerHook(hook, fns[j]) | |
} | |
} else if (fns) { | |
registerHook(hook, fns) | |
} | |
} | |
// broadcast attached/detached hooks | |
observer | |
.on('hook:attached', function () { | |
broadcast(1) | |
}) | |
.on('hook:detached', function () { | |
broadcast(0) | |
}) | |
function onGet (key) { | |
check(key) | |
DepsParser.catcher.emit('get', bindings[key]) | |
} | |
function onSet (key, val, mutation) { | |
observer.emit('change:' + key, val, mutation) | |
check(key) | |
bindings[key].update(val) | |
} | |
function registerHook (hook, fn) { | |
observer.on('hook:' + hook, function () { | |
fn.call(compiler.vm) | |
}) | |
} | |
function broadcast (event) { | |
var children = compiler.children | |
if (children) { | |
var child, i = children.length | |
while (i--) { | |
child = children[i] | |
if (child.el.parentNode) { | |
event = 'hook:' + (event ? 'attached' : 'detached') | |
child.observer.emit(event) | |
child.emitter.emit(event) | |
} | |
} | |
} | |
} | |
function check (key) { | |
if (!bindings[key]) { | |
compiler.createBinding(key) | |
} | |
} | |
} | |
CompilerProto.observeData = function (data) { | |
var compiler = this, | |
observer = compiler.observer | |
// recursively observe nested properties | |
Observer.observe(data, '', observer) | |
// also create binding for top level $data | |
// so it can be used in templates too | |
var $dataBinding = compiler.bindings['$data'] = new Binding(compiler, '$data') | |
$dataBinding.update(data) | |
// allow $data to be swapped | |
def(compiler.vm, '$data', { | |
get: function () { | |
compiler.observer.emit('get', '$data') | |
return compiler.data | |
}, | |
set: function (newData) { | |
var oldData = compiler.data | |
Observer.unobserve(oldData, '', observer) | |
compiler.data = newData | |
Observer.copyPaths(newData, oldData) | |
Observer.observe(newData, '', observer) | |
update() | |
} | |
}) | |
// emit $data change on all changes | |
observer | |
.on('set', onSet) | |
.on('mutate', onSet) | |
function onSet (key) { | |
if (key !== '$data') update() | |
} | |
function update () { | |
$dataBinding.update(compiler.data) | |
observer.emit('change:$data', compiler.data) | |
} | |
} | |
/** | |
* Compile a DOM node (recursive) | |
*/ | |
CompilerProto.compile = function (node, root) { | |
var nodeType = node.nodeType | |
if (nodeType === 1 && node.tagName !== 'SCRIPT') { // a normal node | |
this.compileElement(node, root) | |
} else if (nodeType === 3 && config.interpolate) { | |
this.compileTextNode(node) | |
} | |
} | |
/** | |
* Check for a priority directive | |
* If it is present and valid, return true to skip the rest | |
*/ | |
CompilerProto.checkPriorityDir = function (dirname, node, root) { | |
var expression, directive, Ctor | |
if ( | |
dirname === 'component' && | |
root !== true && | |
(Ctor = this.resolveComponent(node, undefined, true)) | |
) { | |
directive = this.parseDirective(dirname, '', node) | |
directive.Ctor = Ctor | |
} else { | |
expression = utils.attr(node, dirname) | |
directive = expression && this.parseDirective(dirname, expression, node) | |
} | |
if (directive) { | |
if (root === true) { | |
utils.warn( | |
'Directive v-' + dirname + ' cannot be used on an already instantiated ' + | |
'VM\'s root node. Use it from the parent\'s template instead.' | |
) | |
return | |
} | |
this.deferred.push(directive) | |
return true | |
} | |
} | |
/** | |
* Compile normal directives on a node | |
*/ | |
CompilerProto.compileElement = function (node, root) { | |
// textarea is pretty annoying | |
// because its value creates childNodes which | |
// we don't want to compile. | |
if (node.tagName === 'TEXTAREA' && node.value) { | |
node.value = this.eval(node.value) | |
} | |
// only compile if this element has attributes | |
// or its tagName contains a hyphen (which means it could | |
// potentially be a custom element) | |
if (node.hasAttributes() || node.tagName.indexOf('-') > -1) { | |
// skip anything with v-pre | |
if (utils.attr(node, 'pre') !== null) { | |
return | |
} | |
var i, l, j, k | |
// check priority directives. | |
// if any of them are present, it will take over the node with a childVM | |
// so we can skip the rest | |
for (i = 0, l = priorityDirectives.length; i < l; i++) { | |
if (this.checkPriorityDir(priorityDirectives[i], node, root)) { | |
return | |
} | |
} | |
// check transition & animation properties | |
node.vue_trans = utils.attr(node, 'transition') | |
node.vue_anim = utils.attr(node, 'animation') | |
node.vue_effect = this.eval(utils.attr(node, 'effect')) | |
var prefix = config.prefix + '-', | |
attrs = slice.call(node.attributes), | |
params = this.options.paramAttributes, | |
attr, isDirective, exp, directives, directive, dirname | |
for (i = 0, l = attrs.length; i < l; i++) { | |
attr = attrs[i] | |
isDirective = false | |
if (attr.name.indexOf(prefix) === 0) { | |
// a directive - split, parse and bind it. | |
isDirective = true | |
dirname = attr.name.slice(prefix.length) | |
// build with multiple: true | |
directives = this.parseDirective(dirname, attr.value, node, true) | |
// loop through clauses (separated by ",") | |
// inside each attribute | |
for (j = 0, k = directives.length; j < k; j++) { | |
directive = directives[j] | |
if (dirname === 'with') { | |
this.bindDirective(directive, this.parent) | |
} else { | |
this.bindDirective(directive) | |
} | |
} | |
} else if (config.interpolate) { | |
// non directive attribute, check interpolation tags | |
exp = TextParser.parseAttr(attr.value) | |
if (exp) { | |
directive = this.parseDirective('attr', attr.name + ':' + exp, node) | |
if (params && params.indexOf(attr.name) > -1) { | |
// a param attribute... we should use the parent binding | |
// to avoid circular updates like size={{size}} | |
this.bindDirective(directive, this.parent) | |
} else { | |
this.bindDirective(directive) | |
} | |
} | |
} | |
if (isDirective && dirname !== 'cloak') { | |
node.removeAttribute(attr.name) | |
} | |
} | |
} | |
// recursively compile childNodes | |
if (node.hasChildNodes()) { | |
slice.call(node.childNodes).forEach(this.compile, this) | |
} | |
} | |
/** | |
* Compile a text node | |
*/ | |
CompilerProto.compileTextNode = function (node) { | |
var tokens = TextParser.parse(node.nodeValue) | |
if (!tokens) return | |
var el, token, directive | |
for (var i = 0, l = tokens.length; i < l; i++) { | |
token = tokens[i] | |
directive = null | |
if (token.key) { // a binding | |
if (token.key.charAt(0) === '>') { // a partial | |
el = document.createComment('ref') | |
directive = this.parseDirective('partial', token.key.slice(1), el) | |
} else { | |
if (!token.html) { // text binding | |
el = document.createTextNode('') | |
directive = this.parseDirective('text', token.key, el) | |
} else { // html binding | |
el = document.createComment(config.prefix + '-html') | |
directive = this.parseDirective('html', token.key, el) | |
} | |
} | |
} else { // a plain string | |
el = document.createTextNode(token) | |
} | |
// insert node | |
node.parentNode.insertBefore(el, node) | |
// bind directive | |
this.bindDirective(directive) | |
} | |
node.parentNode.removeChild(node) | |
} | |
/** | |
* Parse a directive name/value pair into one or more | |
* directive instances | |
*/ | |
CompilerProto.parseDirective = function (name, value, el, multiple) { | |
var compiler = this, | |
definition = compiler.getOption('directives', name) | |
if (definition) { | |
// parse into AST-like objects | |
var asts = Directive.parse(value) | |
return multiple | |
? asts.map(build) | |
: build(asts[0]) | |
} | |
function build (ast) { | |
return new Directive(name, ast, definition, compiler, el) | |
} | |
} | |
/** | |
* Add a directive instance to the correct binding & viewmodel | |
*/ | |
CompilerProto.bindDirective = function (directive, bindingOwner) { | |
if (!directive) return | |
// keep track of it so we can unbind() later | |
this.dirs.push(directive) | |
// for empty or literal directives, simply call its bind() | |
// and we're done. | |
if (directive.isEmpty || directive.isLiteral) { | |
if (directive.bind) directive.bind() | |
return | |
} | |
// otherwise, we got more work to do... | |
var binding, | |
compiler = bindingOwner || this, | |
key = directive.key | |
if (directive.isExp) { | |
// expression bindings are always created on current compiler | |
binding = compiler.createBinding(key, directive) | |
} else { | |
// recursively locate which compiler owns the binding | |
while (compiler) { | |
if (compiler.hasKey(key)) { | |
break | |
} else { | |
compiler = compiler.parent | |
} | |
} | |
compiler = compiler || this | |
binding = compiler.bindings[key] || compiler.createBinding(key) | |
} | |
binding.dirs.push(directive) | |
directive.binding = binding | |
var value = binding.val() | |
// invoke bind hook if exists | |
if (directive.bind) { | |
directive.bind(value) | |
} | |
// set initial value | |
directive.$update(value, true) | |
} | |
/** | |
* Create binding and attach getter/setter for a key to the viewmodel object | |
*/ | |
CompilerProto.createBinding = function (key, directive) { | |
utils.log(' created binding: ' + key) | |
var compiler = this, | |
methods = compiler.options.methods, | |
isExp = directive && directive.isExp, | |
isFn = (directive && directive.isFn) || (methods && methods[key]), | |
bindings = compiler.bindings, | |
computed = compiler.options.computed, | |
binding = new Binding(compiler, key, isExp, isFn) | |
if (isExp) { | |
// expression bindings are anonymous | |
compiler.defineExp(key, binding, directive) | |
} else if (isFn) { | |
bindings[key] = binding | |
binding.value = compiler.vm[key] = methods[key] | |
} else { | |
bindings[key] = binding | |
if (binding.root) { | |
// this is a root level binding. we need to define getter/setters for it. | |
if (computed && computed[key]) { | |
// computed property | |
compiler.defineComputed(key, binding, computed[key]) | |
} else if (key.charAt(0) !== '$') { | |
// normal property | |
compiler.defineProp(key, binding) | |
} else { | |
compiler.defineMeta(key, binding) | |
} | |
} else if (computed && computed[utils.baseKey(key)]) { | |
// nested path on computed property | |
compiler.defineExp(key, binding) | |
} else { | |
// ensure path in data so that computed properties that | |
// access the path don't throw an error and can collect | |
// dependencies | |
Observer.ensurePath(compiler.data, key) | |
var parentKey = key.slice(0, key.lastIndexOf('.')) | |
if (!bindings[parentKey]) { | |
// this is a nested value binding, but the binding for its parent | |
// has not been created yet. We better create that one too. | |
compiler.createBinding(parentKey) | |
} | |
} | |
} | |
return binding | |
} | |
/** | |
* Define the getter/setter for a root-level property on the VM | |
* and observe the initial value | |
*/ | |
CompilerProto.defineProp = function (key, binding) { | |
var compiler = this, | |
data = compiler.data, | |
ob = data.__emitter__ | |
// make sure the key is present in data | |
// so it can be observed | |
if (!(hasOwn.call(data, key))) { | |
data[key] = undefined | |
} | |
// if the data object is already observed, but the key | |
// is not observed, we need to add it to the observed keys. | |
if (ob && !(hasOwn.call(ob.values, key))) { | |
Observer.convertKey(data, key) | |
} | |
binding.value = data[key] | |
def(compiler.vm, key, { | |
get: function () { | |
return compiler.data[key] | |
}, | |
set: function (val) { | |
compiler.data[key] = val | |
} | |
}) | |
} | |
/** | |
* Define a meta property, e.g. $index or $key, | |
* which is bindable but only accessible on the VM, | |
* not in the data. | |
*/ | |
CompilerProto.defineMeta = function (key, binding) { | |
var ob = this.observer | |
binding.value = this.data[key] | |
delete this.data[key] | |
def(this.vm, key, { | |
get: function () { | |
if (Observer.shouldGet) ob.emit('get', key) | |
return binding.value | |
}, | |
set: function (val) { | |
ob.emit('set', key, val) | |
} | |
}) | |
} | |
/** | |
* Define an expression binding, which is essentially | |
* an anonymous computed property | |
*/ | |
CompilerProto.defineExp = function (key, binding, directive) { | |
var computedKey = directive && directive.computedKey, | |
exp = computedKey ? directive.expression : key, | |
getter = this.expCache[exp] | |
if (!getter) { | |
getter = this.expCache[exp] = ExpParser.parse(computedKey || key, this) | |
} | |
if (getter) { | |
this.markComputed(binding, getter) | |
} | |
} | |
/** | |
* Define a computed property on the VM | |
*/ | |
CompilerProto.defineComputed = function (key, binding, value) { | |
this.markComputed(binding, value) | |
def(this.vm, key, { | |
get: binding.value.$get, | |
set: binding.value.$set | |
}) | |
} | |
/** | |
* Process a computed property binding | |
* so its getter/setter are bound to proper context | |
*/ | |
CompilerProto.markComputed = function (binding, value) { | |
binding.isComputed = true | |
// bind the accessors to the vm | |
if (binding.isFn) { | |
binding.value = value | |
} else { | |
if (typeof value === 'function') { | |
value = { $get: value } | |
} | |
binding.value = { | |
$get: utils.bind(value.$get, this.vm), | |
$set: value.$set | |
? utils.bind(value.$set, this.vm) | |
: undefined | |
} | |
} | |
// keep track for dep parsing later | |
this.computed.push(binding) | |
} | |
/** | |
* Retrive an option from the compiler | |
*/ | |
CompilerProto.getOption = function (type, id, silent) { | |
var opts = this.options, | |
parent = this.parent, | |
globalAssets = config.globalAssets, | |
res = (opts[type] && opts[type][id]) || ( | |
parent | |
? parent.getOption(type, id, silent) | |
: globalAssets[type] && globalAssets[type][id] | |
) | |
if (!res && !silent && typeof id === 'string') { | |
utils.warn('Unknown ' + type.slice(0, -1) + ': ' + id) | |
} | |
return res | |
} | |
/** | |
* Emit lifecycle events to trigger hooks | |
*/ | |
CompilerProto.execHook = function (event) { | |
event = 'hook:' + event | |
this.observer.emit(event) | |
this.emitter.emit(event) | |
} | |
/** | |
* Check if a compiler's data contains a keypath | |
*/ | |
CompilerProto.hasKey = function (key) { | |
var baseKey = utils.baseKey(key) | |
return hasOwn.call(this.data, baseKey) || | |
hasOwn.call(this.vm, baseKey) | |
} | |
/** | |
* Do a one-time eval of a string that potentially | |
* includes bindings. It accepts additional raw data | |
* because we need to dynamically resolve v-component | |
* before a childVM is even compiled... | |
*/ | |
CompilerProto.eval = function (exp, data) { | |
var parsed = TextParser.parseAttr(exp) | |
return parsed | |
? ExpParser.eval(parsed, this, data) | |
: exp | |
} | |
/** | |
* Resolve a Component constructor for an element | |
* with the data to be used | |
*/ | |
CompilerProto.resolveComponent = function (node, data, test) { | |
// late require to avoid circular deps | |
ViewModel = ViewModel || require('./viewmodel') | |
var exp = utils.attr(node, 'component'), | |
tagName = node.tagName, | |
id = this.eval(exp, data), | |
tagId = (tagName.indexOf('-') > 0 && tagName.toLowerCase()), | |
Ctor = this.getOption('components', id || tagId, true) | |
if (id && !Ctor) { | |
utils.warn('Unknown component: ' + id) | |
} | |
return test | |
? exp === '' | |
? ViewModel | |
: Ctor | |
: Ctor || ViewModel | |
} | |
/** | |
* Unbind and remove element | |
*/ | |
CompilerProto.destroy = function () { | |
// avoid being called more than once | |
// this is irreversible! | |
if (this.destroyed) return | |
var compiler = this, | |
i, j, key, dir, dirs, binding, | |
vm = compiler.vm, | |
el = compiler.el, | |
directives = compiler.dirs, | |
computed = compiler.computed, | |
bindings = compiler.bindings, | |
children = compiler.children, | |
parent = compiler.parent | |
compiler.execHook('beforeDestroy') | |
// unobserve data | |
Observer.unobserve(compiler.data, '', compiler.observer) | |
// unbind all direcitves | |
i = directives.length | |
while (i--) { | |
dir = directives[i] | |
// if this directive is an instance of an external binding | |
// e.g. a directive that refers to a variable on the parent VM | |
// we need to remove it from that binding's directives | |
// * empty and literal bindings do not have binding. | |
if (dir.binding && dir.binding.compiler !== compiler) { | |
dirs = dir.binding.dirs | |
if (dirs) { | |
j = dirs.indexOf(dir) | |
if (j > -1) dirs.splice(j, 1) | |
} | |
} | |
dir.$unbind() | |
} | |
// unbind all computed, anonymous bindings | |
i = computed.length | |
while (i--) { | |
computed[i].unbind() | |
} | |
// unbind all keypath bindings | |
for (key in bindings) { | |
binding = bindings[key] | |
if (binding) { | |
binding.unbind() | |
} | |
} | |
// destroy all children | |
i = children.length | |
while (i--) { | |
children[i].destroy() | |
} | |
// remove self from parent | |
if (parent) { | |
j = parent.children.indexOf(compiler) | |
if (j > -1) parent.children.splice(j, 1) | |
} | |
// finally remove dom element | |
if (el === document.body) { | |
el.innerHTML = '' | |
} else { | |
vm.$remove() | |
} | |
el.vue_vm = null | |
compiler.destroyed = true | |
// emit destroy hook | |
compiler.execHook('afterDestroy') | |
// finally, unregister all listeners | |
compiler.observer.off() | |
compiler.emitter.off() | |
} | |
// Helpers -------------------------------------------------------------------- | |
/** | |
* shorthand for getting root compiler | |
*/ | |
function getRoot (compiler) { | |
while (compiler.parent) { | |
compiler = compiler.parent | |
} | |
return compiler | |
} | |
module.exports = Compiler | |
}); | |
require.register("vue/src/viewmodel.js", function(exports, require, module){ | |
var Compiler = require('./compiler'), | |
utils = require('./utils'), | |
transition = require('./transition'), | |
Batcher = require('./batcher'), | |
slice = [].slice, | |
def = utils.defProtected, | |
nextTick = utils.nextTick, | |
// batch $watch callbacks | |
watcherBatcher = new Batcher(), | |
watcherId = 1 | |
/** | |
* ViewModel exposed to the user that holds data, | |
* computed properties, event handlers | |
* and a few reserved methods | |
*/ | |
function ViewModel (options) { | |
// just compile. options are passed directly to compiler | |
new Compiler(this, options) | |
} | |
// All VM prototype methods are inenumerable | |
// so it can be stringified/looped through as raw data | |
var VMProto = ViewModel.prototype | |
/** | |
* Convenience function to get a value from | |
* a keypath | |
*/ | |
def(VMProto, '$get', function (key) { | |
var val = utils.get(this, key) | |
return val === undefined && this.$parent | |
? this.$parent.$get(key) | |
: val | |
}) | |
/** | |
* Convenience function to set an actual nested value | |
* from a flat key string. Used in directives. | |
*/ | |
def(VMProto, '$set', function (key, value) { | |
utils.set(this, key, value) | |
}) | |
/** | |
* watch a key on the viewmodel for changes | |
* fire callback with new value | |
*/ | |
def(VMProto, '$watch', function (key, callback) { | |
// save a unique id for each watcher | |
var id = watcherId++, | |
self = this | |
function on () { | |
var args = slice.call(arguments) | |
watcherBatcher.push({ | |
id: id, | |
override: true, | |
execute: function () { | |
callback.apply(self, args) | |
} | |
}) | |
} | |
callback._fn = on | |
self.$compiler.observer.on('change:' + key, on) | |
}) | |
/** | |
* unwatch a key | |
*/ | |
def(VMProto, '$unwatch', function (key, callback) { | |
// workaround here | |
// since the emitter module checks callback existence | |
// by checking the length of arguments | |
var args = ['change:' + key], | |
ob = this.$compiler.observer | |
if (callback) args.push(callback._fn) | |
ob.off.apply(ob, args) | |
}) | |
/** | |
* unbind everything, remove everything | |
*/ | |
def(VMProto, '$destroy', function () { | |
this.$compiler.destroy() | |
}) | |
/** | |
* broadcast an event to all child VMs recursively. | |
*/ | |
def(VMProto, '$broadcast', function () { | |
var children = this.$compiler.children, | |
i = children.length, | |
child | |
while (i--) { | |
child = children[i] | |
child.emitter.applyEmit.apply(child.emitter, arguments) | |
child.vm.$broadcast.apply(child.vm, arguments) | |
} | |
}) | |
/** | |
* emit an event that propagates all the way up to parent VMs. | |
*/ | |
def(VMProto, '$dispatch', function () { | |
var compiler = this.$compiler, | |
emitter = compiler.emitter, | |
parent = compiler.parent | |
emitter.applyEmit.apply(emitter, arguments) | |
if (parent) { | |
parent.vm.$dispatch.apply(parent.vm, arguments) | |
} | |
}) | |
/** | |
* delegate on/off/once to the compiler's emitter | |
*/ | |
;['emit', 'on', 'off', 'once'].forEach(function (method) { | |
// internal emit has fixed number of arguments. | |
// exposed emit uses the external version | |
// with fn.apply. | |
var realMethod = method === 'emit' | |
? 'applyEmit' | |
: method | |
def(VMProto, '$' + method, function () { | |
var emitter = this.$compiler.emitter | |
emitter[realMethod].apply(emitter, arguments) | |
}) | |
}) | |
// DOM convenience methods | |
def(VMProto, '$appendTo', function (target, cb) { | |
target = query(target) | |
var el = this.$el | |
transition(el, 1, function () { | |
target.appendChild(el) | |
if (cb) nextTick(cb) | |
}, this.$compiler) | |
}) | |
def(VMProto, '$remove', function (cb) { | |
var el = this.$el | |
transition(el, -1, function () { | |
if (el.parentNode) { | |
el.parentNode.removeChild(el) | |
} | |
if (cb) nextTick(cb) | |
}, this.$compiler) | |
}) | |
def(VMProto, '$before', function (target, cb) { | |
target = query(target) | |
var el = this.$el | |
transition(el, 1, function () { | |
target.parentNode.insertBefore(el, target) | |
if (cb) nextTick(cb) | |
}, this.$compiler) | |
}) | |
def(VMProto, '$after', function (target, cb) { | |
target = query(target) | |
var el = this.$el | |
transition(el, 1, function () { | |
if (target.nextSibling) { | |
target.parentNode.insertBefore(el, target.nextSibling) | |
} else { | |
target.parentNode.appendChild(el) | |
} | |
if (cb) nextTick(cb) | |
}, this.$compiler) | |
}) | |
function query (el) { | |
return typeof el === 'string' | |
? document.querySelector(el) | |
: el | |
} | |
module.exports = ViewModel | |
}); | |
require.register("vue/src/binding.js", function(exports, require, module){ | |
var Batcher = require('./batcher'), | |
bindingBatcher = new Batcher(), | |
bindingId = 1 | |
/** | |
* Binding class. | |
* | |
* each property on the viewmodel has one corresponding Binding object | |
* which has multiple directive instances on the DOM | |
* and multiple computed property dependents | |
*/ | |
function Binding (compiler, key, isExp, isFn) { | |
this.id = bindingId++ | |
this.value = undefined | |
this.isExp = !!isExp | |
this.isFn = isFn | |
this.root = !this.isExp && key.indexOf('.') === -1 | |
this.compiler = compiler | |
this.key = key | |
this.dirs = [] | |
this.subs = [] | |
this.deps = [] | |
this.unbound = false | |
} | |
var BindingProto = Binding.prototype | |
/** | |
* Update value and queue instance updates. | |
*/ | |
BindingProto.update = function (value) { | |
if (!this.isComputed || this.isFn) { | |
this.value = value | |
} | |
if (this.dirs.length || this.subs.length) { | |
var self = this | |
bindingBatcher.push({ | |
id: this.id, | |
execute: function () { | |
if (!self.unbound) { | |
self._update() | |
} | |
} | |
}) | |
} | |
} | |
/** | |
* Actually update the directives. | |
*/ | |
BindingProto._update = function () { | |
var i = this.dirs.length, | |
value = this.val() | |
while (i--) { | |
this.dirs[i].$update(value) | |
} | |
this.pub() | |
} | |
/** | |
* Return the valuated value regardless | |
* of whether it is computed or not | |
*/ | |
BindingProto.val = function () { | |
return this.isComputed && !this.isFn | |
? this.value.$get() | |
: this.value | |
} | |
/** | |
* Notify computed properties that depend on this binding | |
* to update themselves | |
*/ | |
BindingProto.pub = function () { | |
var i = this.subs.length | |
while (i--) { | |
this.subs[i].update() | |
} | |
} | |
/** | |
* Unbind the binding, remove itself from all of its dependencies | |
*/ | |
BindingProto.unbind = function () { | |
// Indicate this has been unbound. | |
// It's possible this binding will be in | |
// the batcher's flush queue when its owner | |
// compiler has already been destroyed. | |
this.unbound = true | |
var i = this.dirs.length | |
while (i--) { | |
this.dirs[i].$unbind() | |
} | |
i = this.deps.length | |
var subs | |
while (i--) { | |
subs = this.deps[i].subs | |
var j = subs.indexOf(this) | |
if (j > -1) subs.splice(j, 1) | |
} | |
} | |
module.exports = Binding | |
}); | |
require.register("vue/src/observer.js", function(exports, require, module){ | |
/* jshint proto:true */ | |
var Emitter = require('./emitter'), | |
utils = require('./utils'), | |
// cache methods | |
def = utils.defProtected, | |
isObject = utils.isObject, | |
isArray = Array.isArray, | |
hasOwn = ({}).hasOwnProperty, | |
oDef = Object.defineProperty, | |
slice = [].slice, | |
// fix for IE + __proto__ problem | |
// define methods as inenumerable if __proto__ is present, | |
// otherwise enumerable so we can loop through and manually | |
// attach to array instances | |
hasProto = ({}).__proto__ | |
// Array Mutation Handlers & Augmentations ------------------------------------ | |
// The proxy prototype to replace the __proto__ of | |
// an observed array | |
var ArrayProxy = Object.create(Array.prototype) | |
// intercept mutation methods | |
;[ | |
'push', | |
'pop', | |
'shift', | |
'unshift', | |
'splice', | |
'sort', | |
'reverse' | |
].forEach(watchMutation) | |
// Augment the ArrayProxy with convenience methods | |
def(ArrayProxy, '$set', function (index, data) { | |
return this.splice(index, 1, data)[0] | |
}, !hasProto) | |
def(ArrayProxy, '$remove', function (index) { | |
if (typeof index !== 'number') { | |
index = this.indexOf(index) | |
} | |
if (index > -1) { | |
return this.splice(index, 1)[0] | |
} | |
}, !hasProto) | |
/** | |
* Intercep a mutation event so we can emit the mutation info. | |
* we also analyze what elements are added/removed and link/unlink | |
* them with the parent Array. | |
*/ | |
function watchMutation (method) { | |
def(ArrayProxy, method, function () { | |
var args = slice.call(arguments), | |
result = Array.prototype[method].apply(this, args), | |
inserted, removed | |
// determine new / removed elements | |
if (method === 'push' || method === 'unshift') { | |
inserted = args | |
} else if (method === 'pop' || method === 'shift') { | |
removed = [result] | |
} else if (method === 'splice') { | |
inserted = args.slice(2) | |
removed = result | |
} | |
// link & unlink | |
linkArrayElements(this, inserted) | |
unlinkArrayElements(this, removed) | |
// emit the mutation event | |
this.__emitter__.emit('mutate', '', this, { | |
method : method, | |
args : args, | |
result : result, | |
inserted : inserted, | |
removed : removed | |
}) | |
return result | |
}, !hasProto) | |
} | |
/** | |
* Link new elements to an Array, so when they change | |
* and emit events, the owner Array can be notified. | |
*/ | |
function linkArrayElements (arr, items) { | |
if (items) { | |
var i = items.length, item, owners | |
while (i--) { | |
item = items[i] | |
if (isWatchable(item)) { | |
// if object is not converted for observing | |
// convert it... | |
if (!item.__emitter__) { | |
convert(item) | |
watch(item) | |
} | |
owners = item.__emitter__.owners | |
if (owners.indexOf(arr) < 0) { | |
owners.push(arr) | |
} | |
} | |
} | |
} | |
} | |
/** | |
* Unlink removed elements from the ex-owner Array. | |
*/ | |
function unlinkArrayElements (arr, items) { | |
if (items) { | |
var i = items.length, item | |
while (i--) { | |
item = items[i] | |
if (item && item.__emitter__) { | |
var owners = item.__emitter__.owners | |
if (owners) owners.splice(owners.indexOf(arr)) | |
} | |
} | |
} | |
} | |
// Object add/delete key augmentation ----------------------------------------- | |
var ObjProxy = Object.create(Object.prototype) | |
def(ObjProxy, '$add', function (key, val) { | |
if (hasOwn.call(this, key)) return | |
this[key] = val | |
convertKey(this, key, true) | |
}, !hasProto) | |
def(ObjProxy, '$delete', function (key) { | |
if (!(hasOwn.call(this, key))) return | |
// trigger set events | |
this[key] = undefined | |
delete this[key] | |
this.__emitter__.emit('delete', key) | |
}, !hasProto) | |
// Watch Helpers -------------------------------------------------------------- | |
/** | |
* Check if a value is watchable | |
*/ | |
function isWatchable (obj) { | |
return typeof obj === 'object' && obj && !obj.$compiler | |
} | |
/** | |
* Convert an Object/Array to give it a change emitter. | |
*/ | |
function convert (obj) { | |
if (obj.__emitter__) return true | |
var emitter = new Emitter() | |
def(obj, '__emitter__', emitter) | |
emitter | |
.on('set', function (key, val, propagate) { | |
if (propagate) propagateChange(obj) | |
}) | |
.on('mutate', function () { | |
propagateChange(obj) | |
}) | |
emitter.values = utils.hash() | |
emitter.owners = [] | |
return false | |
} | |
/** | |
* Propagate an array element's change to its owner arrays | |
*/ | |
function propagateChange (obj) { | |
var owners = obj.__emitter__.owners, | |
i = owners.length | |
while (i--) { | |
owners[i].__emitter__.emit('set', '', '', true) | |
} | |
} | |
/** | |
* Watch target based on its type | |
*/ | |
function watch (obj) { | |
if (isArray(obj)) { | |
watchArray(obj) | |
} else { | |
watchObject(obj) | |
} | |
} | |
/** | |
* Augment target objects with modified | |
* methods | |
*/ | |
function augment (target, src) { | |
if (hasProto) { | |
target.__proto__ = src | |
} else { | |
for (var key in src) { | |
def(target, key, src[key]) | |
} | |
} | |
} | |
/** | |
* Watch an Object, recursive. | |
*/ | |
function watchObject (obj) { | |
augment(obj, ObjProxy) | |
for (var key in obj) { | |
convertKey(obj, key) | |
} | |
} | |
/** | |
* Watch an Array, overload mutation methods | |
* and add augmentations by intercepting the prototype chain | |
*/ | |
function watchArray (arr) { | |
augment(arr, ArrayProxy) | |
linkArrayElements(arr, arr) | |
} | |
/** | |
* Define accessors for a property on an Object | |
* so it emits get/set events. | |
* Then watch the value itself. | |
*/ | |
function convertKey (obj, key, propagate) { | |
var keyPrefix = key.charAt(0) | |
if (keyPrefix === '$' || keyPrefix === '_') { | |
return | |
} | |
// emit set on bind | |
// this means when an object is observed it will emit | |
// a first batch of set events. | |
var emitter = obj.__emitter__, | |
values = emitter.values | |
init(obj[key], propagate) | |
oDef(obj, key, { | |
enumerable: true, | |
configurable: true, | |
get: function () { | |
var value = values[key] | |
// only emit get on tip values | |
if (pub.shouldGet) { | |
emitter.emit('get', key) | |
} | |
return value | |
}, | |
set: function (newVal) { | |
var oldVal = values[key] | |
unobserve(oldVal, key, emitter) | |
copyPaths(newVal, oldVal) | |
// an immediate property should notify its parent | |
// to emit set for itself too | |
init(newVal, true) | |
} | |
}) | |
function init (val, propagate) { | |
values[key] = val | |
emitter.emit('set', key, val, propagate) | |
if (isArray(val)) { | |
emitter.emit('set', key + '.length', val.length, propagate) | |
} | |
observe(val, key, emitter) | |
} | |
} | |
/** | |
* When a value that is already converted is | |
* observed again by another observer, we can skip | |
* the watch conversion and simply emit set event for | |
* all of its properties. | |
*/ | |
function emitSet (obj) { | |
var emitter = obj && obj.__emitter__ | |
if (!emitter) return | |
if (isArray(obj)) { | |
emitter.emit('set', 'length', obj.length) | |
} else { | |
var key, val | |
for (key in obj) { | |
val = obj[key] | |
emitter.emit('set', key, val) | |
emitSet(val) | |
} | |
} | |
} | |
/** | |
* Make sure all the paths in an old object exists | |
* in a new object. | |
* So when an object changes, all missing keys will | |
* emit a set event with undefined value. | |
*/ | |
function copyPaths (newObj, oldObj) { | |
if (!isObject(newObj) || !isObject(oldObj)) { | |
return | |
} | |
var path, oldVal, newVal | |
for (path in oldObj) { | |
if (!(hasOwn.call(newObj, path))) { | |
oldVal = oldObj[path] | |
if (isArray(oldVal)) { | |
newObj[path] = [] | |
} else if (isObject(oldVal)) { | |
newVal = newObj[path] = {} | |
copyPaths(newVal, oldVal) | |
} else { | |
newObj[path] = undefined | |
} | |
} | |
} | |
} | |
/** | |
* walk along a path and make sure it can be accessed | |
* and enumerated in that object | |
*/ | |
function ensurePath (obj, key) { | |
var path = key.split('.'), sec | |
for (var i = 0, d = path.length - 1; i < d; i++) { | |
sec = path[i] | |
if (!obj[sec]) { | |
obj[sec] = {} | |
if (obj.__emitter__) convertKey(obj, sec) | |
} | |
obj = obj[sec] | |
} | |
if (isObject(obj)) { | |
sec = path[i] | |
if (!(hasOwn.call(obj, sec))) { | |
obj[sec] = undefined | |
if (obj.__emitter__) convertKey(obj, sec) | |
} | |
} | |
} | |
// Main API Methods ----------------------------------------------------------- | |
/** | |
* Observe an object with a given path, | |
* and proxy get/set/mutate events to the provided observer. | |
*/ | |
function observe (obj, rawPath, observer) { | |
if (!isWatchable(obj)) return | |
var path = rawPath ? rawPath + '.' : '', | |
alreadyConverted = convert(obj), | |
emitter = obj.__emitter__ | |
// setup proxy listeners on the parent observer. | |
// we need to keep reference to them so that they | |
// can be removed when the object is un-observed. | |
observer.proxies = observer.proxies || {} | |
var proxies = observer.proxies[path] = { | |
get: function (key) { | |
observer.emit('get', path + key) | |
}, | |
set: function (key, val, propagate) { | |
if (key) observer.emit('set', path + key, val) | |
// also notify observer that the object itself changed | |
// but only do so when it's a immediate property. this | |
// avoids duplicate event firing. | |
if (rawPath && propagate) { | |
observer.emit('set', rawPath, obj, true) | |
} | |
}, | |
mutate: function (key, val, mutation) { | |
// if the Array is a root value | |
// the key will be null | |
var fixedPath = key ? path + key : rawPath | |
observer.emit('mutate', fixedPath, val, mutation) | |
// also emit set for Array's length when it mutates | |
var m = mutation.method | |
if (m !== 'sort' && m !== 'reverse') { | |
observer.emit('set', fixedPath + '.length', val.length) | |
} | |
} | |
} | |
// attach the listeners to the child observer. | |
// now all the events will propagate upwards. | |
emitter | |
.on('get', proxies.get) | |
.on('set', proxies.set) | |
.on('mutate', proxies.mutate) | |
if (alreadyConverted) { | |
// for objects that have already been converted, | |
// emit set events for everything inside | |
emitSet(obj) | |
} else { | |
watch(obj) | |
} | |
} | |
/** | |
* Cancel observation, turn off the listeners. | |
*/ | |
function unobserve (obj, path, observer) { | |
if (!obj || !obj.__emitter__) return | |
path = path ? path + '.' : '' | |
var proxies = observer.proxies[path] | |
if (!proxies) return | |
// turn off listeners | |
obj.__emitter__ | |
.off('get', proxies.get) | |
.off('set', proxies.set) | |
.off('mutate', proxies.mutate) | |
// remove reference | |
observer.proxies[path] = null | |
} | |
// Expose API ----------------------------------------------------------------- | |
var pub = module.exports = { | |
// whether to emit get events | |
// only enabled during dependency parsing | |
shouldGet : false, | |
observe : observe, | |
unobserve : unobserve, | |
ensurePath : ensurePath, | |
copyPaths : copyPaths, | |
watch : watch, | |
convert : convert, | |
convertKey : convertKey | |
} | |
}); | |
require.register("vue/src/directive.js", function(exports, require, module){ | |
var dirId = 1, | |
ARG_RE = /^[\w\$-]+$/, | |
FILTER_TOKEN_RE = /[^\s'"]+|'[^']+'|"[^"]+"/g, | |
NESTING_RE = /^\$(parent|root)\./, | |
SINGLE_VAR_RE = /^[\w\.$]+$/, | |
QUOTE_RE = /"/g | |
/** | |
* Directive class | |
* represents a single directive instance in the DOM | |
*/ | |
function Directive (name, ast, definition, compiler, el) { | |
this.id = dirId++ | |
this.name = name | |
this.compiler = compiler | |
this.vm = compiler.vm | |
this.el = el | |
this.computeFilters = false | |
this.key = ast.key | |
this.arg = ast.arg | |
this.expression = ast.expression | |
var isEmpty = this.expression === '' | |
// mix in properties from the directive definition | |
if (typeof definition === 'function') { | |
this[isEmpty ? 'bind' : 'update'] = definition | |
} else { | |
for (var prop in definition) { | |
this[prop] = definition[prop] | |
} | |
} | |
// empty expression, we're done. | |
if (isEmpty || this.isEmpty) { | |
this.isEmpty = true | |
return | |
} | |
this.expression = ( | |
this.isLiteral | |
? compiler.eval(this.expression) | |
: this.expression | |
).trim() | |
var filters = ast.filters, | |
filter, fn, i, l, computed | |
if (filters) { | |
this.filters = [] | |
for (i = 0, l = filters.length; i < l; i++) { | |
filter = filters[i] | |
fn = this.compiler.getOption('filters', filter.name) | |
if (fn) { | |
filter.apply = fn | |
this.filters.push(filter) | |
if (fn.computed) { | |
computed = true | |
} | |
} | |
} | |
} | |
if (!this.filters || !this.filters.length) { | |
this.filters = null | |
} | |
if (computed) { | |
this.computedKey = Directive.inlineFilters(this.key, this.filters) | |
this.filters = null | |
} | |
this.isExp = | |
computed || | |
!SINGLE_VAR_RE.test(this.key) || | |
NESTING_RE.test(this.key) | |
} | |
var DirProto = Directive.prototype | |
/** | |
* called when a new value is set | |
* for computed properties, this will only be called once | |
* during initialization. | |
*/ | |
DirProto.$update = function (value, init) { | |
if (this.$lock) return | |
if (init || value !== this.value || (value && typeof value === 'object')) { | |
this.value = value | |
if (this.update) { | |
this.update( | |
this.filters && !this.computeFilters | |
? this.$applyFilters(value) | |
: value, | |
init | |
) | |
} | |
} | |
} | |
/** | |
* pipe the value through filters | |
*/ | |
DirProto.$applyFilters = function (value) { | |
var filtered = value, filter | |
for (var i = 0, l = this.filters.length; i < l; i++) { | |
filter = this.filters[i] | |
filtered = filter.apply.apply(this.vm, [filtered].concat(filter.args)) | |
} | |
return filtered | |
} | |
/** | |
* Unbind diretive | |
*/ | |
DirProto.$unbind = function () { | |
// this can be called before the el is even assigned... | |
if (!this.el || !this.vm) return | |
if (this.unbind) this.unbind() | |
this.vm = this.el = this.binding = this.compiler = null | |
} | |
// Exposed static methods ----------------------------------------------------- | |
/** | |
* Parse a directive string into an Array of | |
* AST-like objects representing directives | |
*/ | |
Directive.parse = function (str) { | |
var inSingle = false, | |
inDouble = false, | |
curly = 0, | |
square = 0, | |
paren = 0, | |
begin = 0, | |
argIndex = 0, | |
dirs = [], | |
dir = {}, | |
lastFilterIndex = 0, | |
arg | |
for (var c, i = 0, l = str.length; i < l; i++) { | |
c = str.charAt(i) | |
if (inSingle) { | |
// check single quote | |
if (c === "'") inSingle = !inSingle | |
} else if (inDouble) { | |
// check double quote | |
if (c === '"') inDouble = !inDouble | |
} else if (c === ',' && !paren && !curly && !square) { | |
// reached the end of a directive | |
pushDir() | |
// reset & skip the comma | |
dir = {} | |
begin = argIndex = lastFilterIndex = i + 1 | |
} else if (c === ':' && !dir.key && !dir.arg) { | |
// argument | |
arg = str.slice(begin, i).trim() | |
if (ARG_RE.test(arg)) { | |
argIndex = i + 1 | |
dir.arg = str.slice(begin, i).trim() | |
} | |
} else if (c === '|' && str.charAt(i + 1) !== '|' && str.charAt(i - 1) !== '|') { | |
if (dir.key === undefined) { | |
// first filter, end of key | |
lastFilterIndex = i + 1 | |
dir.key = str.slice(argIndex, i).trim() | |
} else { | |
// already has filter | |
pushFilter() | |
} | |
} else if (c === '"') { | |
inDouble = true | |
} else if (c === "'") { | |
inSingle = true | |
} else if (c === '(') { | |
paren++ | |
} else if (c === ')') { | |
paren-- | |
} else if (c === '[') { | |
square++ | |
} else if (c === ']') { | |
square-- | |
} else if (c === '{') { | |
curly++ | |
} else if (c === '}') { | |
curly-- | |
} | |
} | |
if (i === 0 || begin !== i) { | |
pushDir() | |
} | |
function pushDir () { | |
dir.expression = str.slice(begin, i).trim() | |
if (dir.key === undefined) { | |
dir.key = str.slice(argIndex, i).trim() | |
} else if (lastFilterIndex !== begin) { | |
pushFilter() | |
} | |
if (i === 0 || dir.key) { | |
dirs.push(dir) | |
} | |
} | |
function pushFilter () { | |
var exp = str.slice(lastFilterIndex, i).trim(), | |
filter | |
if (exp) { | |
filter = {} | |
var tokens = exp.match(FILTER_TOKEN_RE) | |
filter.name = tokens[0] | |
filter.args = tokens.length > 1 ? tokens.slice(1) : null | |
} | |
if (filter) { | |
(dir.filters = dir.filters || []).push(filter) | |
} | |
lastFilterIndex = i + 1 | |
} | |
return dirs | |
} | |
/** | |
* Inline computed filters so they become part | |
* of the expression | |
*/ | |
Directive.inlineFilters = function (key, filters) { | |
var args, filter | |
for (var i = 0, l = filters.length; i < l; i++) { | |
filter = filters[i] | |
args = filter.args | |
? ',"' + filter.args.map(escapeQuote).join('","') + '"' | |
: '' | |
key = 'this.$compiler.getOption("filters", "' + | |
filter.name + | |
'").call(this,' + | |
key + args + | |
')' | |
} | |
return key | |
} | |
/** | |
* Convert double quotes to single quotes | |
* so they don't mess up the generated function body | |
*/ | |
function escapeQuote (v) { | |
return v.indexOf('"') > -1 | |
? v.replace(QUOTE_RE, '\'') | |
: v | |
} | |
module.exports = Directive | |
}); | |
require.register("vue/src/exp-parser.js", function(exports, require, module){ | |
var utils = require('./utils'), | |
STR_SAVE_RE = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g, | |
STR_RESTORE_RE = /"(\d+)"/g, | |
NEWLINE_RE = /\n/g, | |
CTOR_RE = new RegExp('constructor'.split('').join('[\'"+, ]*')), | |
UNICODE_RE = /\\u\d\d\d\d/ | |
// Variable extraction scooped from https://github.com/RubyLouvre/avalon | |
var KEYWORDS = | |
// keywords | |
'break,case,catch,continue,debugger,default,delete,do,else,false' + | |
',finally,for,function,if,in,instanceof,new,null,return,switch,this' + | |
',throw,true,try,typeof,var,void,while,with,undefined' + | |
// reserved | |
',abstract,boolean,byte,char,class,const,double,enum,export,extends' + | |
',final,float,goto,implements,import,int,interface,long,native' + | |
',package,private,protected,public,short,static,super,synchronized' + | |
',throws,transient,volatile' + | |
// ECMA 5 - use strict | |
',arguments,let,yield' + | |
// allow using Math in expressions | |
',Math', | |
KEYWORDS_RE = new RegExp(["\\b" + KEYWORDS.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g'), | |
REMOVE_RE = /\/\*(?:.|\n)*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|'[^']*'|"[^"]*"|[\s\t\n]*\.[\s\t\n]*[$\w\.]+|[\{,]\s*[\w\$_]+\s*:/g, | |
SPLIT_RE = /[^\w$]+/g, | |
NUMBER_RE = /\b\d[^,]*/g, | |
BOUNDARY_RE = /^,+|,+$/g | |
/** | |
* Strip top level variable names from a snippet of JS expression | |
*/ | |
function getVariables (code) { | |
code = code | |
.replace(REMOVE_RE, '') | |
.replace(SPLIT_RE, ',') | |
.replace(KEYWORDS_RE, '') | |
.replace(NUMBER_RE, '') | |
.replace(BOUNDARY_RE, '') | |
return code | |
? code.split(/,+/) | |
: [] | |
} | |
/** | |
* A given path could potentially exist not on the | |
* current compiler, but up in the parent chain somewhere. | |
* This function generates an access relationship string | |
* that can be used in the getter function by walking up | |
* the parent chain to check for key existence. | |
* | |
* It stops at top parent if no vm in the chain has the | |
* key. It then creates any missing bindings on the | |
* final resolved vm. | |
*/ | |
function traceScope (path, compiler, data) { | |
var rel = '', | |
dist = 0, | |
self = compiler | |
if (data && utils.get(data, path) !== undefined) { | |
// hack: temporarily attached data | |
return '$temp.' | |
} | |
while (compiler) { | |
if (compiler.hasKey(path)) { | |
break | |
} else { | |
compiler = compiler.parent | |
dist++ | |
} | |
} | |
if (compiler) { | |
while (dist--) { | |
rel += '$parent.' | |
} | |
if (!compiler.bindings[path] && path.charAt(0) !== '$') { | |
compiler.createBinding(path) | |
} | |
} else { | |
self.createBinding(path) | |
} | |
return rel | |
} | |
/** | |
* Create a function from a string... | |
* this looks like evil magic but since all variables are limited | |
* to the VM's data it's actually properly sandboxed | |
*/ | |
function makeGetter (exp, raw) { | |
var fn | |
try { | |
fn = new Function(exp) | |
} catch (e) { | |
utils.warn('Error parsing expression: ' + raw) | |
} | |
return fn | |
} | |
/** | |
* Escape a leading dollar sign for regex construction | |
*/ | |
function escapeDollar (v) { | |
return v.charAt(0) === '$' | |
? '\\' + v | |
: v | |
} | |
/** | |
* Parse and return an anonymous computed property getter function | |
* from an arbitrary expression, together with a list of paths to be | |
* created as bindings. | |
*/ | |
exports.parse = function (exp, compiler, data) { | |
// unicode and 'constructor' are not allowed for XSS security. | |
if (UNICODE_RE.test(exp) || CTOR_RE.test(exp)) { | |
utils.warn('Unsafe expression: ' + exp) | |
return | |
} | |
// extract variable names | |
var vars = getVariables(exp) | |
if (!vars.length) { | |
return makeGetter('return ' + exp, exp) | |
} | |
vars = utils.unique(vars) | |
var accessors = '', | |
has = utils.hash(), | |
strings = [], | |
// construct a regex to extract all valid variable paths | |
// ones that begin with "$" are particularly tricky | |
// because we can't use \b for them | |
pathRE = new RegExp( | |
"[^$\\w\\.](" + | |
vars.map(escapeDollar).join('|') + | |
")[$\\w\\.]*\\b", 'g' | |
), | |
body = (' ' + exp) | |
.replace(STR_SAVE_RE, saveStrings) | |
.replace(pathRE, replacePath) | |
.replace(STR_RESTORE_RE, restoreStrings) | |
body = accessors + 'return ' + body | |
function saveStrings (str) { | |
var i = strings.length | |
// escape newlines in strings so the expression | |
// can be correctly evaluated | |
strings[i] = str.replace(NEWLINE_RE, '\\n') | |
return '"' + i + '"' | |
} | |
function replacePath (path) { | |
// keep track of the first char | |
var c = path.charAt(0) | |
path = path.slice(1) | |
var val = 'this.' + traceScope(path, compiler, data) + path | |
if (!has[path]) { | |
accessors += val + ';' | |
has[path] = 1 | |
} | |
// don't forget to put that first char back | |
return c + val | |
} | |
function restoreStrings (str, i) { | |
return strings[i] | |
} | |
return makeGetter(body, exp) | |
} | |
/** | |
* Evaluate an expression in the context of a compiler. | |
* Accepts additional data. | |
*/ | |
exports.eval = function (exp, compiler, data) { | |
var getter = exports.parse(exp, compiler, data), res | |
if (getter) { | |
// hack: temporarily attach the additional data so | |
// it can be accessed in the getter | |
compiler.vm.$temp = data | |
res = getter.call(compiler.vm) | |
delete compiler.vm.$temp | |
} | |
return res | |
} | |
}); | |
require.register("vue/src/text-parser.js", function(exports, require, module){ | |
var openChar = '{', | |
endChar = '}', | |
ESCAPE_RE = /[-.*+?^${}()|[\]\/\\]/g, | |
BINDING_RE = buildInterpolationRegex(), | |
// lazy require | |
Directive | |
function buildInterpolationRegex () { | |
var open = escapeRegex(openChar), | |
end = escapeRegex(endChar) | |
return new RegExp(open + open + open + '?(.+?)' + end + '?' + end + end) | |
} | |
function escapeRegex (str) { | |
return str.replace(ESCAPE_RE, '\\$&') | |
} | |
function setDelimiters (delimiters) { | |
exports.delimiters = delimiters | |
openChar = delimiters[0] | |
endChar = delimiters[1] | |
BINDING_RE = buildInterpolationRegex() | |
} | |
/** | |
* Parse a piece of text, return an array of tokens | |
* token types: | |
* 1. plain string | |
* 2. object with key = binding key | |
* 3. object with key & html = true | |
*/ | |
function parse (text) { | |
if (!BINDING_RE.test(text)) return null | |
var m, i, token, match, tokens = [] | |
/* jshint boss: true */ | |
while (m = text.match(BINDING_RE)) { | |
i = m.index | |
if (i > 0) tokens.push(text.slice(0, i)) | |
token = { key: m[1].trim() } | |
match = m[0] | |
token.html = | |
match.charAt(2) === openChar && | |
match.charAt(match.length - 3) === endChar | |
tokens.push(token) | |
text = text.slice(i + m[0].length) | |
} | |
if (text.length) tokens.push(text) | |
return tokens | |
} | |
/** | |
* Parse an attribute value with possible interpolation tags | |
* return a Directive-friendly expression | |
* | |
* e.g. a {{b}} c => "a " + b + " c" | |
*/ | |
function parseAttr (attr) { | |
Directive = Directive || require('./directive') | |
var tokens = parse(attr) | |
if (!tokens) return null | |
if (tokens.length === 1) return tokens[0].key | |
var res = [], token | |
for (var i = 0, l = tokens.length; i < l; i++) { | |
token = tokens[i] | |
res.push( | |
token.key | |
? inlineFilters(token.key) | |
: ('"' + token + '"') | |
) | |
} | |
return res.join('+') | |
} | |
/** | |
* Inlines any possible filters in a binding | |
* so that we can combine everything into a huge expression | |
*/ | |
function inlineFilters (key) { | |
if (key.indexOf('|') > -1) { | |
var dirs = Directive.parse(key), | |
dir = dirs && dirs[0] | |
if (dir && dir.filters) { | |
key = Directive.inlineFilters( | |
dir.key, | |
dir.filters | |
) | |
} | |
} | |
return '(' + key + ')' | |
} | |
exports.parse = parse | |
exports.parseAttr = parseAttr | |
exports.setDelimiters = setDelimiters | |
exports.delimiters = [openChar, endChar] | |
}); | |
require.register("vue/src/deps-parser.js", function(exports, require, module){ | |
var Emitter = require('./emitter'), | |
utils = require('./utils'), | |
Observer = require('./observer'), | |
catcher = new Emitter() | |
/** | |
* Auto-extract the dependencies of a computed property | |
* by recording the getters triggered when evaluating it. | |
*/ | |
function catchDeps (binding) { | |
if (binding.isFn) return | |
utils.log('\n- ' + binding.key) | |
var got = utils.hash() | |
binding.deps = [] | |
catcher.on('get', function (dep) { | |
var has = got[dep.key] | |
if ( | |
// avoid duplicate bindings | |
(has && has.compiler === dep.compiler) || | |
// avoid repeated items as dependency | |
// only when the binding is from self or the parent chain | |
(dep.compiler.repeat && !isParentOf(dep.compiler, binding.compiler)) | |
) { | |
return | |
} | |
got[dep.key] = dep | |
utils.log(' - ' + dep.key) | |
binding.deps.push(dep) | |
dep.subs.push(binding) | |
}) | |
binding.value.$get() | |
catcher.off('get') | |
} | |
/** | |
* Test if A is a parent of or equals B | |
*/ | |
function isParentOf (a, b) { | |
while (b) { | |
if (a === b) { | |
return true | |
} | |
b = b.parent | |
} | |
} | |
module.exports = { | |
/** | |
* the observer that catches events triggered by getters | |
*/ | |
catcher: catcher, | |
/** | |
* parse a list of computed property bindings | |
*/ | |
parse: function (bindings) { | |
utils.log('\nparsing dependencies...') | |
Observer.shouldGet = true | |
bindings.forEach(catchDeps) | |
Observer.shouldGet = false | |
utils.log('\ndone.') | |
} | |
} | |
}); | |
require.register("vue/src/filters.js", function(exports, require, module){ | |
var utils = require('./utils'), | |
get = utils.get, | |
slice = [].slice, | |
QUOTE_RE = /^'.*'$/, | |
filters = module.exports = utils.hash() | |
/** | |
* 'abc' => 'Abc' | |
*/ | |
filters.capitalize = function (value) { | |
if (!value && value !== 0) return '' | |
value = value.toString() | |
return value.charAt(0).toUpperCase() + value.slice(1) | |
} | |
/** | |
* 'abc' => 'ABC' | |
*/ | |
filters.uppercase = function (value) { | |
return (value || value === 0) | |
? value.toString().toUpperCase() | |
: '' | |
} | |
/** | |
* 'AbC' => 'abc' | |
*/ | |
filters.lowercase = function (value) { | |
return (value || value === 0) | |
? value.toString().toLowerCase() | |
: '' | |
} | |
/** | |
* 12345 => $12,345.00 | |
*/ | |
filters.currency = function (value, sign) { | |
if (!value && value !== 0) return '' | |
sign = sign || '$' | |
var s = Math.floor(value).toString(), | |
i = s.length % 3, | |
h = i > 0 ? (s.slice(0, i) + (s.length > 3 ? ',' : '')) : '', | |
f = '.' + value.toFixed(2).slice(-2) | |
return sign + h + s.slice(i).replace(/(\d{3})(?=\d)/g, '$1,') + f | |
} | |
/** | |
* args: 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'] | |
*/ | |
filters.pluralize = function (value) { | |
var args = slice.call(arguments, 1) | |
return args.length > 1 | |
? (args[value - 1] || args[args.length - 1]) | |
: (args[value - 1] || args[0] + 's') | |
} | |
/** | |
* A special filter that takes a handler function, | |
* wraps it so it only gets triggered on specific keypresses. | |
* | |
* v-on only | |
*/ | |
var keyCodes = { | |
enter : 13, | |
tab : 9, | |
'delete' : 46, | |
up : 38, | |
left : 37, | |
right : 39, | |
down : 40, | |
esc : 27 | |
} | |
filters.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) | |
} | |
} | |
} | |
/** | |
* Filter filter for v-repeat | |
*/ | |
filters.filterBy = function (arr, searchKey, delimiter, dataKey) { | |
// allow optional `in` delimiter | |
// because why not | |
if (delimiter && delimiter !== 'in') { | |
dataKey = delimiter | |
} | |
// get the search string | |
var search = stripQuotes(searchKey) || this.$get(searchKey) | |
if (!search) return arr | |
search = search.toLowerCase() | |
// get the optional dataKey | |
dataKey = dataKey && (stripQuotes(dataKey) || this.$get(dataKey)) | |
// convert object to array | |
if (!Array.isArray(arr)) { | |
arr = utils.objectToArray(arr) | |
} | |
return arr.filter(function (item) { | |
return dataKey | |
? contains(get(item, dataKey), search) | |
: contains(item, search) | |
}) | |
} | |
filters.filterBy.computed = true | |
/** | |
* Sort fitler for v-repeat | |
*/ | |
filters.orderBy = function (arr, sortKey, reverseKey) { | |
var key = stripQuotes(sortKey) || this.$get(sortKey) | |
if (!key) return arr | |
// convert object to array | |
if (!Array.isArray(arr)) { | |
arr = utils.objectToArray(arr) | |
} | |
var order = 1 | |
if (reverseKey) { | |
if (reverseKey === '-1') { | |
order = -1 | |
} else if (reverseKey.charAt(0) === '!') { | |
reverseKey = reverseKey.slice(1) | |
order = this.$get(reverseKey) ? 1 : -1 | |
} else { | |
order = this.$get(reverseKey) ? -1 : 1 | |
} | |
} | |
// sort on a copy to avoid mutating original array | |
return arr.slice().sort(function (a, b) { | |
a = get(a, key) | |
b = get(b, key) | |
return a === b ? 0 : a > b ? order : -order | |
}) | |
} | |
filters.orderBy.computed = true | |
// Array filter helpers ------------------------------------------------------- | |
/** | |
* String contain helper | |
*/ | |
function contains (val, search) { | |
/* jshint eqeqeq: false */ | |
if (utils.isObject(val)) { | |
for (var key in val) { | |
if (contains(val[key], search)) { | |
return true | |
} | |
} | |
} else if (val != null) { | |
return val.toString().toLowerCase().indexOf(search) > -1 | |
} | |
} | |
/** | |
* Test whether a string is in quotes, | |
* if yes return stripped string | |
*/ | |
function stripQuotes (str) { | |
if (QUOTE_RE.test(str)) { | |
return str.slice(1, -1) | |
} | |
} | |
}); | |
require.register("vue/src/transition.js", function(exports, require, module){ | |
var endEvents = sniffEndEvents(), | |
config = require('./config'), | |
// batch enter animations so we only force the layout once | |
Batcher = require('./batcher'), | |
batcher = new Batcher(), | |
// cache timer functions | |
setTO = window.setTimeout, | |
clearTO = window.clearTimeout, | |
// exit codes for testing | |
codes = { | |
CSS_E : 1, | |
CSS_L : 2, | |
JS_E : 3, | |
JS_L : 4, | |
CSS_SKIP : -1, | |
JS_SKIP : -2, | |
JS_SKIP_E : -3, | |
JS_SKIP_L : -4, | |
INIT : -5, | |
SKIP : -6 | |
} | |
// force layout before triggering transitions/animations | |
batcher._preFlush = function () { | |
/* jshint unused: false */ | |
var f = document.body.offsetHeight | |
} | |
/** | |
* stage: | |
* 1 = enter | |
* 2 = leave | |
*/ | |
var transition = module.exports = function (el, stage, cb, compiler) { | |
var changeState = function () { | |
cb() | |
compiler.execHook(stage > 0 ? 'attached' : 'detached') | |
} | |
if (compiler.init) { | |
changeState() | |
return codes.INIT | |
} | |
var hasTransition = el.vue_trans === '', | |
hasAnimation = el.vue_anim === '', | |
effectId = el.vue_effect | |
if (effectId) { | |
return applyTransitionFunctions( | |
el, | |
stage, | |
changeState, | |
effectId, | |
compiler | |
) | |
} else if (hasTransition || hasAnimation) { | |
return applyTransitionClass( | |
el, | |
stage, | |
changeState, | |
hasAnimation | |
) | |
} else { | |
changeState() | |
return codes.SKIP | |
} | |
} | |
transition.codes = codes | |
/** | |
* Togggle a CSS class to trigger transition | |
*/ | |
function applyTransitionClass (el, stage, changeState, hasAnimation) { | |
if (!endEvents.trans) { | |
changeState() | |
return codes.CSS_SKIP | |
} | |
// if the browser supports transition, | |
// it must have classList... | |
var onEnd, | |
classList = el.classList, | |
existingCallback = el.vue_trans_cb, | |
enterClass = config.enterClass, | |
leaveClass = config.leaveClass, | |
endEvent = hasAnimation ? endEvents.anim : endEvents.trans | |
// cancel unfinished callbacks and jobs | |
if (existingCallback) { | |
el.removeEventListener(endEvent, existingCallback) | |
classList.remove(enterClass) | |
classList.remove(leaveClass) | |
el.vue_trans_cb = null | |
} | |
if (stage > 0) { // enter | |
// set to enter state before appending | |
classList.add(enterClass) | |
// append | |
changeState() | |
// trigger transition | |
if (!hasAnimation) { | |
batcher.push({ | |
execute: function () { | |
classList.remove(enterClass) | |
} | |
}) | |
} else { | |
onEnd = function (e) { | |
if (e.target === el) { | |
el.removeEventListener(endEvent, onEnd) | |
el.vue_trans_cb = null | |
classList.remove(enterClass) | |
} | |
} | |
el.addEventListener(endEvent, onEnd) | |
el.vue_trans_cb = onEnd | |
} | |
return codes.CSS_E | |
} else { // leave | |
if (el.offsetWidth || el.offsetHeight) { | |
// trigger hide transition | |
classList.add(leaveClass) | |
onEnd = function (e) { | |
if (e.target === el) { | |
el.removeEventListener(endEvent, onEnd) | |
el.vue_trans_cb = null | |
// actually remove node here | |
changeState() | |
classList.remove(leaveClass) | |
} | |
} | |
// attach transition end listener | |
el.addEventListener(endEvent, onEnd) | |
el.vue_trans_cb = onEnd | |
} else { | |
// directly remove invisible elements | |
changeState() | |
} | |
return codes.CSS_L | |
} | |
} | |
function applyTransitionFunctions (el, stage, changeState, effectId, compiler) { | |
var funcs = compiler.getOption('effects', effectId) | |
if (!funcs) { | |
changeState() | |
return codes.JS_SKIP | |
} | |
var enter = funcs.enter, | |
leave = funcs.leave, | |
timeouts = el.vue_timeouts | |
// clear previous timeouts | |
if (timeouts) { | |
var i = timeouts.length | |
while (i--) { | |
clearTO(timeouts[i]) | |
} | |
} | |
timeouts = el.vue_timeouts = [] | |
function timeout (cb, delay) { | |
var id = setTO(function () { | |
cb() | |
timeouts.splice(timeouts.indexOf(id), 1) | |
if (!timeouts.length) { | |
el.vue_timeouts = null | |
} | |
}, delay) | |
timeouts.push(id) | |
} | |
if (stage > 0) { // enter | |
if (typeof enter !== 'function') { | |
changeState() | |
return codes.JS_SKIP_E | |
} | |
enter(el, changeState, timeout) | |
return codes.JS_E | |
} else { // leave | |
if (typeof leave !== 'function') { | |
changeState() | |
return codes.JS_SKIP_L | |
} | |
leave(el, changeState, timeout) | |
return codes.JS_L | |
} | |
} | |
/** | |
* Sniff proper transition end event name | |
*/ | |
function sniffEndEvents () { | |
var el = document.createElement('vue'), | |
defaultEvent = 'transitionend', | |
events = { | |
'transition' : defaultEvent, | |
'mozTransition' : defaultEvent, | |
'webkitTransition' : 'webkitTransitionEnd' | |
}, | |
ret = {} | |
for (var name in events) { | |
if (el.style[name] !== undefined) { | |
ret.trans = events[name] | |
break | |
} | |
} | |
ret.anim = el.style.animation === '' | |
? 'animationend' | |
: 'webkitAnimationEnd' | |
return ret | |
} | |
}); | |
require.register("vue/src/batcher.js", function(exports, require, module){ | |
var utils = require('./utils') | |
function Batcher () { | |
this.reset() | |
} | |
var BatcherProto = Batcher.prototype | |
BatcherProto.push = function (job) { | |
if (!job.id || !this.has[job.id]) { | |
this.queue.push(job) | |
this.has[job.id] = job | |
if (!this.waiting) { | |
this.waiting = true | |
utils.nextTick(utils.bind(this.flush, this)) | |
} | |
} else if (job.override) { | |
var oldJob = this.has[job.id] | |
oldJob.cancelled = true | |
this.queue.push(job) | |
this.has[job.id] = job | |
} | |
} | |
BatcherProto.flush = function () { | |
// before flush hook | |
if (this._preFlush) this._preFlush() | |
// do not cache length because more jobs might be pushed | |
// as we execute existing jobs | |
for (var i = 0; i < this.queue.length; i++) { | |
var job = this.queue[i] | |
if (!job.cancelled) { | |
job.execute() | |
} | |
} | |
this.reset() | |
} | |
BatcherProto.reset = function () { | |
this.has = utils.hash() | |
this.queue = [] | |
this.waiting = false | |
} | |
module.exports = Batcher | |
}); | |
require.register("vue/src/directives/index.js", function(exports, require, module){ | |
var utils = require('../utils'), | |
config = require('../config'), | |
transition = require('../transition'), | |
directives = module.exports = utils.hash() | |
/** | |
* Nest and manage a Child VM | |
*/ | |
directives.component = { | |
isLiteral: true, | |
bind: function () { | |
if (!this.el.vue_vm) { | |
this.childVM = new this.Ctor({ | |
el: this.el, | |
parent: this.vm | |
}) | |
} | |
}, | |
unbind: function () { | |
if (this.childVM) { | |
this.childVM.$destroy() | |
} | |
} | |
} | |
/** | |
* Binding HTML attributes | |
*/ | |
directives.attr = { | |
bind: function () { | |
var params = this.vm.$options.paramAttributes | |
this.isParam = params && params.indexOf(this.arg) > -1 | |
}, | |
update: function (value) { | |
if (value || value === 0) { | |
this.el.setAttribute(this.arg, value) | |
} else { | |
this.el.removeAttribute(this.arg) | |
} | |
if (this.isParam) { | |
this.vm[this.arg] = utils.checkNumber(value) | |
} | |
} | |
} | |
/** | |
* Binding textContent | |
*/ | |
directives.text = { | |
bind: function () { | |
this.attr = this.el.nodeType === 3 | |
? 'nodeValue' | |
: 'textContent' | |
}, | |
update: function (value) { | |
this.el[this.attr] = utils.guard(value) | |
} | |
} | |
/** | |
* Binding CSS display property | |
*/ | |
directives.show = function (value) { | |
var el = this.el, | |
target = value ? '' : 'none', | |
change = function () { | |
el.style.display = target | |
} | |
transition(el, value ? 1 : -1, change, this.compiler) | |
} | |
/** | |
* Binding CSS classes | |
*/ | |
directives['class'] = function (value) { | |
if (this.arg) { | |
utils[value ? 'addClass' : 'removeClass'](this.el, this.arg) | |
} else { | |
if (this.lastVal) { | |
utils.removeClass(this.el, this.lastVal) | |
} | |
if (value) { | |
utils.addClass(this.el, value) | |
this.lastVal = value | |
} | |
} | |
} | |
/** | |
* Only removed after the owner VM is ready | |
*/ | |
directives.cloak = { | |
isEmpty: true, | |
bind: function () { | |
var el = this.el | |
this.compiler.observer.once('hook:ready', function () { | |
el.removeAttribute(config.prefix + '-cloak') | |
}) | |
} | |
} | |
/** | |
* Store a reference to self in parent VM's $ | |
*/ | |
directives.ref = { | |
isLiteral: true, | |
bind: function () { | |
var id = this.expression | |
if (id) { | |
this.vm.$parent.$[id] = this.vm | |
} | |
}, | |
unbind: function () { | |
var id = this.expression | |
if (id) { | |
delete this.vm.$parent.$[id] | |
} | |
} | |
} | |
directives.on = require('./on') | |
directives.repeat = require('./repeat') | |
directives.model = require('./model') | |
directives['if'] = require('./if') | |
directives['with'] = require('./with') | |
directives.html = require('./html') | |
directives.style = require('./style') | |
directives.partial = require('./partial') | |
directives.view = require('./view') | |
}); | |
require.register("vue/src/directives/if.js", function(exports, require, module){ | |
var utils = require('../utils') | |
/** | |
* Manages a conditional child VM | |
*/ | |
module.exports = { | |
bind: function () { | |
this.parent = this.el.parentNode | |
this.ref = document.createComment('vue-if') | |
this.Ctor = this.compiler.resolveComponent(this.el) | |
// insert ref | |
this.parent.insertBefore(this.ref, this.el) | |
this.parent.removeChild(this.el) | |
if (utils.attr(this.el, 'view')) { | |
utils.warn( | |
'Conflict: v-if cannot be used together with v-view. ' + | |
'Just set v-view\'s binding value to empty string to empty it.' | |
) | |
} | |
if (utils.attr(this.el, 'repeat')) { | |
utils.warn( | |
'Conflict: v-if cannot be used together with v-repeat. ' + | |
'Use `v-show` or the `filterBy` filter instead.' | |
) | |
} | |
}, | |
update: function (value) { | |
if (!value) { | |
this.unbind() | |
} else if (!this.childVM) { | |
this.childVM = new this.Ctor({ | |
el: this.el.cloneNode(true), | |
parent: this.vm | |
}) | |
if (this.compiler.init) { | |
this.parent.insertBefore(this.childVM.$el, this.ref) | |
} else { | |
this.childVM.$before(this.ref) | |
} | |
} | |
}, | |
unbind: function () { | |
if (this.childVM) { | |
this.childVM.$destroy() | |
this.childVM = null | |
} | |
} | |
} | |
}); | |
require.register("vue/src/directives/repeat.js", function(exports, require, module){ | |
var utils = require('../utils'), | |
config = require('../config') | |
/** | |
* Binding that manages VMs based on an Array | |
*/ | |
module.exports = { | |
bind: function () { | |
this.identifier = '$r' + this.id | |
// a hash to cache the same expressions on repeated instances | |
// so they don't have to be compiled for every single instance | |
this.expCache = utils.hash() | |
var el = this.el, | |
ctn = this.container = el.parentNode | |
// extract child Id, if any | |
this.childId = this.compiler.eval(utils.attr(el, 'ref')) | |
// create a comment node as a reference node for DOM insertions | |
this.ref = document.createComment(config.prefix + '-repeat-' + this.key) | |
ctn.insertBefore(this.ref, el) | |
ctn.removeChild(el) | |
this.collection = null | |
this.vms = null | |
}, | |
update: function (collection) { | |
if (!Array.isArray(collection)) { | |
if (utils.isObject(collection)) { | |
collection = utils.objectToArray(collection) | |
} else { | |
utils.warn('v-repeat only accepts Array or Object values.') | |
} | |
} | |
// keep reference of old data and VMs | |
// so we can reuse them if possible | |
this.oldVMs = this.vms | |
this.oldCollection = this.collection | |
collection = this.collection = collection || [] | |
var isObject = collection[0] && utils.isObject(collection[0]) | |
this.vms = this.oldCollection | |
? this.diff(collection, isObject) | |
: this.init(collection, isObject) | |
if (this.childId) { | |
this.vm.$[this.childId] = this.vms | |
} | |
}, | |
init: function (collection, isObject) { | |
var vm, vms = [] | |
for (var i = 0, l = collection.length; i < l; i++) { | |
vm = this.build(collection[i], i, isObject) | |
vms.push(vm) | |
if (this.compiler.init) { | |
this.container.insertBefore(vm.$el, this.ref) | |
} else { | |
vm.$before(this.ref) | |
} | |
} | |
return vms | |
}, | |
/** | |
* Diff the new array with the old | |
* and determine the minimum amount of DOM manipulations. | |
*/ | |
diff: function (newCollection, isObject) { | |
var i, l, item, vm, | |
oldIndex, | |
targetNext, | |
currentNext, | |
nextEl, | |
ctn = this.container, | |
oldVMs = this.oldVMs, | |
vms = [] | |
vms.length = newCollection.length | |
// first pass, collect new reused and new created | |
for (i = 0, l = newCollection.length; i < l; i++) { | |
item = newCollection[i] | |
if (isObject) { | |
item.$index = i | |
if (item.__emitter__ && item.__emitter__[this.identifier]) { | |
// this piece of data is being reused. | |
// record its final position in reused vms | |
item.$reused = true | |
} else { | |
vms[i] = this.build(item, i, isObject) | |
} | |
} else { | |
// we can't attach an identifier to primitive values | |
// so have to do an indexOf... | |
oldIndex = indexOf(oldVMs, item) | |
if (oldIndex > -1) { | |
// record the position on the existing vm | |
oldVMs[oldIndex].$reused = true | |
oldVMs[oldIndex].$data.$index = i | |
} else { | |
vms[i] = this.build(item, i, isObject) | |
} | |
} | |
} | |
// second pass, collect old reused and destroy unused | |
for (i = 0, l = oldVMs.length; i < l; i++) { | |
vm = oldVMs[i] | |
item = this.arg | |
? vm.$data[this.arg] | |
: vm.$data | |
if (item.$reused) { | |
vm.$reused = true | |
delete item.$reused | |
} | |
if (vm.$reused) { | |
// update the index to latest | |
vm.$index = item.$index | |
// the item could have had a new key | |
if (item.$key && item.$key !== vm.$key) { | |
vm.$key = item.$key | |
} | |
vms[vm.$index] = vm | |
} else { | |
// this one can be destroyed. | |
if (item.__emitter__) { | |
delete item.__emitter__[this.identifier] | |
} | |
vm.$destroy() | |
} | |
} | |
// final pass, move/insert DOM elements | |
i = vms.length | |
while (i--) { | |
vm = vms[i] | |
item = vm.$data | |
targetNext = vms[i + 1] | |
if (vm.$reused) { | |
nextEl = vm.$el.nextSibling | |
// destroyed VMs' element might still be in the DOM | |
// due to transitions | |
while (!nextEl.vue_vm && nextEl !== this.ref) { | |
nextEl = nextEl.nextSibling | |
} | |
currentNext = nextEl.vue_vm | |
if (currentNext !== targetNext) { | |
if (!targetNext) { | |
ctn.insertBefore(vm.$el, this.ref) | |
} else { | |
nextEl = targetNext.$el | |
// new VMs' element might not be in the DOM yet | |
// due to transitions | |
while (!nextEl.parentNode) { | |
targetNext = vms[nextEl.vue_vm.$index + 1] | |
nextEl = targetNext | |
? targetNext.$el | |
: this.ref | |
} | |
ctn.insertBefore(vm.$el, nextEl) | |
} | |
} | |
delete vm.$reused | |
delete item.$index | |
delete item.$key | |
} else { // a new vm | |
vm.$before(targetNext ? targetNext.$el : this.ref) | |
} | |
} | |
return vms | |
}, | |
build: function (data, index, isObject) { | |
// wrap non-object values | |
var raw, alias, | |
wrap = !isObject || this.arg | |
if (wrap) { | |
raw = data | |
alias = this.arg || '$value' | |
data = {} | |
data[alias] = raw | |
} | |
data.$index = index | |
var el = this.el.cloneNode(true), | |
Ctor = this.compiler.resolveComponent(el, data), | |
vm = new Ctor({ | |
el: el, | |
data: data, | |
parent: this.vm, | |
compilerOptions: { | |
repeat: true, | |
expCache: this.expCache | |
} | |
}) | |
if (isObject) { | |
// attach an ienumerable identifier to the raw data | |
(raw || data).__emitter__[this.identifier] = true | |
} | |
return vm | |
}, | |
unbind: function () { | |
if (this.childId) { | |
delete this.vm.$[this.childId] | |
} | |
if (this.vms) { | |
var i = this.vms.length | |
while (i--) { | |
this.vms[i].$destroy() | |
} | |
} | |
} | |
} | |
// Helpers -------------------------------------------------------------------- | |
/** | |
* Find an object or a wrapped data object | |
* from an Array | |
*/ | |
function indexOf (vms, obj) { | |
for (var vm, i = 0, l = vms.length; i < l; i++) { | |
vm = vms[i] | |
if (!vm.$reused && vm.$value === obj) { | |
return i | |
} | |
} | |
return -1 | |
} | |
}); | |
require.register("vue/src/directives/on.js", function(exports, require, module){ | |
var utils = require('../utils') | |
/** | |
* Binding for event listeners | |
*/ | |
module.exports = { | |
isFn: true, | |
bind: function () { | |
this.context = this.binding.isExp | |
? this.vm | |
: this.binding.compiler.vm | |
}, | |
update: function (handler) { | |
if (typeof handler !== 'function') { | |
utils.warn('Directive "v-on:' + this.expression + '" expects a method.') | |
return | |
} | |
this.unbind() | |
var vm = this.vm, | |
context = this.context | |
this.handler = function (e) { | |
e.targetVM = vm | |
context.$event = e | |
var res = handler.call(context, e) | |
context.$event = null | |
return res | |
} | |
this.el.addEventListener(this.arg, this.handler) | |
}, | |
unbind: function () { | |
this.el.removeEventListener(this.arg, this.handler) | |
} | |
} | |
}); | |
require.register("vue/src/directives/model.js", function(exports, require, module){ | |
var utils = require('../utils'), | |
isIE9 = navigator.userAgent.indexOf('MSIE 9.0') > 0, | |
filter = [].filter | |
/** | |
* Returns an array of values from a multiple select | |
*/ | |
function getMultipleSelectOptions (select) { | |
return filter | |
.call(select.options, function (option) { | |
return option.selected | |
}) | |
.map(function (option) { | |
return option.value || option.text | |
}) | |
} | |
/** | |
* Two-way binding for form input elements | |
*/ | |
module.exports = { | |
bind: function () { | |
var self = this, | |
el = self.el, | |
type = el.type, | |
tag = el.tagName | |
self.lock = false | |
self.ownerVM = self.binding.compiler.vm | |
// determine what event to listen to | |
self.event = | |
(self.compiler.options.lazy || | |
tag === 'SELECT' || | |
type === 'checkbox' || type === 'radio' || type === 'file') | |
? 'change' | |
: 'input' | |
// determine the attribute to change when updating | |
self.attr = type === 'checkbox' | |
? 'checked' | |
: (tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA') | |
? 'value' | |
: 'innerHTML' | |
// select[multiple] support | |
if(tag === 'SELECT' && el.hasAttribute('multiple')) { | |
this.multi = true | |
} | |
var compositionLock = false | |
self.cLock = function () { | |
compositionLock = true | |
} | |
self.cUnlock = function () { | |
compositionLock = false | |
} | |
el.addEventListener('compositionstart', this.cLock) | |
el.addEventListener('compositionend', this.cUnlock) | |
// attach listener | |
self.set = self.filters | |
? function () { | |
if (compositionLock) return | |
// if this directive has filters | |
// we need to let the vm.$set trigger | |
// update() so filters are applied. | |
// therefore we have to record cursor position | |
// so that after vm.$set changes the input | |
// value we can put the cursor back at where it is | |
var cursorPos | |
try { cursorPos = el.selectionStart } catch (e) {} | |
self._set() | |
// since updates are async | |
// we need to reset cursor position async too | |
utils.nextTick(function () { | |
if (cursorPos !== undefined) { | |
el.setSelectionRange(cursorPos, cursorPos) | |
} | |
}) | |
} | |
: function () { | |
if (compositionLock) return | |
// no filters, don't let it trigger update() | |
self.lock = true | |
self._set() | |
utils.nextTick(function () { | |
self.lock = false | |
}) | |
} | |
el.addEventListener(self.event, self.set) | |
// fix shit for IE9 | |
// since it doesn't fire input on backspace / del / cut | |
if (isIE9) { | |
self.onCut = function () { | |
// cut event fires before the value actually changes | |
utils.nextTick(function () { | |
self.set() | |
}) | |
} | |
self.onDel = function (e) { | |
if (e.keyCode === 46 || e.keyCode === 8) { | |
self.set() | |
} | |
} | |
el.addEventListener('cut', self.onCut) | |
el.addEventListener('keyup', self.onDel) | |
} | |
}, | |
_set: function () { | |
this.ownerVM.$set( | |
this.key, this.multi | |
? getMultipleSelectOptions(this.el) | |
: this.el[this.attr] | |
) | |
}, | |
update: function (value, init) { | |
/* jshint eqeqeq: false */ | |
// sync back inline value if initial data is undefined | |
if (init && value === undefined) { | |
return this._set() | |
} | |
if (this.lock) return | |
var el = this.el | |
if (el.tagName === 'SELECT') { // select dropdown | |
el.selectedIndex = -1 | |
if(this.multi && Array.isArray(value)) { | |
value.forEach(this.updateSelect, this) | |
} else { | |
this.updateSelect(value) | |
} | |
} else if (el.type === 'radio') { // radio button | |
el.checked = value == el.value | |
} else if (el.type === 'checkbox') { // checkbox | |
el.checked = !!value | |
} else { | |
el[this.attr] = utils.guard(value) | |
} | |
}, | |
updateSelect: function (value) { | |
/* jshint eqeqeq: false */ | |
// setting <select>'s value in IE9 doesn't work | |
// we have to manually loop through the options | |
var options = this.el.options, | |
i = options.length | |
while (i--) { | |
if (options[i].value == value) { | |
options[i].selected = true | |
break | |
} | |
} | |
}, | |
unbind: function () { | |
var el = this.el | |
el.removeEventListener(this.event, this.set) | |
el.removeEventListener('compositionstart', this.cLock) | |
el.removeEventListener('compositionend', this.cUnlock) | |
if (isIE9) { | |
el.removeEventListener('cut', this.onCut) | |
el.removeEventListener('keyup', this.onDel) | |
} | |
} | |
} | |
}); | |
require.register("vue/src/directives/with.js", function(exports, require, module){ | |
var utils = require('../utils') | |
/** | |
* Binding for inheriting data from parent VMs. | |
*/ | |
module.exports = { | |
bind: function () { | |
var self = this, | |
childKey = self.arg, | |
parentKey = self.key, | |
compiler = self.compiler, | |
owner = self.binding.compiler | |
if (compiler === owner) { | |
this.alone = true | |
return | |
} | |
if (childKey) { | |
if (!compiler.bindings[childKey]) { | |
compiler.createBinding(childKey) | |
} | |
// sync changes on child back to parent | |
compiler.observer.on('change:' + childKey, function (val) { | |
if (compiler.init) return | |
if (!self.lock) { | |
self.lock = true | |
utils.nextTick(function () { | |
self.lock = false | |
}) | |
} | |
owner.vm.$set(parentKey, val) | |
}) | |
} | |
}, | |
update: function (value) { | |
// sync from parent | |
if (!this.alone && !this.lock) { | |
if (this.arg) { | |
this.vm.$set(this.arg, value) | |
} else { | |
this.vm.$data = value | |
} | |
} | |
} | |
} | |
}); | |
require.register("vue/src/directives/html.js", function(exports, require, module){ | |
var utils = require('../utils'), | |
slice = [].slice | |
/** | |
* Binding for innerHTML | |
*/ | |
module.exports = { | |
bind: function () { | |
// a comment node means this is a binding for | |
// {{{ inline unescaped html }}} | |
if (this.el.nodeType === 8) { | |
// hold nodes | |
this.nodes = [] | |
} | |
}, | |
update: function (value) { | |
value = utils.guard(value) | |
if (this.nodes) { | |
this.swap(value) | |
} else { | |
this.el.innerHTML = value | |
} | |
}, | |
swap: function (value) { | |
var parent = this.el.parentNode, | |
nodes = this.nodes, | |
i = nodes.length | |
// remove old nodes | |
while (i--) { | |
parent.removeChild(nodes[i]) | |
} | |
// convert new value to a fragment | |
var frag = utils.toFragment(value) | |
// save a reference to these nodes so we can remove later | |
this.nodes = slice.call(frag.childNodes) | |
parent.insertBefore(frag, this.el) | |
} | |
} | |
}); | |
require.register("vue/src/directives/style.js", function(exports, require, module){ | |
var camelRE = /-([a-z])/g, | |
prefixes = ['webkit', 'moz', 'ms'] | |
function camelReplacer (m) { | |
return m[1].toUpperCase() | |
} | |
/** | |
* Binding for CSS styles | |
*/ | |
module.exports = { | |
bind: function () { | |
var prop = this.arg | |
if (!prop) return | |
var first = prop.charAt(0) | |
if (first === '$') { | |
// properties that start with $ will be auto-prefixed | |
prop = prop.slice(1) | |
this.prefixed = true | |
} else if (first === '-') { | |
// normal starting hyphens should not be converted | |
prop = prop.slice(1) | |
} | |
this.prop = prop.replace(camelRE, camelReplacer) | |
}, | |
update: function (value) { | |
var prop = this.prop | |
if (prop) { | |
this.el.style[prop] = value | |
if (this.prefixed) { | |
prop = prop.charAt(0).toUpperCase() + prop.slice(1) | |
var i = prefixes.length | |
while (i--) { | |
this.el.style[prefixes[i] + prop] = value | |
} | |
} | |
} else { | |
this.el.style.cssText = value | |
} | |
} | |
} | |
}); | |
require.register("vue/src/directives/partial.js", function(exports, require, module){ | |
var utils = require('../utils') | |
/** | |
* Binding for partials | |
*/ | |
module.exports = { | |
isLiteral: true, | |
bind: function () { | |
var id = this.expression | |
if (!id) return | |
var el = this.el, | |
compiler = this.compiler, | |
partial = compiler.getOption('partials', id) | |
if (!partial) { | |
if (id === 'yield') { | |
utils.warn('{{>yield}} syntax has been deprecated. Use <content> tag instead.') | |
} | |
return | |
} | |
partial = partial.cloneNode(true) | |
// comment ref node means inline partial | |
if (el.nodeType === 8) { | |
// keep a ref for the partial's content nodes | |
var nodes = [].slice.call(partial.childNodes), | |
parent = el.parentNode | |
parent.insertBefore(partial, el) | |
parent.removeChild(el) | |
// compile partial after appending, because its children's parentNode | |
// will change from the fragment to the correct parentNode. | |
// This could affect directives that need access to its element's parentNode. | |
nodes.forEach(compiler.compile, compiler) | |
} else { | |
// just set innerHTML... | |
el.innerHTML = '' | |
el.appendChild(partial.cloneNode(true)) | |
} | |
} | |
} | |
}); | |
require.register("vue/src/directives/view.js", function(exports, require, module){ | |
/** | |
* Manages a conditional child VM using the | |
* binding's value as the component ID. | |
*/ | |
module.exports = { | |
bind: function () { | |
// track position in DOM with a ref node | |
var el = this.raw = this.el, | |
parent = el.parentNode, | |
ref = this.ref = document.createComment('v-view') | |
parent.insertBefore(ref, el) | |
parent.removeChild(el) | |
// cache original content | |
/* jshint boss: true */ | |
var node, | |
frag = this.inner = document.createElement('div') | |
while (node = el.firstChild) { | |
frag.appendChild(node) | |
} | |
}, | |
update: function(value) { | |
this.unbind() | |
var Ctor = this.compiler.getOption('components', value) | |
if (!Ctor) return | |
this.childVM = new Ctor({ | |
el: this.raw.cloneNode(true), | |
parent: this.vm, | |
compilerOptions: { | |
rawContent: this.inner.cloneNode(true) | |
} | |
}) | |
this.el = this.childVM.$el | |
if (this.compiler.init) { | |
this.ref.parentNode.insertBefore(this.el, this.ref) | |
} else { | |
this.childVM.$before(this.ref) | |
} | |
}, | |
unbind: function() { | |
if (this.childVM) { | |
this.childVM.$destroy() | |
} | |
} | |
} | |
}); | |
require.alias("vue/src/main.js", "vue/index.js"); | |
if (typeof exports == 'object') { | |
module.exports = require('vue'); | |
} else if (typeof define == 'function' && define.amd) { | |
define(function(){ return require('vue'); }); | |
} else { | |
window['Vue'] = require('vue'); | |
}})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment