Director.js
Director.js 源码
// // 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));
Director.js的更多相关文章
- director.js:客户端的路由---简明中文教程
1.引子 最近学用director.js,那是相当的简单易学易使用.不过开始学的时候,搜搜过后,却没有发现相关的中文教程.于是决定硬啃E文,翻译备用的同时也当是给自己上课并加深对它的理解. direc ...
- director.js教程
directive.js 初始化和注册路由 director.js 的主要对象是Router对象,构造方法如下: var router = new Router(routes); //routes为路 ...
- director.js实现前端路由
注:director.js的官网 https://github.com/flatiron/director director.js是什么? 理解:前端的route框架,director.js客户端的路 ...
- bootstrap + requireJS+ director+ knockout + web API = 一个时髦的单页程序
也许单页程序(Single Page Application)并不是什么时髦的玩意,像Gmail在很早之前就已经在使用这种模式.通常的说法是它通过避免页面刷新大大提高了网站的响应性,像操作桌面应用程序 ...
- 比較Backbone.js, Angular.js, Ember.js, Knockout.js 心得
還記得第一次寫網站的時候,我無意間寫成了 SPA(single page application),當時還沒有SPA這個詞,後來因為廣告主需要不同 url location 頁面的廣告展示,只好把部分 ...
- 一周一个小demo — vue.js实现备忘录功能
这个vue实现备忘录的功能demo是K在github上找到的,K觉得这是一个用来对vue.js入门的一个非常简单的demo,所以拿在这里共享一下. (尊重他人劳动成果,从小事做起~ demo原git ...
- JS实用插件
1. jQuery鼠标滚轮事件插件Mouse Wheel 下载链接:https://github.com/brandonaaron/jquery-mousewheel/ 使用方法: // using ...
- Index
我主要在研究.NET/C# 实现 PC IMERP 和 Android IMERP ,目的在解决企业通信中遇到的各类自动化问题 分布式缓存框架: Microsoft Velocity:微软自家分布 ...
- 初探React,将我们的View标签化
前言 我之前喜欢玩一款游戏:全民飞机大战,而且有点痴迷其中,如果你想站在游戏的第一阶梯,便需要不断的练技术练装备,但是腾讯的游戏一般而言是有点恶心的,他会不断的出新飞机.新装备.新宠物,所以,很多时候 ...
随机推荐
- Java学习笔记(2)
int 和 booleam 不能直接转换,如下语法是不能通过的: boolean b = true; int i = (int) b; int j = 1; boolean a = (boolean) ...
- xcfe桌面快捷键整理
转载自:https://my.oschina.net/u/565351/blog/502018 commands custom <Alt>F1:xfce4-popup-applicatio ...
- blueprint的使用
第一步:导入蓝图模块: from flask import Blueprint 第二步:创建蓝图对象: #Blueprint必须指定两个参数,admin表示蓝图的名称,__name__表示蓝图所在模块 ...
- H5视频直播扫盲
H5视频直播扫盲 2016-05-25 • 前端杂项 • 14 条评论 • lvming19901227 视频直播这么火,再不学就out了. 为了紧跟潮流,本文将向大家介绍一下视频直播中的基本流程和主 ...
- React Lifecycle
React Lifecycle 分为三种: 初始化阶段 状态的更新 销毁 实例化: ReactDom.render 用于将模板转换成HTML语言,并插入DOM节点. 1.getDefaultProps ...
- Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [33,755] milliseconds.
刚部署好程序,第一次登录时,加载非常得慢,查看log日志发现:Creation of SecureRandom instance for session ID generation using [SH ...
- Java中HBase的集中過濾器
比較過濾器: rowKey过滤器 RowFilter 列族过滤器 FamilyFilter 列过滤器 QualifierFilter 列值过滤器 ...
- Appium环境搭建——安装以及运行appium server失败点总结
一.运行Appium失败:未安装.Net Framework 4.5 之前安装AppScan安全测试工具时,就要求.Net 4.5以上环境,我其中一台电脑的系统是Win7-32bit的,安装.Net ...
- Linux下搭建测试环境
一. 安装虚拟机 1.选择linux 型号 3.0x 64的版本 2.磁盘分区 /目录, home目录 ,boot,var ,设置root密码 3.安装(过程略) 二. 配置虚拟机网卡 路径:cd / ...
- (转)Unity_什么是Draw Call? 什么是Batch?
開發遊戲時,一定被時時提醒要減少 Draw Call,當然UNITY也不例外,打開Game Window裡的 Stats,可以看到 Draw Call 與 Batched 的數字.但到底甚麼是 Dra ...