Evan You
8 years ago
4 changed files with 2 additions and 1199 deletions
@ -1,725 +0,0 @@ |
|||
|
|||
|
|||
//
|
|||
// Generated on Tue Dec 16 2014 12:13:47 GMT+0100 (CET) by Charlie Robbins, Paolo Fragomeni & the Contributors (Using Codesurgeon).
|
|||
// Version 1.2.6
|
|||
//
|
|||
|
|||
(function (exports) { |
|||
|
|||
/* |
|||
* browser.js: Browser specific functionality for director. |
|||
* |
|||
* (C) 2011, Charlie Robbins, Paolo Fragomeni, & the Contributors. |
|||
* MIT LICENSE |
|||
* |
|||
*/ |
|||
|
|||
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 |
|||
, routeTo; |
|||
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 { |
|||
if (this.convert_hash_in_init) { |
|||
// Use hash as route
|
|||
routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null; |
|||
if (routeTo) { |
|||
window.history.replaceState({}, document.title, routeTo); |
|||
} |
|||
} |
|||
else { |
|||
// Use canonical url
|
|||
routeTo = this.getPath(); |
|||
} |
|||
|
|||
// 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; |
|||
} |
|||
|
|||
var QUERY_SEPARATOR = /\?.*/; |
|||
|
|||
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.convert_hash_in_init = this.history === true && options.convert_hash_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); |
|||
}; |
|||
return this; |
|||
}; |
|||
|
|||
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.path = function(path, routesFn) { |
|||
var self = this, length = this.scope.length; |
|||
if (path.source) { |
|||
path = path.source.replace(/\\\//ig, "/"); |
|||
} |
|||
path = path.split(new RegExp(this.delimiter)); |
|||
path = terminator(path, this.delimiter); |
|||
this.scope = this.scope.concat(path); |
|||
routesFn.call(this, this); |
|||
this.scope.splice(length, path.length); |
|||
}; |
|||
|
|||
Router.prototype.dispatch = function(method, path, callback) { |
|||
var self = this, fns = this.traverse(method, path.replace(QUERY_SEPARATOR, ""), 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)); |
@ -1,378 +0,0 @@ |
|||
html, |
|||
body { |
|||
margin: 0; |
|||
padding: 0; |
|||
} |
|||
|
|||
button { |
|||
margin: 0; |
|||
padding: 0; |
|||
border: 0; |
|||
background: none; |
|||
font-size: 100%; |
|||
vertical-align: baseline; |
|||
font-family: inherit; |
|||
font-weight: inherit; |
|||
color: inherit; |
|||
-webkit-appearance: none; |
|||
appearance: none; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-font-smoothing: antialiased; |
|||
font-smoothing: antialiased; |
|||
} |
|||
|
|||
body { |
|||
font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; |
|||
line-height: 1.4em; |
|||
background: #f5f5f5; |
|||
color: #4d4d4d; |
|||
min-width: 230px; |
|||
max-width: 550px; |
|||
margin: 0 auto; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-font-smoothing: antialiased; |
|||
font-smoothing: antialiased; |
|||
font-weight: 300; |
|||
} |
|||
|
|||
button, |
|||
input[type="checkbox"] { |
|||
outline: none; |
|||
} |
|||
|
|||
.hidden { |
|||
display: none; |
|||
} |
|||
|
|||
.todoapp { |
|||
background: #fff; |
|||
margin: 130px 0 40px 0; |
|||
position: relative; |
|||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), |
|||
0 25px 50px 0 rgba(0, 0, 0, 0.1); |
|||
} |
|||
|
|||
.todoapp input::-webkit-input-placeholder { |
|||
font-style: italic; |
|||
font-weight: 300; |
|||
color: #e6e6e6; |
|||
} |
|||
|
|||
.todoapp input::-moz-placeholder { |
|||
font-style: italic; |
|||
font-weight: 300; |
|||
color: #e6e6e6; |
|||
} |
|||
|
|||
.todoapp input::input-placeholder { |
|||
font-style: italic; |
|||
font-weight: 300; |
|||
color: #e6e6e6; |
|||
} |
|||
|
|||
.todoapp h1 { |
|||
position: absolute; |
|||
top: -155px; |
|||
width: 100%; |
|||
font-size: 100px; |
|||
font-weight: 100; |
|||
text-align: center; |
|||
color: rgba(175, 47, 47, 0.15); |
|||
-webkit-text-rendering: optimizeLegibility; |
|||
-moz-text-rendering: optimizeLegibility; |
|||
text-rendering: optimizeLegibility; |
|||
} |
|||
|
|||
.new-todo, |
|||
.edit { |
|||
position: relative; |
|||
margin: 0; |
|||
width: 100%; |
|||
font-size: 24px; |
|||
font-family: inherit; |
|||
font-weight: 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); |
|||
box-sizing: border-box; |
|||
-webkit-font-smoothing: antialiased; |
|||
-moz-font-smoothing: antialiased; |
|||
font-smoothing: antialiased; |
|||
} |
|||
|
|||
.new-todo { |
|||
padding: 16px 16px 16px 60px; |
|||
border: none; |
|||
background: rgba(0, 0, 0, 0.003); |
|||
box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); |
|||
} |
|||
|
|||
.main { |
|||
position: relative; |
|||
z-index: 2; |
|||
border-top: 1px solid #e6e6e6; |
|||
} |
|||
|
|||
label[for='toggle-all'] { |
|||
display: none; |
|||
} |
|||
|
|||
.toggle-all { |
|||
position: absolute; |
|||
top: -55px; |
|||
left: -12px; |
|||
width: 60px; |
|||
height: 34px; |
|||
text-align: center; |
|||
border: none; /* Mobile Safari */ |
|||
} |
|||
|
|||
.toggle-all:before { |
|||
content: '❯'; |
|||
font-size: 22px; |
|||
color: #e6e6e6; |
|||
padding: 10px 27px 10px 27px; |
|||
} |
|||
|
|||
.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 solid #ededed; |
|||
} |
|||
|
|||
.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; |
|||
border: none; /* Mobile Safari */ |
|||
-webkit-appearance: none; |
|||
appearance: none; |
|||
} |
|||
|
|||
.todo-list li .toggle:after { |
|||
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#ededed" stroke-width="3"/></svg>'); |
|||
} |
|||
|
|||
.todo-list li .toggle:checked:after { |
|||
content: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="-10 -18 100 135"><circle cx="50" cy="50" r="50" fill="none" stroke="#bddad5" stroke-width="3"/><path fill="#5dc2af" d="M72 25L42 71 27 56l-4 4 20 20 34-52z"/></svg>'); |
|||
} |
|||
|
|||
.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; |
|||
transition: color 0.4s; |
|||
} |
|||
|
|||
.todo-list li.completed label { |
|||
color: #d9d9d9; |
|||
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: 30px; |
|||
color: #cc9a9a; |
|||
margin-bottom: 11px; |
|||
transition: color 0.2s ease-out; |
|||
} |
|||
|
|||
.todo-list li .destroy:hover { |
|||
color: #af5b5e; |
|||
} |
|||
|
|||
.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: 10px 15px; |
|||
height: 20px; |
|||
text-align: center; |
|||
border-top: 1px solid #e6e6e6; |
|||
} |
|||
|
|||
.footer:before { |
|||
content: ''; |
|||
position: absolute; |
|||
right: 0; |
|||
bottom: 0; |
|||
left: 0; |
|||
height: 50px; |
|||
overflow: hidden; |
|||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), |
|||
0 8px 0 -3px #f6f6f6, |
|||
0 9px 1px -3px rgba(0, 0, 0, 0.2), |
|||
0 16px 0 -6px #f6f6f6, |
|||
0 17px 2px -6px rgba(0, 0, 0, 0.2); |
|||
} |
|||
|
|||
.todo-count { |
|||
float: left; |
|||
text-align: left; |
|||
} |
|||
|
|||
.todo-count strong { |
|||
font-weight: 300; |
|||
} |
|||
|
|||
.filters { |
|||
margin: 0; |
|||
padding: 0; |
|||
list-style: none; |
|||
position: absolute; |
|||
right: 0; |
|||
left: 0; |
|||
} |
|||
|
|||
.filters li { |
|||
display: inline; |
|||
} |
|||
|
|||
.filters li a { |
|||
color: inherit; |
|||
margin: 3px; |
|||
padding: 3px 7px; |
|||
text-decoration: none; |
|||
border: 1px solid transparent; |
|||
border-radius: 3px; |
|||
} |
|||
|
|||
.filters li a.selected, |
|||
.filters li a:hover { |
|||
border-color: rgba(175, 47, 47, 0.1); |
|||
} |
|||
|
|||
.filters li a.selected { |
|||
border-color: rgba(175, 47, 47, 0.2); |
|||
} |
|||
|
|||
.clear-completed, |
|||
html .clear-completed:active { |
|||
float: right; |
|||
position: relative; |
|||
line-height: 20px; |
|||
text-decoration: none; |
|||
cursor: pointer; |
|||
position: relative; |
|||
} |
|||
|
|||
.clear-completed:hover { |
|||
text-decoration: underline; |
|||
} |
|||
|
|||
.info { |
|||
margin: 65px auto 0; |
|||
color: #bfbfbf; |
|||
font-size: 10px; |
|||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); |
|||
text-align: center; |
|||
} |
|||
|
|||
.info p { |
|||
line-height: 1; |
|||
} |
|||
|
|||
.info a { |
|||
color: inherit; |
|||
text-decoration: none; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
.info a:hover { |
|||
text-decoration: underline; |
|||
} |
|||
|
|||
/* |
|||
Hack to remove background from Mobile Safari. |
|||
Can't use it globally since it destroys checkboxes in Firefox |
|||
*/ |
|||
@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 { |
|||
-webkit-transform: rotate(90deg); |
|||
transform: rotate(90deg); |
|||
-webkit-appearance: none; |
|||
appearance: none; |
|||
} |
|||
} |
|||
|
|||
@media (max-width: 430px) { |
|||
.footer { |
|||
height: 50px; |
|||
} |
|||
|
|||
.filters { |
|||
bottom: 10px; |
|||
} |
|||
} |
@ -1,78 +0,0 @@ |
|||
setTimeout(function () { |
|||
|
|||
if (window.isPhantom) return |
|||
|
|||
// Initial load & render metrics
|
|||
|
|||
metrics.afterRenderAsync = now() |
|||
console.log('Vue load : ' + (metrics.afterLoad - metrics.beforeLoad).toFixed(2) + 'ms') |
|||
console.log('Render sync : ' + (metrics.afterRender - metrics.beforeRender).toFixed(2) + 'ms') |
|||
console.log('Render async : ' + (metrics.afterRenderAsync - metrics.beforeRender).toFixed(2) + 'ms') |
|||
console.log('Total sync : ' + (metrics.afterRender - metrics.beforeLoad).toFixed(2) + 'ms') |
|||
console.log('Total async : ' + (metrics.afterRenderAsync - metrics.beforeLoad).toFixed(2) + 'ms') |
|||
|
|||
// Benchmark
|
|||
// add 100 items
|
|||
// toggle them one by one
|
|||
// then delete them one by one
|
|||
|
|||
var benchSetting = window.location.search.match(/\bbenchmark=(\d+)/) |
|||
if (!benchSetting) return |
|||
|
|||
var itemsToAdd = +benchSetting[1], |
|||
render, |
|||
bench, |
|||
addTime, |
|||
toggleTime, |
|||
removeTime |
|||
|
|||
var start = now(), |
|||
last |
|||
|
|||
add() |
|||
|
|||
function add() { |
|||
last = now() |
|||
var newTodo = '12345', |
|||
todoInput = document.getElementById('new-todo') |
|||
for (var i = 0; i < itemsToAdd; i++) { |
|||
var keyupEvent = document.createEvent('Event'); |
|||
keyupEvent.initEvent('keyup', true, true); |
|||
keyupEvent.keyCode = 13; |
|||
app.newTodo = 'Something to do ' + i; |
|||
todoInput.dispatchEvent(keyupEvent) |
|||
} |
|||
setTimeout(toggle, 0) |
|||
} |
|||
|
|||
function toggle () { |
|||
addTime = now() - last |
|||
var checkboxes = document.querySelectorAll('.toggle') |
|||
for (var i = 0; i < checkboxes.length; i++) { |
|||
checkboxes[i].click() |
|||
} |
|||
last = now() |
|||
setTimeout(remove, 0) |
|||
} |
|||
|
|||
function remove () { |
|||
toggleTime = now() - last |
|||
var deleteButtons = document.querySelectorAll('.destroy'); |
|||
for (var i = 0; i < deleteButtons.length; i++) { |
|||
deleteButtons[i].click() |
|||
} |
|||
last = now() |
|||
setTimeout(report, 0) |
|||
} |
|||
|
|||
function report () { |
|||
bench = now() - start |
|||
removeTime = now() - last |
|||
console.log('\nBenchmark x ' + itemsToAdd) |
|||
console.log('add : ' + addTime.toFixed(2) + 'ms') |
|||
console.log('toggle : ' + toggleTime.toFixed(2) + 'ms') |
|||
console.log('remove : ' + removeTime.toFixed(2) + 'ms') |
|||
console.log('total : ' + bench.toFixed(2) + 'ms') |
|||
} |
|||
|
|||
}, 0) |
Loading…
Reference in new issue