define('route', function () {
/**
* 路由模块
* @module route
*/
function Router() {}
Router.prototype.definemap = {};
Router.prototype.state = {};
/**
* 定义路由映射表以及路由的回调函数,支持利用正则表达式
* @method setup
* @memberOf module:route
* @param [Object] routemap 路由定义表
* @param [Object] callbacks 回调组
* @example
* var router new Router();
* router.setup({
* '#home': 'Page.Home' //请确保唯一性
* },{
* before: function(){}, //处理路由切换前触发
* fail: function(){} //当路由找不到时的回调
* })
* router.define('Page.Home', function() {
* //todo something
* })
* //同时支持使用正则表达式匹配路由
* //假设一个新闻业务模块包含bbc、cctv、netflix,那么路由可以这样定义:
* //#page/bbc/news/
* //#page/cctv/news/
* //#page/netflix/news/
* router.setup({
* '#page/(\\w+)/news/': 'Page.News' // 可以匹配到上述三个新闻渠道
* });
* router.define('Page.News', function(nId) {
* console.log(nid); //bbc
* });
*/
Router.prototype.setup = function (routemap, cb) {
var rule, func;
this.routemap = [];
this.callback = cb;
for (rule in routemap) {
if (!routemap.hasOwnProperty(rule)) continue;
this.routemap.push({
rule: new RegExp('^' + rule + '$', 'i'),
quote: routemap[rule]
});
}
};
/**
* 定义路由回调
* @method define
* @memberOf module:route
* @param {String} name
* @param {Object}[nexus] 此路由所依赖的关系链
* @param {Boolean}[authorize] 是否需要登录操作
* @param {Function} callback 回调函数,如果有路由关系链,将会等待关系链的成员准备好后再执行回调
* @example
* var router = new Router();
* //一般定义
* router.define('Page.Home', function() {
* //todo something
* })
* //需要登陆验证
* router.define('Page.Home', true, function() {
* //todo something
* });
* //需要路由依赖
* router.define('Page.Home', ['ui.Nav', 'ui.Header', 'ui.Footer'], function() {
* //todo something
* });
* //需要路由依赖、登陆验证
* router.define('Page.Home', ['ui.Nav', 'ui.Header', 'ui.Footer'], true, function() {
* //todo something
* });
*/
Router.prototype.define = function (name, nexus, authorize, cb) {
var route;
if (!authorize && !cb) {
cb = nexus;
authorize = false;
nexus = null;
} else if (nexus instanceof Array && !cb) {
cb = authorize;
authorize = false;
} else if (typeof nexus == 'boolean' && !cb) {
cb = authorize;
authorize = nexus;
nexus = null;
}
this.definemap[name] = {
Func: cb,
authorize: authorize,
nexus: nexus
};
for (var way in this.routemap) {
route = this.routemap[way];
if (route.quote == name)
return this.definemap[name].rule = route.rule;
}
};
/**
* 路由导航
* @method navigator
* @memberOf xjs/router
* @see module:route#navigator
*/
/**
* 导航到下一个路由地址
* @method navigator
* @memberOf module:route
* @param {String}[hash] 下一个路由的Hash地址,不填写则默认引导到#home
* @param {Object}[state] 路由的缓存内容,将会存储到history.state对象里
* @param {Boolean}[replaceHash] 当设置为true时会提换历史记录里最后一条路由信息,当设置为false时则会以新增的方式插入历史纪录
* @example
* var router = new Router();
* //默认情况,引导到#home并在历史记录里插入新的记录
* router.navigator('#home');
* //追加路由缓存信息
* router.navigator('#home', {isHomePage: true});
* console.log(history.state.isHomePage) // true
* //提换当前路由历史记录
* router.navigator('#home', {}, true);
*/
Router.prototype.navigator = function (hash, state, replaceHash) {
var hash = hash || '#home/',
activeHash = location.hash,
state = state || {},
self = this;
this.checkMatchResult(hash, function (response, result, nexus) {
history[replaceHash ? 'replaceState' : 'pushState'](state, null, hash);
xjs.triggerAnnounceEvent('beforePageChange', activeHash, result);
self.excludeAbandondModules(response, result, nexus, function (renderTeam) {
var crenderTeam = renderTeam.concat();
xjs.addAnnounceEvent('widgetReady', function (e, routeResponseName) {
if (routeResponseName)
crenderTeam.splice(crenderTeam.indexOf(routeResponseName), 1);
if (!crenderTeam.length) {
xjs.removeAnnounceEvent('widgetReady');
response.apply(null, result);
}
});
if (!renderTeam.length)
return xjs.triggerAnnounceEvent('widgetReady');
$.each(crenderTeam, function (i, name) {
setTimeout(function () {
self.definemap[name].Func();
}, 0);
});
});
});
};
/**
* 启动路由监听事件,必须在定义路由引射表以及路由回调后再启动。<br>
* 将会监听路由切换事件,例如浏览器的回退和前进
* @method start
* @memberOf module:route
*/
Router.prototype.start = function () {
var that = this;
function onHashChange(e) {
var param = [];
if (location.hash) {
param.push(location.hash);
if (e && e.isTrusted) {
param.push(null, true);
}
that.navigator.apply(that, param);
} else {
that.navigator('#home/', null, true);
}
}
window.onhashchange = onHashChange;
onHashChange();
};
/**
* 检测hash是否在路由路由映射表内
* @method verify
* @memberOf module:route
* @param hash
* @return {Boolean} 是否匹配到
*/
Router.prototype.verify = function (hash) {
var route, matchResult;
var hash = hash.indexOf('?') > 0 ? hash.slice(0, hash.indexOf('?')) : hash;
for (var obj in this.definemap) {
route = this.definemap[obj];
if (route.rule == undefined)
continue;
matchResult = hash.match(route.rule);
if (matchResult) return {
route: route,
matchResult: matchResult
};
}
return false;
};
/**
* 跳转到登陆模块,完成用户身份验证后再进入hash所匹配的路由
* @method getAuthorization
* @memberOf module:route
* @param hash
*/
Router.prototype.getAuthorization = function (hash) {
this.navigator('#login/', {backHash: hash}, true);
};
/**
* 检查当前路由是否能在路由映射表里找到,如果找不到匹配值就会触发传递给setup里的fail函数
* @method checkMatchResult
* @memberOf module:route
* @param {String} hash
* @param {Function} callback
*/
Router.prototype.checkMatchResult = function (hash, cb) {
var result = this.verify(hash);
if (!result)
return this.callback.fail();
var routeParameters = result.matchResult.slice(1);
var routeResponse = result.route.Func;
var nexus = result.route.nexus;
if (result.route.authorize && !xjs.getUserInfo()) {
this.getAuthorization(hash);
} else {
cb.call(this, routeResponse, routeParameters, nexus);
}
};
/**
* 对当前路由依赖关系以及下一个路由依赖关系做对比,对不存在于路由关系链里的模块会销毁掉[xjs.destroyView]{@link xjs.destroyView}
* @method excludeAbandonModules
* @memberOf module:route
*/
Router.prototype.excludeAbandondModules = function (response, result, newNexus, cb) {
if (!newNexus) {
xjs.destroyView();
this._currentNexus = undefined;
response.apply(null, result);
} else {
if (this._currentNexus == undefined) {
xjs.destroyView();
this._currentNexus = newNexus;
cb(newNexus);
} else {
var remainTeam = [];
var exeTeam = [];
//找出无需更新的组以及需要加载的组
for (var i = 0; i < newNexus.length; i++) {
if (this._currentNexus.indexOf(newNexus[i]) < 0) {
exeTeam.push(this._currentNexus[i]);
} else {
remainTeam.push(this._currentNexus[i]);
}
}
for (var instance in xjs._instances) {
if (remainTeam.indexOf(xjs._instances[instance].routeEventName) < 0)
xjs.destroyView(instance);
}
cb(exeTeam);
}
}
};
return Router;
});