Commit 3522bd1d authored by Evan You's avatar Evan You

Add Vue.js implementation

parent db95f7b6
......@@ -148,6 +148,9 @@
<li class="labs routing">
<a href="labs/architecture-examples/componentjs/" data-source="http://componentjs.com" data-content="ComponentJS is a stand-alone MPL-licensed Open Source library for JavaScript, providing a powerful run-time Component System for hierarchically structuring the User-Interface (UI) dialogs of complex HTML5-based Rich Clients (aka Single-Page-Apps) — under maximum applied Separation of Concerns (SoC) architecture principle, through optional Model, View and Controller component roles, with sophisticated hierarchical Event, Service, Hook, Model, Socket and Property mechanisms, and fully independent and agnostic of the particular UI widget toolkit.">ComponentJS</a>
</li>
<li class="labs routing">
<a href="labs/architecture-examples/vue/" data-source="http://vuejs.org" data-content="Vue.js provides the benefits of MVVM data binding and a composable component system with an extremely simple and flexible API.">Vue.js</a>
</li>
</ul>
<ul class="legend">
<li><b>*</b> <span class="label">R</span> = App also demonstrates routing</li>
......
{
"name": "todomvc-vue",
"dependencies": {
"vue": "~0.8.3",
"todomvc-common": "~0.1.9"
}
}
\ No newline at end of file
//
// Generated on Fri Dec 27 2013 12:02:11 GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon).
// Version 1.2.2
//
(function (exports) {
/*
* browser.js: Browser specific functionality for director.
*
* (C) 2011, Nodejitsu Inc.
* MIT LICENSE
*
*/
if (!Array.prototype.filter) {
Array.prototype.filter = function(filter, that) {
var other = [], v;
for (var i = 0, n = this.length; i < n; i++) {
if (i in this && filter.call(that, v = this[i], i, this)) {
other.push(v);
}
}
return other;
};
}
if (!Array.isArray){
Array.isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
}
var dloc = document.location;
function dlocHashEmpty() {
// Non-IE browsers return '' when the address bar shows '#'; Director's logic
// assumes both mean empty.
return dloc.hash === '' || dloc.hash === '#';
}
var listener = {
mode: 'modern',
hash: dloc.hash,
history: false,
check: function () {
var h = dloc.hash;
if (h != this.hash) {
this.hash = h;
this.onHashChanged();
}
},
fire: function () {
if (this.mode === 'modern') {
this.history === true ? window.onpopstate() : window.onhashchange();
}
else {
this.onHashChanged();
}
},
init: function (fn, history) {
var self = this;
this.history = history;
if (!Router.listeners) {
Router.listeners = [];
}
function onchange(onChangeEvent) {
for (var i = 0, l = Router.listeners.length; i < l; i++) {
Router.listeners[i](onChangeEvent);
}
}
//note IE8 is being counted as 'modern' because it has the hashchange event
if ('onhashchange' in window && (document.documentMode === undefined
|| document.documentMode > 7)) {
// At least for now HTML5 history is available for 'modern' browsers only
if (this.history === true) {
// There is an old bug in Chrome that causes onpopstate to fire even
// upon initial page load. Since the handler is run manually in init(),
// this would cause Chrome to run it twise. Currently the only
// workaround seems to be to set the handler after the initial page load
// http://code.google.com/p/chromium/issues/detail?id=63040
setTimeout(function() {
window.onpopstate = onchange;
}, 500);
}
else {
window.onhashchange = onchange;
}
this.mode = 'modern';
}
else {
//
// IE support, based on a concept by Erik Arvidson ...
//
var frame = document.createElement('iframe');
frame.id = 'state-frame';
frame.style.display = 'none';
document.body.appendChild(frame);
this.writeFrame('');
if ('onpropertychange' in document && 'attachEvent' in document) {
document.attachEvent('onpropertychange', function () {
if (event.propertyName === 'location') {
self.check();
}
});
}
window.setInterval(function () { self.check(); }, 50);
this.onHashChanged = onchange;
this.mode = 'legacy';
}
Router.listeners.push(fn);
return this.mode;
},
destroy: function (fn) {
if (!Router || !Router.listeners) {
return;
}
var listeners = Router.listeners;
for (var i = listeners.length - 1; i >= 0; i--) {
if (listeners[i] === fn) {
listeners.splice(i, 1);
}
}
},
setHash: function (s) {
// Mozilla always adds an entry to the history
if (this.mode === 'legacy') {
this.writeFrame(s);
}
if (this.history === true) {
window.history.pushState({}, document.title, s);
// Fire an onpopstate event manually since pushing does not obviously
// trigger the pop event.
this.fire();
} else {
dloc.hash = (s[0] === '/') ? s : '/' + s;
}
return this;
},
writeFrame: function (s) {
// IE support...
var f = document.getElementById('state-frame');
var d = f.contentDocument || f.contentWindow.document;
d.open();
d.write("<script>_hash = '" + s + "'; onload = parent.listener.syncHash;<script>");
d.close();
},
syncHash: function () {
// IE support...
var s = this._hash;
if (s != dloc.hash) {
dloc.hash = s;
}
return this;
},
onHashChanged: function () {}
};
var Router = exports.Router = function (routes) {
if (!(this instanceof Router)) return new Router(routes);
this.params = {};
this.routes = {};
this.methods = ['on', 'once', 'after', 'before'];
this.scope = [];
this._methods = {};
this._insert = this.insert;
this.insert = this.insertEx;
this.historySupport = (window.history != null ? window.history.pushState : null) != null
this.configure();
this.mount(routes || {});
};
Router.prototype.init = function (r) {
var self = this;
this.handler = function(onChangeEvent) {
var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
self.dispatch('on', url.charAt(0) === '/' ? url : '/' + url);
};
listener.init(this.handler, this.history);
if (this.history === false) {
if (dlocHashEmpty() && r) {
dloc.hash = r;
} else if (!dlocHashEmpty()) {
self.dispatch('on', '/' + dloc.hash.replace(/^(#\/|#|\/)/, ''));
}
}
else {
var routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null;
if (routeTo) {
window.history.replaceState({}, document.title, routeTo);
}
// Router has been initialized, but due to the chrome bug it will not
// yet actually route HTML5 history state changes. Thus, decide if should route.
if (routeTo || this.run_in_init === true) {
this.handler();
}
}
return this;
};
Router.prototype.explode = function () {
var v = this.history === true ? this.getPath() : dloc.hash;
if (v.charAt(1) === '/') { v=v.slice(1) }
return v.slice(1, v.length).split("/");
};
Router.prototype.setRoute = function (i, v, val) {
var url = this.explode();
if (typeof i === 'number' && typeof v === 'string') {
url[i] = v;
}
else if (typeof val === 'string') {
url.splice(i, v, s);
}
else {
url = [i];
}
listener.setHash(url.join('/'));
return url;
};
//
// ### function insertEx(method, path, route, parent)
// #### @method {string} Method to insert the specific `route`.
// #### @path {Array} Parsed path to insert the `route` at.
// #### @route {Array|function} Route handlers to insert.
// #### @parent {Object} **Optional** Parent "routes" to insert into.
// insert a callback that will only occur once per the matched route.
//
Router.prototype.insertEx = function(method, path, route, parent) {
if (method === "once") {
method = "on";
route = function(route) {
var once = false;
return function() {
if (once) return;
once = true;
return route.apply(this, arguments);
};
}(route);
}
return this._insert(method, path, route, parent);
};
Router.prototype.getRoute = function (v) {
var ret = v;
if (typeof v === "number") {
ret = this.explode()[v];
}
else if (typeof v === "string"){
var h = this.explode();
ret = h.indexOf(v);
}
else {
ret = this.explode();
}
return ret;
};
Router.prototype.destroy = function () {
listener.destroy(this.handler);
return this;
};
Router.prototype.getPath = function () {
var path = window.location.pathname;
if (path.substr(0, 1) !== '/') {
path = '/' + path;
}
return path;
};
function _every(arr, iterator) {
for (var i = 0; i < arr.length; i += 1) {
if (iterator(arr[i], i, arr) === false) {
return;
}
}
}
function _flatten(arr) {
var flat = [];
for (var i = 0, n = arr.length; i < n; i++) {
flat = flat.concat(arr[i]);
}
return flat;
}
function _asyncEverySeries(arr, iterator, callback) {
if (!arr.length) {
return callback();
}
var completed = 0;
(function iterate() {
iterator(arr[completed], function(err) {
if (err || err === false) {
callback(err);
callback = function() {};
} else {
completed += 1;
if (completed === arr.length) {
callback();
} else {
iterate();
}
}
});
})();
}
function paramifyString(str, params, mod) {
mod = str;
for (var param in params) {
if (params.hasOwnProperty(param)) {
mod = params[param](str);
if (mod !== str) {
break;
}
}
}
return mod === str ? "([._a-zA-Z0-9-]+)" : mod;
}
function regifyString(str, params) {
var matches, last = 0, out = "";
while (matches = str.substr(last).match(/[^\w\d\- %@&]*\*[^\w\d\- %@&]*/)) {
last = matches.index + matches[0].length;
matches[0] = matches[0].replace(/^\*/, "([_.()!\\ %@&a-zA-Z0-9-]+)");
out += str.substr(0, matches.index) + matches[0];
}
str = out += str.substr(last);
var captures = str.match(/:([^\/]+)/ig), capture, length;
if (captures) {
length = captures.length;
for (var i = 0; i < length; i++) {
capture = captures[i];
if (capture.slice(0, 2) === "::") {
str = capture.slice(1);
} else {
str = str.replace(capture, paramifyString(capture, params));
}
}
}
return str;
}
function terminator(routes, delimiter, start, stop) {
var last = 0, left = 0, right = 0, start = (start || "(").toString(), stop = (stop || ")").toString(), i;
for (i = 0; i < routes.length; i++) {
var chunk = routes[i];
if (chunk.indexOf(start, last) > chunk.indexOf(stop, last) || ~chunk.indexOf(start, last) && !~chunk.indexOf(stop, last) || !~chunk.indexOf(start, last) && ~chunk.indexOf(stop, last)) {
left = chunk.indexOf(start, last);
right = chunk.indexOf(stop, last);
if (~left && !~right || !~left && ~right) {
var tmp = routes.slice(0, (i || 1) + 1).join(delimiter);
routes = [ tmp ].concat(routes.slice((i || 1) + 1));
}
last = (right > left ? right : left) + 1;
i = 0;
} else {
last = 0;
}
}
return routes;
}
Router.prototype.configure = function(options) {
options = options || {};
for (var i = 0; i < this.methods.length; i++) {
this._methods[this.methods[i]] = true;
}
this.recurse = options.recurse || this.recurse || false;
this.async = options.async || false;
this.delimiter = options.delimiter || "/";
this.strict = typeof options.strict === "undefined" ? true : options.strict;
this.notfound = options.notfound;
this.resource = options.resource;
this.history = options.html5history && this.historySupport || false;
this.run_in_init = this.history === true && options.run_handler_in_init !== false;
this.every = {
after: options.after || null,
before: options.before || null,
on: options.on || null
};
return this;
};
Router.prototype.param = function(token, matcher) {
if (token[0] !== ":") {
token = ":" + token;
}
var compiled = new RegExp(token, "g");
this.params[token] = function(str) {
return str.replace(compiled, matcher.source || matcher);
};
};
Router.prototype.on = Router.prototype.route = function(method, path, route) {
var self = this;
if (!route && typeof path == "function") {
route = path;
path = method;
method = "on";
}
if (Array.isArray(path)) {
return path.forEach(function(p) {
self.on(method, p, route);
});
}
if (path.source) {
path = path.source.replace(/\\\//ig, "/");
}
if (Array.isArray(method)) {
return method.forEach(function(m) {
self.on(m.toLowerCase(), path, route);
});
}
path = path.split(new RegExp(this.delimiter));
path = terminator(path, this.delimiter);
this.insert(method, this.scope.concat(path), route);
};
Router.prototype.dispatch = function(method, path, callback) {
var self = this, fns = this.traverse(method, path, this.routes, ""), invoked = this._invoked, after;
this._invoked = true;
if (!fns || fns.length === 0) {
this.last = [];
if (typeof this.notfound === "function") {
this.invoke([ this.notfound ], {
method: method,
path: path
}, callback);
}
return false;
}
if (this.recurse === "forward") {
fns = fns.reverse();
}
function updateAndInvoke() {
self.last = fns.after;
self.invoke(self.runlist(fns), self, callback);
}
after = this.every && this.every.after ? [ this.every.after ].concat(this.last) : [ this.last ];
if (after && after.length > 0 && invoked) {
if (this.async) {
this.invoke(after, this, updateAndInvoke);
} else {
this.invoke(after, this);
updateAndInvoke();
}
return true;
}
updateAndInvoke();
return true;
};
Router.prototype.invoke = function(fns, thisArg, callback) {
var self = this;
var apply;
if (this.async) {
apply = function(fn, next) {
if (Array.isArray(fn)) {
return _asyncEverySeries(fn, apply, next);
} else if (typeof fn == "function") {
fn.apply(thisArg, fns.captures.concat(next));
}
};
_asyncEverySeries(fns, apply, function() {
if (callback) {
callback.apply(thisArg, arguments);
}
});
} else {
apply = function(fn) {
if (Array.isArray(fn)) {
return _every(fn, apply);
} else if (typeof fn === "function") {
return fn.apply(thisArg, fns.captures || []);
} else if (typeof fn === "string" && self.resource) {
self.resource[fn].apply(thisArg, fns.captures || []);
}
};
_every(fns, apply);
}
};
Router.prototype.traverse = function(method, path, routes, regexp, filter) {
var fns = [], current, exact, match, next, that;
function filterRoutes(routes) {
if (!filter) {
return routes;
}
function deepCopy(source) {
var result = [];
for (var i = 0; i < source.length; i++) {
result[i] = Array.isArray(source[i]) ? deepCopy(source[i]) : source[i];
}
return result;
}
function applyFilter(fns) {
for (var i = fns.length - 1; i >= 0; i--) {
if (Array.isArray(fns[i])) {
applyFilter(fns[i]);
if (fns[i].length === 0) {
fns.splice(i, 1);
}
} else {
if (!filter(fns[i])) {
fns.splice(i, 1);
}
}
}
}
var newRoutes = deepCopy(routes);
newRoutes.matched = routes.matched;
newRoutes.captures = routes.captures;
newRoutes.after = routes.after.filter(filter);
applyFilter(newRoutes);
return newRoutes;
}
if (path === this.delimiter && routes[method]) {
next = [ [ routes.before, routes[method] ].filter(Boolean) ];
next.after = [ routes.after ].filter(Boolean);
next.matched = true;
next.captures = [];
return filterRoutes(next);
}
for (var r in routes) {
if (routes.hasOwnProperty(r) && (!this._methods[r] || this._methods[r] && typeof routes[r] === "object" && !Array.isArray(routes[r]))) {
current = exact = regexp + this.delimiter + r;
if (!this.strict) {
exact += "[" + this.delimiter + "]?";
}
match = path.match(new RegExp("^" + exact));
if (!match) {
continue;
}
if (match[0] && match[0] == path && routes[r][method]) {
next = [ [ routes[r].before, routes[r][method] ].filter(Boolean) ];
next.after = [ routes[r].after ].filter(Boolean);
next.matched = true;
next.captures = match.slice(1);
if (this.recurse && routes === this.routes) {
next.push([ routes.before, routes.on ].filter(Boolean));
next.after = next.after.concat([ routes.after ].filter(Boolean));
}
return filterRoutes(next);
}
next = this.traverse(method, path, routes[r], current);
if (next.matched) {
if (next.length > 0) {
fns = fns.concat(next);
}
if (this.recurse) {
fns.push([ routes[r].before, routes[r].on ].filter(Boolean));
next.after = next.after.concat([ routes[r].after ].filter(Boolean));
if (routes === this.routes) {
fns.push([ routes["before"], routes["on"] ].filter(Boolean));
next.after = next.after.concat([ routes["after"] ].filter(Boolean));
}
}
fns.matched = true;
fns.captures = next.captures;
fns.after = next.after;
return filterRoutes(fns);
}
}
}
return false;
};
Router.prototype.insert = function(method, path, route, parent) {
var methodType, parentType, isArray, nested, part;
path = path.filter(function(p) {
return p && p.length > 0;
});
parent = parent || this.routes;
part = path.shift();
if (/\:|\*/.test(part) && !/\\d|\\w/.test(part)) {
part = regifyString(part, this.params);
}
if (path.length > 0) {
parent[part] = parent[part] || {};
return this.insert(method, path, route, parent[part]);
}
if (!part && !path.length && parent === this.routes) {
methodType = typeof parent[method];
switch (methodType) {
case "function":
parent[method] = [ parent[method], route ];
return;
case "object":
parent[method].push(route);
return;
case "undefined":
parent[method] = route;
return;
}
return;
}
parentType = typeof parent[part];
isArray = Array.isArray(parent[part]);
if (parent[part] && !isArray && parentType == "object") {
methodType = typeof parent[part][method];
switch (methodType) {
case "function":
parent[part][method] = [ parent[part][method], route ];
return;
case "object":
parent[part][method].push(route);
return;
case "undefined":
parent[part][method] = route;
return;
}
} else if (parentType == "undefined") {
nested = {};
nested[method] = route;
parent[part] = nested;
return;
}
throw new Error("Invalid route context: " + parentType);
};
Router.prototype.extend = function(methods) {
var self = this, len = methods.length, i;
function extend(method) {
self._methods[method] = true;
self[method] = function() {
var extra = arguments.length === 1 ? [ method, "" ] : [ method ];
self.on.apply(self, extra.concat(Array.prototype.slice.call(arguments)));
};
}
for (i = 0; i < len; i++) {
extend(methods[i]);
}
};
Router.prototype.runlist = function(fns) {
var runlist = this.every && this.every.before ? [ this.every.before ].concat(_flatten(fns)) : _flatten(fns);
if (this.every && this.every.on) {
runlist.push(this.every.on);
}
runlist.captures = fns.captures;
runlist.source = fns.source;
return runlist;
};
Router.prototype.mount = function(routes, path) {
if (!routes || typeof routes !== "object" || Array.isArray(routes)) {
return;
}
var self = this;
path = path || [];
if (!Array.isArray(path)) {
path = path.split(self.delimiter);
}
function insertOrMount(route, local) {
var rename = route, parts = route.split(self.delimiter), routeType = typeof routes[route], isRoute = parts[0] === "" || !self._methods[parts[0]], event = isRoute ? "on" : rename;
if (isRoute) {
rename = rename.slice((rename.match(new RegExp("^" + self.delimiter)) || [ "" ])[0].length);
parts.shift();
}
if (isRoute && routeType === "object" && !Array.isArray(routes[route])) {
local = local.concat(parts);
self.mount(routes[route], local);
return;
}
if (isRoute) {
local = local.concat(rename.split(self.delimiter));
local = terminator(local, self.delimiter);
}
self.insert(event, local, routes[route]);
}
for (var route in routes) {
if (routes.hasOwnProperty(route)) {
insertOrMount(route, path.slice(0));
}
}
};
}(typeof exports === "object" ? exports : window));
\ No newline at end of file
html,
body {
margin: 0;
padding: 0;
}
button {
margin: 0;
padding: 0;
border: 0;
background: none;
font-size: 100%;
vertical-align: baseline;
font-family: inherit;
color: inherit;
-webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
body {
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif;
line-height: 1.4em;
background: #eaeaea url('bg.png');
color: #4d4d4d;
width: 550px;
margin: 0 auto;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
button,
input[type="checkbox"] {
outline: none;
}
#todoapp {
background: #fff;
background: rgba(255, 255, 255, 0.9);
margin: 130px 0 40px 0;
border: 1px solid #ccc;
position: relative;
border-top-left-radius: 2px;
border-top-right-radius: 2px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.2),
0 25px 50px 0 rgba(0, 0, 0, 0.15);
}
#todoapp:before {
content: '';
border-left: 1px solid #f5d6d6;
border-right: 1px solid #f5d6d6;
width: 2px;
position: absolute;
top: 0;
left: 40px;
height: 100%;
}
#todoapp input::-webkit-input-placeholder {
font-style: italic;
}
#todoapp input::-moz-placeholder {
font-style: italic;
color: #a9a9a9;
}
#todoapp h1 {
position: absolute;
top: -120px;
width: 100%;
font-size: 70px;
font-weight: bold;
text-align: center;
color: #b3b3b3;
color: rgba(255, 255, 255, 0.3);
text-shadow: -1px -1px rgba(0, 0, 0, 0.2);
-webkit-text-rendering: optimizeLegibility;
-moz-text-rendering: optimizeLegibility;
-ms-text-rendering: optimizeLegibility;
-o-text-rendering: optimizeLegibility;
text-rendering: optimizeLegibility;
}
#header {
padding-top: 15px;
border-radius: inherit;
}
#header:before {
content: '';
position: absolute;
top: 0;
right: 0;
left: 0;
height: 15px;
z-index: 2;
border-bottom: 1px solid #6c615c;
background: #8d7d77;
background: -webkit-gradient(linear, left top, left bottom, from(rgba(132, 110, 100, 0.8)),to(rgba(101, 84, 76, 0.8)));
background: -webkit-linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
background: linear-gradient(top, rgba(132, 110, 100, 0.8), rgba(101, 84, 76, 0.8));
filter: progid:DXImageTransform.Microsoft.gradient(GradientType=0,StartColorStr='#9d8b83', EndColorStr='#847670');
border-top-left-radius: 1px;
border-top-right-radius: 1px;
}
#new-todo,
.edit {
position: relative;
margin: 0;
width: 100%;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
color: inherit;
padding: 6px;
border: 1px solid #999;
box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2);
-moz-box-sizing: border-box;
-ms-box-sizing: border-box;
-o-box-sizing: border-box;
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
-moz-font-smoothing: antialiased;
-ms-font-smoothing: antialiased;
-o-font-smoothing: antialiased;
font-smoothing: antialiased;
}
#new-todo {
padding: 16px 16px 16px 60px;
border: none;
background: rgba(0, 0, 0, 0.02);
z-index: 2;
box-shadow: none;
}
#main {
position: relative;
z-index: 2;
border-top: 1px dotted #adadad;
}
label[for='toggle-all'] {
display: none;
}
#toggle-all {
position: absolute;
top: -42px;
left: -4px;
width: 40px;
text-align: center;
/* Mobile Safari */
border: none;
}
#toggle-all:before {
content: '»';
font-size: 28px;
color: #d9d9d9;
padding: 0 25px 7px;
}
#toggle-all:checked:before {
color: #737373;
}
#todo-list {
margin: 0;
padding: 0;
list-style: none;
}
#todo-list li {
position: relative;
font-size: 24px;
border-bottom: 1px dotted #ccc;
}
#todo-list li:last-child {
border-bottom: none;
}
#todo-list li.editing {
border-bottom: none;
padding: 0;
}
#todo-list li.editing .edit {
display: block;
width: 506px;
padding: 13px 17px 12px 17px;
margin: 0 0 0 43px;
}
#todo-list li.editing .view {
display: none;
}
#todo-list li .toggle {
text-align: center;
width: 40px;
/* auto, since non-WebKit browsers doesn't support input styling */
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: auto 0;
/* Mobile Safari */
border: none;
-webkit-appearance: none;
-ms-appearance: none;
-o-appearance: none;
appearance: none;
}
#todo-list li .toggle:after {
content: '✔';
/* 40 + a couple of pixels visual adjustment */
line-height: 43px;
font-size: 20px;
color: #d9d9d9;
text-shadow: 0 -1px 0 #bfbfbf;
}
#todo-list li .toggle:checked:after {
color: #85ada7;
text-shadow: 0 1px 0 #669991;
bottom: 1px;
position: relative;
}
#todo-list li label {
white-space: pre;
word-break: break-word;
padding: 15px 60px 15px 15px;
margin-left: 45px;
display: block;
line-height: 1.2;
-webkit-transition: color 0.4s;
transition: color 0.4s;
}
#todo-list li.completed label {
color: #a9a9a9;
text-decoration: line-through;
}
#todo-list li .destroy {
display: none;
position: absolute;
top: 0;
right: 10px;
bottom: 0;
width: 40px;
height: 40px;
margin: auto 0;
font-size: 22px;
color: #a88a8a;
-webkit-transition: all 0.2s;
transition: all 0.2s;
}
#todo-list li .destroy:hover {
text-shadow: 0 0 1px #000,
0 0 10px rgba(199, 107, 107, 0.8);
-webkit-transform: scale(1.3);
-ms-transform: scale(1.3);
transform: scale(1.3);
}
#todo-list li .destroy:after {
content: '✖';
}
#todo-list li:hover .destroy {
display: block;
}
#todo-list li .edit {
display: none;
}
#todo-list li.editing:last-child {
margin-bottom: -1px;
}
#footer {
color: #777;
padding: 0 15px;
position: absolute;
right: 0;
bottom: -31px;
left: 0;
height: 20px;
z-index: 1;
text-align: center;
}
#footer:before {
content: '';
position: absolute;
right: 0;
bottom: 31px;
left: 0;
height: 50px;
z-index: -1;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.3),
0 6px 0 -3px rgba(255, 255, 255, 0.8),
0 7px 1px -3px rgba(0, 0, 0, 0.3),
0 43px 0 -6px rgba(255, 255, 255, 0.8),
0 44px 2px -6px rgba(0, 0, 0, 0.2);
}
#todo-count {
float: left;
text-align: left;
}
#filters {
margin: 0;
padding: 0;
list-style: none;
position: absolute;
right: 0;
left: 0;
}
#filters li {
display: inline;
}
#filters li a {
color: #83756f;
margin: 2px;
text-decoration: none;
}
#filters li a.selected {
font-weight: bold;
}
#clear-completed {
float: right;
position: relative;
line-height: 20px;
text-decoration: none;
background: rgba(0, 0, 0, 0.1);
font-size: 11px;
padding: 0 10px;
border-radius: 3px;
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.2);
}
#clear-completed:hover {
background: rgba(0, 0, 0, 0.15);
box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.3);
}
#info {
margin: 65px auto 0;
color: #a6a6a6;
font-size: 12px;
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.7);
text-align: center;
}
#info a {
color: inherit;
}
/*
Hack to remove background from Mobile Safari.
Can't use it globally since it destroys checkboxes in Firefox and Opera
*/
@media screen and (-webkit-min-device-pixel-ratio:0) {
#toggle-all,
#todo-list li .toggle {
background: none;
}
#todo-list li .toggle {
height: 40px;
}
#toggle-all {
top: -56px;
left: -15px;
width: 65px;
height: 41px;
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-appearance: none;
appearance: none;
}
}
.hidden {
display: none;
}
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #C5C5C5;
border-bottom: 1px dashed #F7F7F7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: .15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: .15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, .04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, .04);
}
.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, .6);
-webkit-transition-property: left;
transition-property: left;
-webkit-transition-duration: 500ms;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
margin: 0 0 0 300px;
}
.learn-bar > .learn {
left: 8px;
}
.learn-bar #todoapp {
width: 550px;
margin: 130px auto 40px auto;
}
}
(function () {
'use strict';
// Underscore's Template Module
// Courtesy of underscorejs.org
var _ = (function (_) {
_.defaults = function (object) {
if (!object) {
return object;
}
for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {
var iterable = arguments[argsIndex];
if (iterable) {
for (var key in iterable) {
if (object[key] == null) {
object[key] = iterable[key];
}
}
}
}
return object;
}
// By default, Underscore uses ERB-style template delimiters, change the
// following template settings to use alternative delimiters.
_.templateSettings = {
evaluate : /<%([\s\S]+?)%>/g,
interpolate : /<%=([\s\S]+?)%>/g,
escape : /<%-([\s\S]+?)%>/g
};
// When customizing `templateSettings`, if you don't want to define an
// interpolation, evaluation or escaping regex, we need one that is
// guaranteed not to match.
var noMatch = /(.)^/;
// Certain characters need to be escaped so that they can be put into a
// string literal.
var escapes = {
"'": "'",
'\\': '\\',
'\r': 'r',
'\n': 'n',
'\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
// JavaScript micro-templating, similar to John Resig's implementation.
// Underscore templating handles arbitrary delimiters, preserves whitespace,
// and correctly escapes quotes within interpolated code.
_.template = function(text, data, settings) {
var render;
settings = _.defaults({}, settings, _.templateSettings);
// Combine delimiters into one regular expression via alternation.
var matcher = new RegExp([
(settings.escape || noMatch).source,
(settings.interpolate || noMatch).source,
(settings.evaluate || noMatch).source
].join('|') + '|$', 'g');
// Compile the template source, escaping string literals appropriately.
var index = 0;
var source = "__p+='";
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
source += text.slice(index, offset)
.replace(escaper, function(match) { return '\\' + escapes[match]; });
if (escape) {
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
}
if (interpolate) {
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
}
if (evaluate) {
source += "';\n" + evaluate + "\n__p+='";
}
index = offset + match.length;
return match;
});
source += "';\n";
// If a variable is not specified, place data values in local scope.
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
source = "var __t,__p='',__j=Array.prototype.join," +
"print=function(){__p+=__j.call(arguments,'');};\n" +
source + "return __p;\n";
try {
render = new Function(settings.variable || 'obj', '_', source);
} catch (e) {
e.source = source;
throw e;
}
if (data) return render(data, _);
var template = function(data) {
return render.call(this, data, _);
};
// Provide the compiled function source as a convenience for precompilation.
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
return template;
};
return _;
})({});
if (location.hostname === 'todomvc.com') {
window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script'));
}
function redirect() {
if (location.hostname === 'tastejs.github.io') {
location.href = location.href.replace('tastejs.github.io/todomvc', 'todomvc.com');
}
}
function findRoot() {
var base;
[/labs/, /\w*-examples/].forEach(function (href) {
var match = location.href.match(href);
if (!base && match) {
base = location.href.indexOf(match);
}
});
return location.href.substr(0, base);
}
function getFile(file, callback) {
if (!location.host) {
return console.info('Miss the info bar? Run TodoMVC from a server to avoid a cross-origin error.');
}
var xhr = new XMLHttpRequest();
xhr.open('GET', findRoot() + file, true);
xhr.send();
xhr.onload = function () {
if (xhr.status === 200 && callback) {
callback(xhr.responseText);
}
};
}
function Learn(learnJSON, config) {
if (!(this instanceof Learn)) {
return new Learn(learnJSON, config);
}
var template, framework;
if (typeof learnJSON !== 'object') {
try {
learnJSON = JSON.parse(learnJSON);
} catch (e) {
return;
}
}
if (config) {
template = config.template;
framework = config.framework;
}
if (!template && learnJSON.templates) {
template = learnJSON.templates.todomvc;
}
if (!framework && document.querySelector('[data-framework]')) {
framework = document.querySelector('[data-framework]').getAttribute('data-framework');
}
if (template && learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework];
this.template = template;
this.append();
}
}
Learn.prototype.append = function () {
var aside = document.createElement('aside');
aside.innerHTML = _.template(this.template, this.frameworkJSON);
aside.className = 'learn';
// Localize demo links
var demoLinks = aside.querySelectorAll('.demo-link');
Array.prototype.forEach.call(demoLinks, function (demoLink) {
demoLink.setAttribute('href', findRoot() + demoLink.getAttribute('href'));
});
document.body.className = (document.body.className + ' learn-bar').trim();
document.body.insertAdjacentHTML('afterBegin', aside.outerHTML);
};
redirect();
getFile('learn.json', Learn);
})();
/*
Vue.js v0.8.3
(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) {
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;
}
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;
}
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.resolve = function(path) {
if (path.charAt(0) === '/') path = path.slice(1);
var paths = [
path,
path + '.js',
path + '.json',
path + '/index.js',
path + '/index.json'
];
for (var i = 0; i < paths.length; i++) {
var path = paths[i];
if (require.modules.hasOwnProperty(path)) return path;
if (require.aliases.hasOwnProperty(path)) return require.aliases[path];
}
};
/**
* 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)) {
throw new Error('Failed to alias "' + from + '", it does not exist');
}
require.aliases[to] = from;
};
/**
* 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, '..');
/**
* lastIndexOf helper.
*/
function lastIndexOf(arr, obj) {
var i = arr.length;
while (i--) {
if (arr[i] === obj) return i;
}
return -1;
}
/**
* 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 = lastIndexOf(segs, 'deps') + 1;
if (!i) i = 0;
path = segs.slice(0, i + 1).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("component-emitter/index.js", function(exports, require, module){
/**
* Expose `Emitter`.
*/
module.exports = Emitter;
/**
* Initialize a new `Emitter`.
*
* @api public
*/
function Emitter(obj) {
if (obj) return mixin(obj);
};
/**
* Mixin the emitter properties.
*
* @param {Object} obj
* @return {Object}
* @api private
*/
function mixin(obj) {
for (var key in Emitter.prototype) {
obj[key] = Emitter.prototype[key];
}
return obj;
}
/**
* Listen on the given `event` with `fn`.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.on =
Emitter.prototype.addEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
(this._callbacks[event] = this._callbacks[event] || [])
.push(fn);
return this;
};
/**
* Adds an `event` listener that will be invoked a single
* time then automatically removed.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.once = function(event, fn){
var self = this;
this._callbacks = this._callbacks || {};
function on() {
self.off(event, on);
fn.apply(this, arguments);
}
on.fn = fn;
this.on(event, on);
return this;
};
/**
* Remove the given callback for `event` or all
* registered callbacks.
*
* @param {String} event
* @param {Function} fn
* @return {Emitter}
* @api public
*/
Emitter.prototype.off =
Emitter.prototype.removeListener =
Emitter.prototype.removeAllListeners =
Emitter.prototype.removeEventListener = function(event, fn){
this._callbacks = this._callbacks || {};
// all
if (0 == arguments.length) {
this._callbacks = {};
return this;
}
// specific event
var callbacks = this._callbacks[event];
if (!callbacks) return this;
// remove all handlers
if (1 == arguments.length) {
delete this._callbacks[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;
};
/**
* Emit `event` with the given args.
*
* @param {String} event
* @param {Mixed} ...
* @return {Emitter}
*/
Emitter.prototype.emit = function(event){
this._callbacks = this._callbacks || {};
var args = [].slice.call(arguments, 1)
, callbacks = this._callbacks[event];
if (callbacks) {
callbacks = callbacks.slice(0);
for (var i = 0, len = callbacks.length; i < len; ++i) {
callbacks[i].apply(this, args);
}
}
return this;
};
/**
* Return array of callbacks for `event`.
*
* @param {String} event
* @return {Array}
* @api public
*/
Emitter.prototype.listeners = function(event){
this._callbacks = this._callbacks || {};
return this._callbacks[event] || [];
};
/**
* Check if this emitter has `event` handlers.
*
* @param {String} event
* @return {Boolean}
* @api public
*/
Emitter.prototype.hasListeners = function(event){
return !! this.listeners(event).length;
};
});
require.register("vue/src/main.js", function(exports, require, module){
var config = require('./config'),
ViewModel = require('./viewmodel'),
directives = require('./directives'),
filters = require('./filters'),
utils = require('./utils')
/**
* 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
}
/**
* Allows user to register/retrieve a directive definition
*/
ViewModel.directive = function (id, fn) {
if (!fn) return directives[id]
directives[id] = fn
return this
}
/**
* Allows user to register/retrieve a filter function
*/
ViewModel.filter = function (id, fn) {
if (!fn) return filters[id]
filters[id] = fn
return this
}
/**
* Allows user to register/retrieve a ViewModel constructor
*/
ViewModel.component = function (id, Ctor) {
if (!Ctor) return utils.components[id]
utils.components[id] = utils.toConstructor(Ctor)
return this
}
/**
* Allows user to register/retrieve a template partial
*/
ViewModel.partial = function (id, partial) {
if (!partial) return utils.partials[id]
utils.partials[id] = utils.toFragment(partial)
return this
}
/**
* Allows user to register/retrieve a transition definition object
*/
ViewModel.transition = function (id, transition) {
if (!transition) return utils.transitions[id]
utils.transitions[id] = transition
return this
}
ViewModel.extend = extend
ViewModel.nextTick = utils.nextTick
/**
* Expose the main ViewModel class
* and add extend method
*/
function extend (options) {
var ParentVM = this
// inherit options
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)
// copy prototype props
var methods = options.methods
if (methods) {
for (var key in methods) {
if (
!(key in ViewModel.prototype) &&
typeof methods[key] === 'function'
) {
proto[key] = methods[key]
}
}
}
// allow extended VM to be further extended
ExtendedVM.extend = extend
ExtendedVM.super = ParentVM
ExtendedVM.options = options
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 || utils.hash()
if (!parent) return child
for (var key in parent) {
if (key === 'el' || key === 'methods') continue
var val = child[key],
parentVal = parent[key],
type = utils.typeOf(val)
if (topLevel && type === 'Function' && parentVal) {
// merge hook functions
child[key] = mergeHook(val, parentVal)
} else if (topLevel && type === 'Object') {
// merge toplevel object options
inheritOptions(val, parentVal)
} else if (val === undefined) {
// inherit if child doesn't override
child[key] = parentVal
}
}
return child
}
/**
* Merge hook functions
* so parent hooks also get called
*/
function mergeHook (fn, parentFn) {
return function (opts) {
parentFn.call(this, opts)
fn.call(this, opts)
}
}
module.exports = ViewModel
});
require.register("vue/src/emitter.js", function(exports, require, module){
// shiv to make this work for Component, Browserify and Node at the same time.
var Emitter,
componentEmitter = 'emitter'
try {
// Requiring without a string literal will make browserify
// unable to parse the dependency, thus preventing it from
// stopping the compilation after a failed lookup.
Emitter = require(componentEmitter)
} catch (e) {
Emitter = require('events').EventEmitter
Emitter.prototype.off = function () {
var method = arguments.length > 1
? this.removeListener
: this.removeAllListeners
return method.apply(this, arguments)
}
}
module.exports = Emitter
});
require.register("vue/src/config.js", function(exports, require, module){
var prefix = 'v',
specialAttributes = [
'pre',
'text',
'repeat',
'partial',
'with',
'component',
'component-id',
'transition'
],
config = module.exports = {
debug : false,
silent : false,
enterClass : 'v-enter',
leaveClass : 'v-leave',
attrs : {},
get prefix () {
return prefix
},
set prefix (val) {
prefix = val
updatePrefix()
}
}
function updatePrefix () {
specialAttributes.forEach(function (attr) {
config.attrs[attr] = prefix + '-' + attr
})
}
updatePrefix()
});
require.register("vue/src/utils.js", function(exports, require, module){
var config = require('./config'),
attrs = config.attrs,
toString = Object.prototype.toString,
join = Array.prototype.join,
console = window.console,
hasClassList = 'classList' in document.documentElement,
ViewModel // late def
var defer =
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.setTimeout
/**
* Create a prototype-less object
* which is a better hash/map
*/
function makeHash () {
return Object.create(null)
}
var utils = module.exports = {
hash: makeHash,
// global storage for user-registered
// vms, partials and transitions
components : makeHash(),
partials : makeHash(),
transitions : makeHash(),
/**
* get an attribute and remove it.
*/
attr: function (el, type, noRemove) {
var attr = attrs[type],
val = el.getAttribute(attr)
if (!noRemove && 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, configurable) {
if (obj.hasOwnProperty(key)) return
Object.defineProperty(obj, key, {
value : val,
enumerable : !!enumerable,
configurable : !!configurable
})
},
/**
* Accurate type check
* internal use only, so no need to check for NaN
*/
typeOf: function (obj) {
return toString.call(obj).slice(8, -1)
},
/**
* 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 only strings and numbers are output to html
* output empty string is value is not string or number
*/
toText: function (value) {
/* jshint eqeqeq: false */
return (typeof value === 'string' ||
typeof value === 'boolean' ||
(typeof value === 'number' && value == value)) // deal with NaN
? value
: ''
},
/**
* simple extend
*/
extend: function (obj, ext, protective) {
for (var key in ext) {
if (protective && obj[key]) continue
obj[key] = ext[key]
}
},
/**
* 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 a string template to a dom fragment
*/
toFragment: function (template) {
if (typeof template !== 'string') {
return template
}
if (template.charAt(0) === '#') {
var templateNode = document.getElementById(template.slice(1))
if (!templateNode) return
template = templateNode.innerHTML
}
var node = document.createElement('div'),
frag = document.createDocumentFragment(),
child
node.innerHTML = template.trim()
/* jshint boss: true */
while (child = node.firstChild) {
frag.appendChild(child)
}
return frag
},
/**
* Convert the object to a ViewModel constructor
* if it is not already one
*/
toConstructor: function (obj) {
ViewModel = ViewModel || require('./viewmodel')
return utils.typeOf(obj) === 'Object'
? ViewModel.extend(obj)
: typeof obj === 'function'
? obj
: null
},
/**
* convert certain option values to the desired format.
*/
processOptions: function (options) {
var components = options.components,
partials = options.partials,
template = options.template,
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 (template) {
options.template = utils.toFragment(template)
}
},
/**
* log for debugging
*/
log: function () {
if (config.debug && console) {
console.log(join.call(arguments, ' '))
}
},
/**
* warnings, traces by default
* can be suppressed by `silent` option.
*/
warn: function() {
if (!config.silent && console) {
console.warn(join.call(arguments, ' '))
if (config.debug) {
console.trace()
}
}
},
/**
* 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()
}
}
}
});
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'),
// cache methods
slice = Array.prototype.slice,
log = utils.log,
makeHash = utils.hash,
extend = utils.extend,
def = utils.defProtected,
hasOwn = Object.prototype.hasOwnProperty
/**
* The DOM compiler
* scans a DOM node and compile bindings for a ViewModel
*/
function Compiler (vm, options) {
var compiler = this
// indicate that we are intiating this instance
// so we should not run any transitions
compiler.init = true
// process and extend options
options = compiler.options = options || makeHash()
utils.processOptions(options)
// copy data, methods & compiler options
var data = compiler.data = options.data || {}
extend(vm, data, true)
extend(vm, options.methods, true)
extend(compiler, options.compilerOptions)
// initialize element
var el = compiler.setupElement(options)
log('\nnew VM instance:', el.tagName, '\n')
// set compiler properties
compiler.vm = vm
compiler.bindings = makeHash()
compiler.dirs = []
compiler.deferred = []
compiler.exps = []
compiler.computed = []
compiler.childCompilers = []
compiler.emitter = new Emitter()
// set inenumerable VM properties
def(vm, '$', makeHash())
def(vm, '$el', el)
def(vm, '$compiler', compiler)
def(vm, '$root', getRoot(compiler).vm)
// set parent VM
// and register child id on parent
var parent = compiler.parentCompiler,
childId = utils.attr(el, 'component-id')
if (parent) {
parent.childCompilers.push(compiler)
def(vm, '$parent', parent.vm)
if (childId) {
compiler.childId = childId
parent.vm.$[childId] = vm
}
}
// setup observer
compiler.setupObserver()
// create bindings for computed properties
var computed = options.computed
if (computed) {
for (var key in computed) {
compiler.createBinding(key)
}
}
// beforeCompile hook
compiler.execHook('beforeCompile', 'created')
// the user might have set some props on the vm
// so copy it back to the data...
extend(data, vm)
// observe the data
Observer.observe(data, '', compiler.observer)
// for repeated items, create an index binding
// which should be inenumerable but configurable
if (compiler.repeat) {
//data.$index = compiler.repeatIndex
def(data, '$index', compiler.repeatIndex, false, true)
compiler.createBinding('$index')
}
// allow the $data object to be swapped
Object.defineProperty(vm, '$data', {
enumerable: false,
get: function () {
return compiler.data
},
set: function (newData) {
var oldData = compiler.data
Observer.unobserve(oldData, '', compiler.observer)
compiler.data = newData
Observer.copyPaths(newData, oldData)
Observer.observe(newData, '', compiler.observer)
}
})
// now parse the DOM, during which we will create necessary bindings
// and bind the parsed directives
compiler.compile(el, true)
// bind deferred directives (child components)
for (var i = 0, l = compiler.deferred.length; i < l; i++) {
compiler.bindDirective(compiler.deferred[i])
}
// extract dependencies for computed properties
compiler.parseDeps()
// done!
compiler.init = false
// post compile / ready hook
compiler.execHook('afterCompile', '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 = this.el = typeof options.el === 'string'
? document.querySelector(options.el)
: options.el || document.createElement(options.tagName || 'div')
var template = options.template
if (template) {
// replace option: use the first node in
// the template directly
if (options.replace && template.childNodes.length === 1) {
var replacer = template.childNodes[0].cloneNode(true)
if (el.parentNode) {
el.parentNode.insertBefore(replacer, el)
el.parentNode.removeChild(el)
}
el = replacer
} else {
el.innerHTML = ''
el.appendChild(template.cloneNode(true))
}
}
// apply element options
if (options.id) el.id = options.id
if (options.className) el.className = options.className
var attrs = options.attributes
if (attrs) {
for (var attr in attrs) {
el.setAttribute(attr, attrs[attr])
}
}
return el
}
/**
* Setup observer.
* The observer listens for get/set/mutate events on all VM
* values/objects and trigger corresponding binding updates.
*/
CompilerProto.setupObserver = function () {
var compiler = this,
bindings = compiler.bindings,
observer = compiler.observer = new Emitter()
// a hash to hold event proxies for each root level key
// so they can be referenced and removed later
observer.proxies = makeHash()
// add own listeners which trigger binding updates
observer
.on('get', function (key) {
check(key)
DepsParser.catcher.emit('get', bindings[key])
})
.on('set', function (key, val) {
observer.emit('change:' + key, val)
check(key)
bindings[key].update(val)
})
.on('mutate', function (key, val, mutation) {
observer.emit('change:' + key, val, mutation)
check(key)
bindings[key].pub()
})
function check (key) {
if (!bindings[key]) {
compiler.createBinding(key)
}
}
}
/**
* Compile a DOM node (recursive)
*/
CompilerProto.compile = function (node, root) {
var compiler = this,
nodeType = node.nodeType,
tagName = node.tagName
if (nodeType === 1 && tagName !== 'SCRIPT') { // a normal node
// skip anything with v-pre
if (utils.attr(node, 'pre') !== null) return
// special attributes to check
var repeatExp,
withKey,
partialId,
directive,
componentId = utils.attr(node, 'component') || tagName.toLowerCase(),
componentCtor = compiler.getOption('components', componentId)
// It is important that we access these attributes
// procedurally because the order matters.
//
// `utils.attr` removes the attribute once it gets the
// value, so we should not access them all at once.
// v-repeat has the highest priority
// and we need to preserve all other attributes for it.
/* jshint boss: true */
if (repeatExp = utils.attr(node, 'repeat')) {
// repeat block cannot have v-id at the same time.
directive = Directive.parse('repeat', repeatExp, compiler, node)
if (directive) {
directive.Ctor = componentCtor
// defer child component compilation
// so by the time they are compiled, the parent
// would have collected all bindings
compiler.deferred.push(directive)
}
// v-with has 2nd highest priority
} else if (!root && ((withKey = utils.attr(node, 'with')) || componentCtor)) {
directive = Directive.parse('with', withKey || '', compiler, node)
if (directive) {
directive.Ctor = componentCtor
compiler.deferred.push(directive)
}
} else {
// check transition property
node.vue_trans = utils.attr(node, 'transition')
// replace innerHTML with partial
partialId = utils.attr(node, 'partial')
if (partialId) {
var partial = compiler.getOption('partials', partialId)
if (partial) {
node.innerHTML = ''
node.appendChild(partial.cloneNode(true))
}
}
// finally, only normal directives left!
compiler.compileNode(node)
}
} else if (nodeType === 3) { // text node
compiler.compileTextNode(node)
}
}
/**
* Compile a normal node
*/
CompilerProto.compileNode = function (node) {
var i, j,
attrs = node.attributes,
prefix = config.prefix + '-'
// parse if has attributes
if (attrs && attrs.length) {
var attr, isDirective, exps, exp, directive
// loop through all attributes
i = attrs.length
while (i--) {
attr = attrs[i]
isDirective = false
if (attr.name.indexOf(prefix) === 0) {
// a directive - split, parse and bind it.
isDirective = true
exps = Directive.split(attr.value)
// loop through clauses (separated by ",")
// inside each attribute
j = exps.length
while (j--) {
exp = exps[j]
directive = Directive.parse(attr.name.slice(prefix.length), exp, this, node)
if (directive) {
this.bindDirective(directive)
}
}
} else {
// non directive attribute, check interpolation tags
exp = TextParser.parseAttr(attr.value)
if (exp) {
directive = Directive.parse('attr', attr.name + ':' + exp, this, node)
if (directive) {
this.bindDirective(directive)
}
}
}
if (isDirective) node.removeAttribute(attr.name)
}
}
// recursively compile childNodes
if (node.childNodes.length) {
var nodes = slice.call(node.childNodes)
for (i = 0, j = nodes.length; i < j; i++) {
this.compile(nodes[i])
}
}
}
/**
* Compile a text node
*/
CompilerProto.compileTextNode = function (node) {
var tokens = TextParser.parse(node.nodeValue)
if (!tokens) return
var el, token, directive, partial, partialId, partialNodes
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
partialId = token.key.slice(1).trim()
partial = this.getOption('partials', partialId)
if (partial) {
el = partial.cloneNode(true)
// save an Array reference of the partial's nodes
// so we can compile them AFTER appending the fragment
partialNodes = slice.call(el.childNodes)
}
} else { // a real binding
if (!token.html) { // text binding
el = document.createTextNode('')
directive = Directive.parse('text', token.key, this, el)
} else { // html binding
el = document.createComment(config.prefix + '-html')
directive = Directive.parse('html', token.key, this, el)
}
}
} else { // a plain string
el = document.createTextNode(token)
}
// insert node
node.parentNode.insertBefore(el, node)
if (directive) {
this.bindDirective(directive)
}
// 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.
if (partialNodes) {
for (var j = 0, k = partialNodes.length; j < k; j++) {
this.compile(partialNodes[j])
}
partialNodes = null
}
}
node.parentNode.removeChild(node)
}
/**
* Add a directive instance to the correct binding & viewmodel
*/
CompilerProto.bindDirective = function (directive) {
// keep track of it so we can unbind() later
this.dirs.push(directive)
// for a simple directive, simply call its bind() or _update()
// and we're done.
if (directive.isEmpty) {
if (directive.bind) directive.bind()
return
}
// otherwise, we got more work to do...
var binding,
compiler = this,
key = directive.key
if (directive.isExp) {
// expression bindings are always created on current compiler
binding = compiler.createBinding(key, true, directive.isFn)
} else {
// recursively locate which compiler owns the binding
while (compiler) {
if (compiler.hasKey(key)) {
break
} else {
compiler = compiler.parentCompiler
}
}
compiler = compiler || this
binding = compiler.bindings[key] || compiler.createBinding(key)
}
binding.instances.push(directive)
directive.binding = binding
// invoke bind hook if exists
if (directive.bind) {
directive.bind()
}
// set initial value
directive.update(binding.val(), true)
}
/**
* Create binding and attach getter/setter for a key to the viewmodel object
*/
CompilerProto.createBinding = function (key, isExp, isFn) {
log(' created binding: ' + key)
var compiler = this,
bindings = compiler.bindings,
computed = compiler.options.computed,
binding = new Binding(compiler, key, isExp, isFn)
if (isExp) {
// expression bindings are anonymous
compiler.defineExp(key, binding)
} 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 {
// normal property
compiler.defineProp(key, binding)
}
} else {
// ensure path in data so it can be observed
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.__observer__
// make sure the key is present in data
// so it can be observed
if (!(key in data)) {
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 && !(key in ob.values)) {
Observer.convert(data, key)
}
binding.value = data[key]
Object.defineProperty(compiler.vm, key, {
get: function () {
return compiler.data[key]
},
set: function (val) {
compiler.data[key] = val
}
})
}
/**
* Define an expression binding, which is essentially
* an anonymous computed property
*/
CompilerProto.defineExp = function (key, binding) {
var getter = ExpParser.parse(key, this)
if (getter) {
this.markComputed(binding, getter)
this.exps.push(binding)
}
}
/**
* Define a computed property on the VM
*/
CompilerProto.defineComputed = function (key, binding, value) {
this.markComputed(binding, value)
var def = {
get: binding.value.$get,
set: binding.value.$set
}
Object.defineProperty(this.vm, key, def)
}
/**
* 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) {
var opts = this.options,
parent = this.parentCompiler
return (opts[type] && opts[type][id]) || (
parent
? parent.getOption(type, id)
: utils[type] && utils[type][id]
)
}
/**
* Execute a user hook
*/
CompilerProto.execHook = function (id, alt) {
var opts = this.options,
hook = opts[id] || opts[alt]
if (hook) {
hook.call(this.vm, opts)
}
}
/**
* Check if a compiler's data contains a keypath
*/
CompilerProto.hasKey = function (key) {
var baseKey = key.split('.')[0]
return hasOwn.call(this.data, baseKey) ||
hasOwn.call(this.vm, baseKey)
}
/**
* Collect dependencies for computed properties
*/
CompilerProto.parseDeps = function () {
if (!this.computed.length) return
DepsParser.parse(this.computed)
}
/**
* Unbind and remove element
*/
CompilerProto.destroy = function () {
var compiler = this,
i, key, dir, instances, binding,
vm = compiler.vm,
el = compiler.el,
directives = compiler.dirs,
exps = compiler.exps,
bindings = compiler.bindings
compiler.execHook('beforeDestroy')
// unwatch
compiler.observer.off()
compiler.emitter.off()
// 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 instances
if (!dir.isEmpty && dir.binding.compiler !== compiler) {
instances = dir.binding.instances
if (instances) instances.splice(instances.indexOf(dir), 1)
}
dir.unbind()
}
// unbind all expressions (anonymous bindings)
i = exps.length
while (i--) {
exps[i].unbind()
}
// unbind/unobserve all own bindings
for (key in bindings) {
binding = bindings[key]
if (binding) {
if (binding.root) {
Observer.unobserve(binding.value, binding.key, compiler.observer)
}
binding.unbind()
}
}
// remove self from parentCompiler
var parent = compiler.parentCompiler,
childId = compiler.childId
if (parent) {
parent.childCompilers.splice(parent.childCompilers.indexOf(compiler), 1)
if (childId) {
delete parent.vm.$[childId]
}
}
// finally remove dom element
if (el === document.body) {
el.innerHTML = ''
} else {
vm.$remove()
}
compiler.execHook('afterDestroy')
}
// Helpers --------------------------------------------------------------------
/**
* shorthand for getting root compiler
*/
function getRoot (compiler) {
while (compiler.parentCompiler) {
compiler = compiler.parentCompiler
}
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'),
def = utils.defProtected,
nextTick = utils.nextTick
/**
* 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 set an actual nested value
* from a flat key string. Used in directives.
*/
def(VMProto, '$set', function (key, value) {
var path = key.split('.'),
obj = getTargetVM(this, path)
if (!obj) return
for (var d = 0, l = path.length - 1; d < l; d++) {
obj = obj[path[d]]
}
obj[path[d]] = value
})
/**
* watch a key on the viewmodel for changes
* fire callback with new value
*/
def(VMProto, '$watch', function (key, callback) {
var self = this
function on () {
var args = arguments
utils.nextTick(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.childCompilers,
i = children.length,
child
while (i--) {
child = children[i]
child.emitter.emit.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.parentCompiler
emitter.emit.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) {
def(VMProto, '$' + method, function () {
var emitter = this.$compiler.emitter
emitter[method].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,
parent = el.parentNode
if (!parent) return
transition(el, -1, function () {
parent.removeChild(el)
if (cb) nextTick(cb)
}, this.$compiler)
})
def(VMProto, '$before', function (target, cb) {
target = query(target)
var el = this.$el,
parent = target.parentNode
if (!parent) return
transition(el, 1, function () {
parent.insertBefore(el, target)
if (cb) nextTick(cb)
}, this.$compiler)
})
def(VMProto, '$after', function (target, cb) {
target = query(target)
var el = this.$el,
parent = target.parentNode,
next = target.nextSibling
if (!parent) return
transition(el, 1, function () {
if (next) {
parent.insertBefore(el, next)
} else {
parent.appendChild(el)
}
if (cb) nextTick(cb)
}, this.$compiler)
})
function query (el) {
return typeof el === 'string'
? document.querySelector(el)
: el
}
/**
* If a VM doesn't contain a path, go up the prototype chain
* to locate the ancestor that has it.
*/
function getTargetVM (vm, path) {
var baseKey = path[0],
binding = vm.$compiler.bindings[baseKey]
return binding
? binding.compiler.vm
: null
}
module.exports = ViewModel
});
require.register("vue/src/binding.js", function(exports, require, module){
var batcher = require('./batcher'),
id = 0
/**
* 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 = id++
this.value = undefined
this.isExp = !!isExp
this.isFn = isFn
this.root = !this.isExp && key.indexOf('.') === -1
this.compiler = compiler
this.key = key
this.instances = []
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
}
batcher.queue(this)
}
/**
* Actually update the instances.
*/
BindingProto._update = function () {
var i = this.instances.length,
value = this.val()
while (i--) {
this.instances[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.instances.length
while (i--) {
this.instances[i].unbind()
}
i = this.deps.length
var subs
while (i--) {
subs = this.deps[i].subs
subs.splice(subs.indexOf(this), 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
typeOf = utils.typeOf,
def = utils.defProtected,
slice = Array.prototype.slice,
// types
OBJECT = 'Object',
ARRAY = 'Array',
// Array mutation methods to wrap
methods = ['push','pop','shift','unshift','splice','sort','reverse'],
// 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__,
// lazy load
ViewModel
// The proxy prototype to replace the __proto__ of
// an observed array
var ArrayProxy = Object.create(Array.prototype)
// Define mutation interceptors so we can emit the mutation info
methods.forEach(function (method) {
def(ArrayProxy, method, function () {
var result = Array.prototype[method].apply(this, arguments)
this.__observer__.emit('mutate', this.__observer__.path, this, {
method: method,
args: slice.call(arguments),
result: result
})
return result
}, !hasProto)
})
// Augment it with several convenience methods
var extensions = {
remove: function (index) {
if (typeof index === 'function') {
var i = this.length,
removed = []
while (i--) {
if (index(this[i])) {
removed.push(this.splice(i, 1)[0])
}
}
return removed.reverse()
} else {
if (typeof index !== 'number') {
index = this.indexOf(index)
}
if (index > -1) {
return this.splice(index, 1)[0]
}
}
},
replace: function (index, data) {
if (typeof index === 'function') {
var i = this.length,
replaced = [],
replacer
while (i--) {
replacer = index(this[i])
if (replacer !== undefined) {
replaced.push(this.splice(i, 1, replacer)[0])
}
}
return replaced.reverse()
} else {
if (typeof index !== 'number') {
index = this.indexOf(index)
}
if (index > -1) {
return this.splice(index, 1, data)[0]
}
}
}
}
for (var method in extensions) {
def(ArrayProxy, method, extensions[method], !hasProto)
}
/**
* Watch an Object, recursive.
*/
function watchObject (obj) {
for (var key in obj) {
convert(obj, key)
}
}
/**
* Watch an Array, overload mutation methods
* and add augmentations by intercepting the prototype chain
*/
function watchArray (arr, path) {
var observer = arr.__observer__
if (!observer) {
observer = new Emitter()
def(arr, '__observer__', observer)
}
observer.path = path
if (hasProto) {
arr.__proto__ = ArrayProxy
} else {
for (var key in ArrayProxy) {
def(arr, key, ArrayProxy[key])
}
}
}
/**
* Define accessors for a property on an Object
* so it emits get/set events.
* Then watch the value itself.
*/
function convert (obj, key) {
var keyPrefix = key.charAt(0)
if ((keyPrefix === '$' || keyPrefix === '_') && key !== '$index') {
return
}
// emit set on bind
// this means when an object is observed it will emit
// a first batch of set events.
var observer = obj.__observer__,
values = observer.values,
val = values[key] = obj[key]
observer.emit('set', key, val)
if (Array.isArray(val)) {
observer.emit('set', key + '.length', val.length)
}
Object.defineProperty(obj, key, {
get: function () {
var value = values[key]
// only emit get on tip values
if (pub.shouldGet && typeOf(value) !== OBJECT) {
observer.emit('get', key)
}
return value
},
set: function (newVal) {
var oldVal = values[key]
unobserve(oldVal, key, observer)
values[key] = newVal
copyPaths(newVal, oldVal)
observer.emit('set', key, newVal)
observe(newVal, key, observer)
}
})
observe(val, key, observer)
}
/**
* Check if a value is watchable
*/
function isWatchable (obj) {
ViewModel = ViewModel || require('./viewmodel')
var type = typeOf(obj)
return (type === OBJECT || type === ARRAY) && !(obj instanceof ViewModel)
}
/**
* 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 type = typeOf(obj),
emitter = obj && obj.__observer__
if (type === ARRAY) {
emitter.emit('set', 'length', obj.length)
} else if (type === OBJECT) {
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 (typeOf(oldObj) !== OBJECT || typeOf(newObj) !== OBJECT) {
return
}
var path, type, oldVal, newVal
for (path in oldObj) {
if (!(path in newObj)) {
oldVal = oldObj[path]
type = typeOf(oldVal)
if (type === OBJECT) {
newVal = newObj[path] = {}
copyPaths(newVal, oldVal)
} else if (type === ARRAY) {
newObj[path] = []
} 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.__observer__) convert(obj, sec)
}
obj = obj[sec]
}
if (typeOf(obj) === OBJECT) {
sec = path[i]
if (!(sec in obj)) {
obj[sec] = undefined
if (obj.__observer__) convert(obj, sec)
}
}
}
/**
* 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 + '.' : '',
ob, alreadyConverted = !!obj.__observer__
if (!alreadyConverted) {
def(obj, '__observer__', new Emitter())
}
ob = obj.__observer__
ob.values = ob.values || utils.hash()
observer.proxies = observer.proxies || {}
var proxies = observer.proxies[path] = {
get: function (key) {
observer.emit('get', path + key)
},
set: function (key, val) {
observer.emit('set', path + key, val)
},
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)
}
}
}
ob
.on('get', proxies.get)
.on('set', proxies.set)
.on('mutate', proxies.mutate)
if (alreadyConverted) {
emitSet(obj)
} else {
var type = typeOf(obj)
if (type === OBJECT) {
watchObject(obj)
} else if (type === ARRAY) {
watchArray(obj)
}
}
}
/**
* Cancel observation, turn off the listeners.
*/
function unobserve (obj, path, observer) {
if (!obj || !obj.__observer__) return
path = path ? path + '.' : ''
var proxies = observer.proxies[path]
if (!proxies) return
obj.__observer__
.off('get', proxies.get)
.off('set', proxies.set)
.off('mutate', proxies.mutate)
observer.proxies[path] = null
}
var pub = module.exports = {
// whether to emit get events
// only enabled during dependency parsing
shouldGet : false,
observe : observe,
unobserve : unobserve,
ensurePath : ensurePath,
convert : convert,
copyPaths : copyPaths,
watchArray : watchArray
}
});
require.register("vue/src/directive.js", function(exports, require, module){
var utils = require('./utils'),
directives = require('./directives'),
filters = require('./filters'),
// Regexes!
// regex to split multiple directive expressions
// split by commas, but ignore commas within quotes, parens and escapes.
SPLIT_RE = /(?:['"](?:\\.|[^'"])*['"]|\((?:\\.|[^\)])*\)|\\.|[^,])+/g,
// match up to the first single pipe, ignore those within quotes.
KEY_RE = /^(?:['"](?:\\.|[^'"])*['"]|\\.|[^\|]|\|\|)+/,
ARG_RE = /^([\w- ]+):(.+)$/,
FILTERS_RE = /\|[^\|]+/g,
FILTER_TOKEN_RE = /[^\s']+|'[^']+'/g,
NESTING_RE = /^\$(parent|root)\./,
SINGLE_VAR_RE = /^[\w\.\$]+$/
/**
* Directive class
* represents a single directive instance in the DOM
*/
function Directive (definition, expression, rawKey, compiler, node) {
this.compiler = compiler
this.vm = compiler.vm
this.el = node
var isEmpty = expression === ''
// mix in properties from the directive definition
if (typeof definition === 'function') {
this[isEmpty ? 'bind' : '_update'] = definition
} else {
for (var prop in definition) {
if (prop === 'unbind' || prop === 'update') {
this['_' + prop] = definition[prop]
} else {
this[prop] = definition[prop]
}
}
}
// empty expression, we're done.
if (isEmpty) {
this.isEmpty = true
return
}
this.expression = expression.trim()
this.rawKey = rawKey
parseKey(this, rawKey)
this.isExp = !SINGLE_VAR_RE.test(this.key) || NESTING_RE.test(this.key)
var filterExps = this.expression.slice(rawKey.length).match(FILTERS_RE)
if (filterExps) {
this.filters = []
var i = 0, l = filterExps.length, filter
for (; i < l; i++) {
filter = parseFilter(filterExps[i], this.compiler)
if (filter) this.filters.push(filter)
}
if (!this.filters.length) this.filters = null
} else {
this.filters = null
}
}
var DirProto = Directive.prototype
/**
* parse a key, extract argument and nesting/root info
*/
function parseKey (dir, rawKey) {
var key = rawKey
if (rawKey.indexOf(':') > -1) {
var argMatch = rawKey.match(ARG_RE)
key = argMatch
? argMatch[2].trim()
: key
dir.arg = argMatch
? argMatch[1].trim()
: null
}
dir.key = key
}
/**
* parse a filter expression
*/
function parseFilter (filter, compiler) {
var tokens = filter.slice(1).match(FILTER_TOKEN_RE)
if (!tokens) return
tokens = tokens.map(function (token) {
return token.replace(/'/g, '').trim()
})
var name = tokens[0],
apply = compiler.getOption('filters', name) || filters[name]
if (!apply) {
utils.warn('Unknown filter: ' + name)
return
}
return {
name : name,
apply : apply,
args : tokens.length > 1
? tokens.slice(1)
: null
}
}
/**
* called when a new value is set
* for computed properties, this will only be called once
* during initialization.
*/
DirProto.update = function (value, init) {
if (!init && value === this.value) return
this.value = value
if (this._update) {
this._update(
this.filters
? this.applyFilters(value)
: value
)
}
}
/**
* 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.call(this.vm, filtered, filter.args)
}
return filtered
}
/**
* Unbind diretive
* @ param {Boolean} update
* Sometimes we call unbind before an update (i.e. not destroy)
* just to teardown previous stuff, in that case we do not want
* to null everything.
*/
DirProto.unbind = function (update) {
// this can be called before the el is even assigned...
if (!this.el) return
if (this._unbind) this._unbind(update)
if (!update) this.vm = this.el = this.binding = this.compiler = null
}
// exposed methods ------------------------------------------------------------
/**
* split a unquoted-comma separated expression into
* multiple clauses
*/
Directive.split = function (exp) {
return exp.indexOf(',') > -1
? exp.match(SPLIT_RE) || ['']
: [exp]
}
/**
* make sure the directive and expression is valid
* before we create an instance
*/
Directive.parse = function (dirname, expression, compiler, node) {
var dir = compiler.getOption('directives', dirname) || directives[dirname]
if (!dir) return utils.warn('unknown directive: ' + dirname)
var rawKey
if (expression.indexOf('|') > -1) {
var keyMatch = expression.match(KEY_RE)
if (keyMatch) {
rawKey = keyMatch[0].trim()
}
} else {
rawKey = expression.trim()
}
// have a valid raw key, or be an empty directive
return (rawKey || expression === '')
? new Directive(dir, expression, rawKey, compiler, node)
: utils.warn('invalid directive expression: ' + expression)
}
module.exports = Directive
});
require.register("vue/src/exp-parser.js", function(exports, require, module){
var utils = require('./utils'),
stringSaveRE = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g,
stringRestoreRE = /"(\d+)"/g
// 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\.]+/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 getRel (path, compiler) {
var rel = '',
dist = 0,
self = compiler
while (compiler) {
if (compiler.hasKey(path)) {
break
} else {
compiler = compiler.parentCompiler
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) {
/* jshint evil: true */
var fn
try {
fn = new Function(exp)
} catch (e) {
utils.warn('Invalid expression: ' + raw)
}
return fn
}
/**
* Escape a leading dollar sign for regex construction
*/
function escapeDollar (v) {
return v.charAt(0) === '$'
? '\\' + v
: v
}
module.exports = {
/**
* Parse and return an anonymous computed property getter function
* from an arbitrary expression, together with a list of paths to be
* created as bindings.
*/
parse: function (exp, compiler) {
// 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 = ('return ' + exp)
.replace(stringSaveRE, saveStrings)
.replace(pathRE, replacePath)
.replace(stringRestoreRE, restoreStrings)
body = accessors + body
function saveStrings (str) {
var i = strings.length
strings[i] = str
return '"' + i + '"'
}
function replacePath (path) {
// keep track of the first char
var c = path.charAt(0)
path = path.slice(1)
var val = 'this.' + getRel(path, compiler) + 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)
}
}
});
require.register("vue/src/text-parser.js", function(exports, require, module){
var BINDING_RE = /{{{?([^{}]+?)}?}}/,
TRIPLE_RE = /{{{[^{}]+}}}/
/**
* Parse a piece of text, return an array of tokens
*/
function parse (text) {
if (!BINDING_RE.test(text)) return null
var m, i, token, 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() }
if (TRIPLE_RE.test(m[0])) token.html = true
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
*/
function parseAttr (attr) {
var tokens = parse(attr)
if (!tokens) return null
var res = [], token
for (var i = 0, l = tokens.length; i < l; i++) {
token = tokens[i]
res.push(token.key || ('"' + token + '"'))
}
return res.join('+')
}
exports.parse = parse
exports.parseAttr = parseAttr
});
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 (has && has.compiler === dep.compiler) return
got[dep.key] = dep
utils.log(' - ' + dep.key)
binding.deps.push(dep)
dep.subs.push(binding)
})
binding.value.$get()
catcher.off('get')
}
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
var i = bindings.length
while (i--) { catchDeps(bindings[i]) }
Observer.shouldGet = false
utils.log('\ndone.')
}
}
});
require.register("vue/src/filters.js", function(exports, require, module){
var keyCodes = {
enter : 13,
tab : 9,
'delete' : 46,
up : 38,
left : 37,
right : 39,
down : 40,
esc : 27
}
module.exports = {
/**
* 'abc' => 'Abc'
*/
capitalize: function (value) {
if (!value && value !== 0) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
},
/**
* 'abc' => 'ABC'
*/
uppercase: function (value) {
return (value || value === 0)
? value.toString().toUpperCase()
: ''
},
/**
* 'AbC' => 'abc'
*/
lowercase: function (value) {
return (value || value === 0)
? value.toString().toLowerCase()
: ''
},
/**
* 12345 => $12,345.00
*/
currency: function (value, args) {
if (!value && value !== 0) return ''
var sign = (args && args[0]) || '$',
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']
*/
pluralize: function (value, args) {
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.
*/
key: function (handler, args) {
if (!handler) return
var code = keyCodes[args[0]]
if (!code) {
code = parseInt(args[0], 10)
}
return function (e) {
if (e.keyCode === code) {
handler.call(this, e)
}
}
}
}
});
require.register("vue/src/transition.js", function(exports, require, module){
var endEvent = sniffTransitionEndEvent(),
config = require('./config'),
// 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
}
/**
* stage:
* 1 = enter
* 2 = leave
*/
var transition = module.exports = function (el, stage, cb, compiler) {
var changeState = function () {
cb()
compiler.execHook(stage > 0 ? 'enteredView' : 'leftView')
}
if (compiler.init) {
changeState()
return codes.INIT
}
var transitionId = el.vue_trans
if (transitionId) {
return applyTransitionFunctions(
el,
stage,
changeState,
transitionId,
compiler
)
} else if (transitionId === '') {
return applyTransitionClass(
el,
stage,
changeState
)
} else {
changeState()
return codes.SKIP
}
}
transition.codes = codes
/**
* Togggle a CSS class to trigger transition
*/
function applyTransitionClass (el, stage, changeState) {
if (!endEvent) {
changeState()
return codes.CSS_SKIP
}
// if the browser supports transition,
// it must have classList...
var classList = el.classList,
lastLeaveCallback = el.vue_trans_cb
if (stage > 0) { // enter
// cancel unfinished leave transition
if (lastLeaveCallback) {
el.removeEventListener(endEvent, lastLeaveCallback)
el.vue_trans_cb = null
}
// set to hidden state before appending
classList.add(config.enterClass)
// append
changeState()
// force a layout so transition can be triggered
/* jshint unused: false */
var forceLayout = el.clientHeight
// trigger transition
classList.remove(config.enterClass)
return codes.CSS_E
} else { // leave
// trigger hide transition
classList.add(config.leaveClass)
var onEnd = function (e) {
if (e.target === el) {
el.removeEventListener(endEvent, onEnd)
el.vue_trans_cb = null
// actually remove node here
changeState()
classList.remove(config.leaveClass)
}
}
// attach transition end listener
el.addEventListener(endEvent, onEnd)
el.vue_trans_cb = onEnd
return codes.CSS_L
}
}
function applyTransitionFunctions (el, stage, changeState, functionId, compiler) {
var funcs = compiler.getOption('transitions', functionId)
if (!funcs) {
changeState()
return codes.JS_SKIP
}
var enter = funcs.enter,
leave = funcs.leave
if (stage > 0) { // enter
if (typeof enter !== 'function') {
changeState()
return codes.JS_SKIP_E
}
enter(el, changeState)
return codes.JS_E
} else { // leave
if (typeof leave !== 'function') {
changeState()
return codes.JS_SKIP_L
}
leave(el, changeState)
return codes.JS_L
}
}
/**
* Sniff proper transition end event name
*/
function sniffTransitionEndEvent () {
var el = document.createElement('vue'),
defaultEvent = 'transitionend',
events = {
'transition' : defaultEvent,
'mozTransition' : defaultEvent,
'webkitTransition' : 'webkitTransitionEnd'
}
for (var name in events) {
if (el.style[name] !== undefined) {
return events[name]
}
}
}
});
require.register("vue/src/batcher.js", function(exports, require, module){
var utils = require('./utils'),
queue, has, waiting
reset()
exports.queue = function (binding) {
if (!has[binding.id]) {
queue.push(binding)
has[binding.id] = true
if (!waiting) {
waiting = true
utils.nextTick(flush)
}
}
}
function flush () {
for (var i = 0; i < queue.length; i++) {
var b = queue[i]
if (b.unbound) continue
b._update()
has[b.id] = false
}
reset()
}
function reset () {
queue = []
has = utils.hash()
waiting = false
}
});
require.register("vue/src/directives/index.js", function(exports, require, module){
var utils = require('../utils'),
transition = require('../transition')
module.exports = {
on : require('./on'),
repeat : require('./repeat'),
model : require('./model'),
'if' : require('./if'),
'with' : require('./with'),
html : require('./html'),
attr: function (value) {
this.el.setAttribute(this.arg, value)
},
text: function (value) {
this.el.textContent = utils.toText(value)
},
show: function (value) {
var el = this.el,
target = value ? '' : 'none',
change = function () {
el.style.display = target
}
transition(el, value ? 1 : -1, change, this.compiler)
},
'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
}
}
}
}
});
require.register("vue/src/directives/if.js", function(exports, require, module){
var config = require('../config'),
transition = require('../transition')
module.exports = {
bind: function () {
this.parent = this.el.parentNode
this.ref = document.createComment(config.prefix + '-if-' + this.key)
this.el.vue_ref = this.ref
},
update: function (value) {
var el = this.el
if (!this.parent) { // the node was detached when bound
if (!el.parentNode) {
return
} else {
this.parent = el.parentNode
}
}
// should always have this.parent if we reach here
var parent = this.parent,
ref = this.ref,
compiler = this.compiler
if (!value) {
transition(el, -1, remove, compiler)
} else {
transition(el, 1, insert, compiler)
}
function remove () {
if (!el.parentNode) return
// insert the reference node
var next = el.nextSibling
if (next) {
parent.insertBefore(ref, next)
} else {
parent.appendChild(ref)
}
parent.removeChild(el)
}
function insert () {
if (el.parentNode) return
parent.insertBefore(el, ref)
parent.removeChild(ref)
}
},
unbind: function () {
this.el.vue_ref = null
}
}
});
require.register("vue/src/directives/repeat.js", function(exports, require, module){
var Observer = require('../observer'),
Emitter = require('../emitter'),
utils = require('../utils'),
config = require('../config'),
transition = require('../transition'),
ViewModel // lazy def to avoid circular dependency
/**
* Mathods that perform precise DOM manipulation
* based on mutator method triggered
*/
var mutationHandlers = {
push: function (m) {
var i, l = m.args.length,
base = this.collection.length - l
for (i = 0; i < l; i++) {
this.buildItem(m.args[i], base + i)
}
},
pop: function () {
var vm = this.vms.pop()
if (vm) vm.$destroy()
},
unshift: function (m) {
var i, l = m.args.length
for (i = 0; i < l; i++) {
this.buildItem(m.args[i], i)
}
},
shift: function () {
var vm = this.vms.shift()
if (vm) vm.$destroy()
},
splice: function (m) {
var i, l,
index = m.args[0],
removed = m.args[1],
added = m.args.length - 2,
removedVMs = this.vms.splice(index, removed)
for (i = 0, l = removedVMs.length; i < l; i++) {
removedVMs[i].$destroy()
}
for (i = 0; i < added; i++) {
this.buildItem(m.args[i + 2], index + i)
}
},
sort: function () {
var vms = this.vms,
col = this.collection,
l = col.length,
sorted = new Array(l),
i, j, vm, data
for (i = 0; i < l; i++) {
data = col[i]
for (j = 0; j < l; j++) {
vm = vms[j]
if (vm.$data === data) {
sorted[i] = vm
break
}
}
}
for (i = 0; i < l; i++) {
this.container.insertBefore(sorted[i].$el, this.ref)
}
this.vms = sorted
},
reverse: function () {
var vms = this.vms
vms.reverse()
for (var i = 0, l = vms.length; i < l; i++) {
this.container.insertBefore(vms[i].$el, this.ref)
}
}
}
module.exports = {
bind: function () {
var el = this.el,
ctn = this.container = el.parentNode
// extract child VM information, if any
ViewModel = ViewModel || require('../viewmodel')
this.Ctor = this.Ctor || ViewModel
// extract transition information
this.hasTrans = el.hasAttribute(config.attrs.transition)
// extract child Id, if any
this.childId = utils.attr(el, 'component-id')
// 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.initiated = false
this.collection = null
this.vms = null
var self = this
this.mutationListener = function (path, arr, mutation) {
var method = mutation.method
mutationHandlers[method].call(self, mutation)
if (method !== 'push' && method !== 'pop') {
self.updateIndexes()
}
if (method === 'push' || method === 'unshift' || method === 'splice') {
self.changed()
}
}
},
update: function (collection, init) {
this.unbind(true)
// attach an object to container to hold handlers
this.container.vue_dHandlers = utils.hash()
// if initiating with an empty collection, we need to
// force a compile so that we get all the bindings for
// dependency extraction.
if (!this.initiated && (!collection || !collection.length)) {
this.buildItem()
this.initiated = true
}
collection = this.collection = collection || []
this.vms = []
if (this.childId) {
this.vm.$[this.childId] = this.vms
}
// listen for collection mutation events
// the collection has been augmented during Binding.set()
if (!collection.__observer__) Observer.watchArray(collection, null, new Emitter())
collection.__observer__.on('mutate', this.mutationListener)
// create child-vms and append to DOM
if (collection.length) {
for (var i = 0, l = collection.length; i < l; i++) {
this.buildItem(collection[i], i)
}
if (!init) this.changed()
}
},
/**
* Notify parent compiler that new items
* have been added to the collection, it needs
* to re-calculate computed property dependencies.
* Batched to ensure it's called only once every event loop.
*/
changed: function () {
if (this.queued) return
this.queued = true
var self = this
setTimeout(function () {
self.compiler.parseDeps()
self.queued = false
}, 0)
},
/**
* Create a new child VM from a data object
* passing along compiler options indicating this
* is a v-repeat item.
*/
buildItem: function (data, index) {
var node = this.el.cloneNode(true),
ctn = this.container,
ref, item
// append node into DOM first
// so v-if can get access to parentNode
if (data) {
ref = this.vms.length > index
? this.vms[index].$el
: this.ref
// make sure it works with v-if
if (!ref.parentNode) ref = ref.vue_ref
// process transition info before appending
node.vue_trans = utils.attr(node, 'transition', true)
transition(node, 1, function () {
ctn.insertBefore(node, ref)
}, this.compiler)
}
item = new this.Ctor({
el: node,
data: data,
compilerOptions: {
repeat: true,
repeatIndex: index,
repeatCollection: this.collection,
parentCompiler: this.compiler,
delegator: ctn
}
})
if (!data) {
// this is a forced compile for an empty collection.
// let's remove it...
item.$destroy()
} else {
this.vms.splice(index, 0, item)
}
},
/**
* Update index of each item after a mutation
*/
updateIndexes: function () {
var i = this.vms.length
while (i--) {
this.vms[i].$data.$index = i
}
},
unbind: function () {
if (this.childId) {
delete this.vm.$[this.childId]
}
if (this.collection) {
this.collection.__observer__.off('mutate', this.mutationListener)
var i = this.vms.length
while (i--) {
this.vms[i].$destroy()
}
}
var ctn = this.container,
handlers = ctn.vue_dHandlers
for (var key in handlers) {
ctn.removeEventListener(handlers[key].event, handlers[key])
}
ctn.vue_dHandlers = null
}
}
});
require.register("vue/src/directives/on.js", function(exports, require, module){
var utils = require('../utils')
function delegateCheck (el, root, identifier) {
while (el && el !== root) {
if (el[identifier]) return el
el = el.parentNode
}
}
module.exports = {
isFn: true,
bind: function () {
if (this.compiler.repeat) {
// attach an identifier to the el
// so it can be matched during event delegation
this.el[this.expression] = true
// attach the owner viewmodel of this directive
this.el.vue_viewmodel = this.vm
}
},
update: function (handler) {
this.unbind(true)
if (typeof handler !== 'function') {
return utils.warn('Directive "on" expects a function value.')
}
var compiler = this.compiler,
event = this.arg,
isExp = this.binding.isExp,
ownerVM = this.binding.compiler.vm
if (compiler.repeat &&
// do not delegate if the repeat is combined with an extended VM
!this.vm.constructor.super &&
// blur and focus events do not bubble
event !== 'blur' && event !== 'focus') {
// for each blocks, delegate for better performance
// focus and blur events dont bubble so exclude them
var delegator = compiler.delegator,
identifier = this.expression,
dHandler = delegator.vue_dHandlers[identifier]
if (dHandler) return
// the following only gets run once for the entire each block
dHandler = delegator.vue_dHandlers[identifier] = function (e) {
var target = delegateCheck(e.target, delegator, identifier)
if (target) {
e.el = target
e.targetVM = target.vue_viewmodel
handler.call(isExp ? e.targetVM : ownerVM, e)
}
}
dHandler.event = event
delegator.addEventListener(event, dHandler)
} else {
// a normal, single element handler
var vm = this.vm
this.handler = function (e) {
e.el = e.currentTarget
e.targetVM = vm
handler.call(ownerVM, e)
}
this.el.addEventListener(event, this.handler)
}
},
unbind: function (update) {
this.el.removeEventListener(this.arg, this.handler)
this.handler = null
if (!update) this.el.vue_viewmodel = null
}
}
});
require.register("vue/src/directives/model.js", function(exports, require, module){
var utils = require('../utils'),
isIE9 = navigator.userAgent.indexOf('MSIE 9.0') > 0
module.exports = {
bind: function () {
var self = this,
el = self.el,
type = el.type,
tag = el.tagName
self.lock = false
// determine what event to listen to
self.event =
(self.compiler.options.lazy ||
tag === 'SELECT' ||
type === 'checkbox' || type === 'radio')
? 'change'
: 'input'
// determine the attribute to change when updating
var attr = self.attr = type === 'checkbox'
? 'checked'
: (tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA')
? 'value'
: 'innerHTML'
var compositionLock = false
this.cLock = function () {
compositionLock = true
}
this.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.vm.$set(self.key, el[attr])
// 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.vm.$set(self.key, el[attr])
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)
}
},
update: function (value) {
if (this.lock) return
/* jshint eqeqeq: false */
var self = this,
el = self.el
if (el.tagName === 'SELECT') { // select dropdown
// setting <select>'s value in IE9 doesn't work
var o = el.options,
i = o.length,
index = -1
while (i--) {
if (o[i].value == value) {
index = i
break
}
}
o.selectedIndex = index
} else if (el.type === 'radio') { // radio button
el.checked = value == el.value
} else if (el.type === 'checkbox') { // checkbox
el.checked = !!value
} else {
el[self.attr] = utils.toText(value)
}
},
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 ViewModel
module.exports = {
bind: function () {
if (this.isEmpty) {
this.build()
}
},
update: function (value) {
if (!this.component) {
this.build(value)
} else {
this.component.$data = value
}
},
build: function (value) {
ViewModel = ViewModel || require('../viewmodel')
var Ctor = this.Ctor || ViewModel
this.component = new Ctor({
el: this.el,
data: value,
compilerOptions: {
parentCompiler: this.compiler
}
})
},
unbind: function () {
this.component.$destroy()
}
}
});
require.register("vue/src/directives/html.js", function(exports, require, module){
var toText = require('../utils').toText,
slice = Array.prototype.slice
module.exports = {
bind: function () {
// a comment node means this is a binding for
// {{{ inline unescaped html }}}
if (this.el.nodeType === 8) {
// hold nodes
this.holder = document.createElement('div')
this.nodes = []
}
},
update: function (value) {
value = toText(value)
if (this.holder) {
this.swap(value)
} else {
this.el.innerHTML = value
}
},
swap: function (value) {
var parent = this.el.parentNode,
holder = this.holder,
nodes = this.nodes,
i = nodes.length, l
while (i--) {
parent.removeChild(nodes[i])
}
holder.innerHTML = value
nodes = this.nodes = slice.call(holder.childNodes)
for (i = 0, l = nodes.length; i < l; i++) {
parent.insertBefore(nodes[i], this.el)
}
}
}
});
require.alias("component-emitter/index.js", "vue/deps/emitter/index.js");
require.alias("component-emitter/index.js", "emitter/index.js");
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');
}})();
\ No newline at end of file
<!doctype html>
<html data-framework="vue">
<head>
<meta charset="utf-8">
<title>Vue.js • TodoMVC</title>
<link rel="stylesheet" href="bower_components/todomvc-common/base.css">
</head>
<body>
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<input id="new-todo" autofocus autocomplete="off" placeholder="What needs to be done?" v-model="newTodo" v-on="keyup:addTodo | key enter">
</header>
<section id="main" v-show="todos.length">
<input id="toggle-all" type="checkbox" v-model="allDone">
<ul id="todo-list">
<li class="todo" v-repeat="todos" v-if="filterTodo(this)" v-class="completed: completed, editing: this == editedTodo">
<div class="view">
<input class="toggle" type="checkbox" v-model="completed" v-on="change: toggleTodo(this)">
<label v-text="title" v-on="dblclick: editTodo(this)"></label>
<button class="destroy" v-on="click: removeTodo(this)"></button>
</div>
<input class="edit" type="text" v-model="title" v-todo-focus="this == editedTodo" v-on="blur: doneEdit(this), keyup: doneEdit(this) | key enter, keyup: cancelEdit(this) | key esc">
</li>
</ul>
</section>
<footer id="footer" v-show="todos.length">
<span id="todo-count">
<strong v-text="remaining"></strong> {{remaining | pluralize item}} left
</span>
<ul id="filters">
<li><a href="#/all" v-class="selected:filter=='all'">All</a></li>
<li><a href="#/active" v-class="selected:filter=='active'">Active</a></li>
<li><a href="#/completed" v-class="selected:filter=='completed'">Completed</a></li>
</ul>
<button id="clear-completed" v-on="click:removeCompleted" v-show="todos.length > remaining">
Clear completed ({{todos.length - remaining}})
</button>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Written by <a href="http://evanyou.me">Evan You</a></p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script src="bower_components/todomvc-common/base.js"></script>
<script src="bower_components/director/director.js"></script>
<script src="bower_components/vue/vue.js"></script>
<script src="js/store.js"></script>
<script src="js/app.js"></script>
<script src="js/routes.js"></script>
</body>
</html>
\ No newline at end of file
/*global Vue, todoStorage */
(function (exports) {
'use strict';
exports.app = new Vue({
// the root element that will be compiled
el: '#todoapp',
// data
data: {
todos: todoStorage.fetch(),
newTodo: '',
editedTodo: null
},
// a custom directive to wait for the DOM to be updated
// before focusing on the input field.
// http://vuejs.org/guide/directives.html#Writing_a_Custom_Directive
directives: {
'todo-focus': function (value) {
if (!value) {
return;
}
var el = this.el;
setTimeout(function () {
el.focus();
}, 0);
}
},
// the `created` lifecycle hook.
// this is where we do the initialization work.
// http://vuejs.org/api/instantiation-options.html#created
created: function () {
// setup filters
this.filters = {
all: function (todo) {
// collect dependency.
// http://vuejs.org/guide/computed.html#Dependency_Collection_Gotcha
/* jshint expr:true */
todo.completed;
return true;
},
active: function (todo) {
return !todo.completed;
},
completed: function (todo) {
return todo.completed;
}
};
// default filter
this.setFilter('all');
},
// computed property
// http://vuejs.org/guide/computed.html
computed: {
remaining: function () {
return this.todos.filter(this.filters.active).length
},
allDone: {
$get: function () {
return this.remaining === 0;
},
$set: function (value) {
this.todos.forEach(function (todo) {
todo.completed = value;
});
todoStorage.save();
}
}
},
// methods that implement data logic.
// note there's no DOM manipulation here at all.
methods: {
setFilter: function (filter) {
this.filter = filter;
this.filterTodo = this.filters[filter];
},
addTodo: function () {
var value = this.newTodo && this.newTodo.trim();
if (!value) {
return;
}
this.todos.push({ title: value, completed: false });
this.newTodo = '';
todoStorage.save();
},
removeTodo: function (todo) {
this.todos.remove(todo.$data);
todoStorage.save();
},
toggleTodo: function (todo) {
todoStorage.save();
},
editTodo: function (todo) {
this.beforeEditCache = todo.title;
this.editedTodo = todo;
},
doneEdit: function (todo) {
if (!this.editedTodo) {
return;
}
this.editedTodo = null;
todo.title = todo.title.trim();
if (!todo.title) {
this.removeTodo(todo);
}
todoStorage.save();
},
cancelEdit: function (todo) {
this.editedTodo = null;
todo.title = this.beforeEditCache;
},
removeCompleted: function () {
this.todos.remove(function (todo) {
return todo.completed;
});
todoStorage.save();
}
}
});
})(window);
\ No newline at end of file
/*global app, Router */
(function (app, Router) {
'use strict';
var router = new Router();
Object.keys(app.filters).forEach(function (filter) {
router.on(filter, function () {
app.setFilter(filter);
});
});
router.init();
})(app, Router);
\ No newline at end of file
/*jshint unused:false */
(function (exports) {
'use strict';
var STORAGE_KEY = 'todos-vuejs';
var todos = null;
exports.todoStorage = {
fetch: function () {
if (!todos) {
todos = JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]');
}
return todos;
},
save: function () {
localStorage.setItem(STORAGE_KEY, JSON.stringify(todos));
}
};
})(window);
\ No newline at end of file
# Vue.js TodoMVC Example
> Vue.js is an intuitive, fast and composable MVVM library for building interactive web interfaces. It provides efficient data bindings with a simple and flexible API.
> _[Vue.js - vuejs.org](http://vuejs.org)_
## Learning Vue.js
The [Vue.js website](http://vuejs.org/) is a great resource to get started.
Here are some links you may find helpful:
* [Official Guide](http://vuejs.org/guide/)
* [API Reference](http://vuejs.org/api/)
* [Examples](http://vuejs.org/examples/)
* [Building Larger Apps with Vue.js](http://vuejs.org/guide/application.html)
* [Performance Comparison](http://vuejs.org/perf/)
Get help from other Vue.js users:
* [Vue.js on Twitter](https://twitter.com/vuejs)
_If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._
## Credit
This TodoMVC application was created by [Evan You](http://evanyou.me).
\ No newline at end of file
......@@ -2138,6 +2138,43 @@
}]
}]
},
"vue": {
"name": "Vue.js",
"description": "Vue.js provides efficient MVVM data bindings with a simple and flexible API. It uses plain JavaScript object models, DOM-based templating and extendable directives and filters.",
"homepage": "vuejs.org",
"examples": [{
"name": "Architecture Example",
"url": "labs/architecture-examples/vue"
}],
"link_groups": [{
"heading": "Official Resources",
"links": [{
"name": "Documentation",
"url": "http://vuejs.org/guide/"
}, {
"name": "API Reference",
"url": "http://vuejs.org/api/"
}, {
"name": "Examples",
"url": "http://vuejs.org/examples/"
}, {
"name": "Vue.js on GitHub",
"url": "https://github.com/yyx990803/vue"
}]
}, {
"heading": "Community",
"links": [{
"name": "Twitter",
"url": "http://twitter.com/vuejs"
}, {
"name": "Google+ Community",
"url": "https://plus.google.com/communities/112229843610661683911"
}, {
"name": "IRC Channel: #vuejs",
"url": "http://freenode.net/faq.shtml#whatwhy"
}]
}]
},
"yui": {
"name": "YUI",
"description": "YUI is a free, open source JavaScript and CSS library for building richly interactive web applications.",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment