easy-magix
Version:
view manager framewrok
435 lines (426 loc) • 14.6 kB
JavaScript
var Router_VIEW = 'view';
var Router_HrefCache = new G_Cache();
var Router_ChgdCache = new G_Cache();
var Router_WinLoc = G_WINDOW.location;
var Router_LastChanged;
var Router_Silent = 0;
var Router_LLoc = {
query: {},
params: {},
href: G_EMPTY
};
var Router_TrimHashReg = /(?:^.*\/\/[^\/]+|#.*$)/gi;
var Router_TrimQueryReg = /^[^#]*#?!?/;
var GetParam = function(key, params) {
params = this[G_PARAMS];
return params[key] || G_EMPTY;
};
var Router_Edge;
/*#if(!modules.forceEdgeRouter){#*/
var Router_Hashbang = G_HashKey + '!';
var Router_UpdateHash = function(path, replace) {
path = Router_Hashbang + path;
if (replace) {
Router_WinLoc.replace(path);
} else {
Router_WinLoc.hash = path;
}
};
var Router_Update = function(path, params, loc, replace, silent, lQuery) {
path = G_ToUri(path, params, lQuery);
if (path != loc.srcHash) {
Router_Silent = silent;
Router_UpdateHash(path, replace);
}
};
/*#if(modules.tipRouter){#*/
var Router_Bind = function() {
var lastHash = Router_Parse().srcHash;
var newHash, suspend;
G_DOMEventLibBind(G_WINDOW, 'hashchange', function(e, loc, resolve) {
if (suspend) {
/*#if(modules.tipLockUrlRouter){#*/
Router_UpdateHash(lastHash);
/*#}#*/
return;
}
loc = Router_Parse();
newHash = loc.srcHash;
if (newHash != lastHash) {
resolve = function() {
e.p = 1;
lastHash = newHash;
suspend = G_EMPTY;
Router_UpdateHash(newHash);
Router_Diff();
};
e = {
reject: function() {
e.p = 1;
suspend = G_EMPTY;
/*#if(!modules.tipLockUrlRouter){#*/
Router_UpdateHash(lastHash);
/*#}#*/
},
resolve: resolve,
prevent: function() {
suspend = 1;
/*#if(modules.tipLockUrlRouter){#*/
Router_UpdateHash(lastHash);
/*#}#*/
}
};
Router.fire('change', e);
if (!suspend && !e.p) {
resolve();
}
}
});
G_WINDOW.onbeforeunload = function(e, te, msg) {
e = e || G_WINDOW.event;
te = {};
Router.fire('pageunload', te);
if ((msg = te.msg)) {
if (e) e.returnValue = msg;
return msg;
}
};
Router_Diff();
};
/*#}else{#*/
var Router_Bind = function() {
G_DOMEventLibBind(G_WINDOW, 'hashchange', Router_Diff);
Router_Diff();
};
/*#}#*/
/*#}#*/
/*#if(modules.edgeRouter||modules.forceEdgeRouter){#*/
var WinHistory = G_WINDOW.history;
/*#if(!modules.forceEdgeRouter){#*/
if (WinHistory.pushState) {
/*#}#*/
Router_Edge = 1;
var Router_DidUpdate;
var Router_UpdateState = function(path, replace) {
WinHistory[replace ? 'replaceState' : 'pushState'](G_NULL, G_NULL, path);
};
var Router_Popstate;
var Router_Update = function(path, params, loc, replace, silent) {
path = G_ToUri(path, params);
if (path != loc.srcQuery) {
Router_Silent = silent;
Router_UpdateState(path, replace);
if (Router_Popstate) {
Router_Popstate(1);
} else {
Router_Diff();
}
}
};
/*#if(modules.tipRouter){#*/
var Router_Bind = function() {
var initialURL = Router_WinLoc.href;
var lastHref = initialURL;
var newHref, suspend;
G_DOMEventLibBind(G_WINDOW, 'popstate', Router_Popstate = function(f, e, resolve) {
newHref = Router_WinLoc.href;
var initPop = !Router_DidUpdate && newHref == initialURL;
Router_DidUpdate = 1;
if (initPop) return;
if (suspend) {
/*#if(modules.tipLockUrlRouter){#*/
Router_UpdateState(lastHref);
/*#}#*/
return;
}
if (newHref != lastHref) {
resolve = function() {
e.p = 1;
suspend = G_EMPTY;
lastHref = newHref;
if (!f) Router_UpdateState(newHref);
Router_Diff();
};
e = {
reject: function() {
suspend = G_EMPTY;
e.p = 1;
/*#if(!modules.tipLockUrlRouter){#*/
Router_UpdateState(lastHref);
/*#}#*/
},
resolve: resolve,
prevent: function() {
suspend = 1;
/*#if(modules.tipLockUrlRouter){#*/
Router_UpdateState(lastHref);
/*#}#*/
}
};
Router.fire('change', e);
if (!suspend && !e.p) {
resolve();
}
}
});
G_WINDOW.onbeforeunload = function(e, te, msg) {
e = e || G_WINDOW.event;
te = {};
Router.fire('pageunload', te);
if ((msg = te.msg)) {
if (e) e.returnValue = msg;
return msg;
}
};
Router_Diff();
};
/*#}else{#*/
var Router_Bind = function() {
var initialURL = Router_WinLoc.href;
G_DOMEventLibBind(G_WINDOW, 'popstate', function() {
var initPop = !Router_DidUpdate && Router_WinLoc.href == initialURL;
Router_DidUpdate = 1;
if (initPop) return;
Router_Diff();
});
Router_Diff();
};
/*#}#*/
/*#if(!modules.forceEdgeRouter){#*/
}
/*#}#*/
/*#}#*/
var Router_PNR_Routers, Router_PNR_UnmatchView, /*Router_PNR_IsFun,*/
Router_PNR_DefaultView, Router_PNR_DefaultPath;
/*#if(modules.urlRewriteRouter){#*/
var Router_PNR_Rewrite;
/*#}#*/
/*#if(modules.updateTitleRouter){#*/
var DefaultTitle = G_DOCUMENT.title;
/*#}#*/
var Router_AttachViewAndPath = function(loc, view) {
if (!Router_PNR_Routers) {
Router_PNR_Routers = Magix_Cfg.routes || {};
Router_PNR_UnmatchView = Magix_Cfg.unmatchView;
Router_PNR_DefaultView = Magix_Cfg.defaultView;
Router_PNR_DefaultPath = Magix_Cfg.defaultPath || '/';
//Router_PNR_IsFun = G_IsFunction(Router_PNR_Routers);
//if (!Router_PNR_IsFun && !Router_PNR_Routers[Router_PNR_DefaultPath]) {
// Router_PNR_Routers[Router_PNR_DefaultPath] = Router_PNR_DefaultView;
//}
/*#if(modules.urlRewriteRouter){#*/
Router_PNR_Rewrite = Magix_Cfg.rewrite;
//if (!G_IsFunction(Router_PNR_Rewrite)) {
// Router_PNR_Rewrite = G_NULL;
//}
/*#}#*/
}
if (!loc[Router_VIEW]) {
/*#if(modules.forceEdgeRouter){#*/
var path = loc.query[G_PATH] || Router_PNR_DefaultPath;
/*#}else{#*/
var path = loc.hash[G_PATH] || (Router_Edge && loc.query[G_PATH]) || Router_PNR_DefaultPath;
/*#}#*/
/*#if(modules.urlRewriteRouter){#*/
if (Router_PNR_Rewrite) {
path = Router_PNR_Rewrite(path, loc[G_PARAMS], Router_PNR_Routers);
}
/*#}#*/
//if (Router_PNR_IsFun) {
// view = Router_PNR_Routers.call(Magix_Cfg, path, loc);
//} else {
view = Router_PNR_Routers[path] || Router_PNR_UnmatchView || Router_PNR_DefaultView;
//}
loc[G_PATH] = path;
loc[Router_VIEW] = view;
/*#if(modules.updateTitleRouter){#*/
if (G_IsObject(view)) {
if (DEBUG) {
if (!view.view) {
console.error(path, ' config missing view!', view);
}
}
G_Mix(loc, view);
}
/*#}#*/
}
};
var Router_GetChged = function(oldLocation, newLocation) {
var oKey = oldLocation.href;
var nKey = newLocation.href;
var tKey = oKey + G_SPLITER + nKey;
var result = Router_ChgdCache.get(tKey);
if (!result) {
var hasChanged, from, to, rps;
result = {
//isParam: Router_IsParam,
//location: newLocation,
force: !oKey //是否强制触发的changed,对于首次加载会强制触发一次
};
//result[Router_VIEW] = to;
//result[G_PATH] = to;
result[G_PARAMS] = rps = {};
var oldParams = oldLocation[G_PARAMS],
newParams = newLocation[G_PARAMS];
var tArr = [G_PATH, Router_VIEW].concat(G_Keys(oldParams), G_Keys(newParams)),
idx, key;
for (idx = tArr.length; idx--;) {
key = tArr[idx];
if (idx == 1) {
oldParams = oldLocation;
newParams = newLocation;
rps = result;
}
from = oldParams[key];
to = newParams[key];
if (from != to) {
rps[key] = {
from: from,
to: to
};
hasChanged = 1;
}
}
Router_ChgdCache.set(tKey, result = {
a: hasChanged,
b: result
});
}
return result;
};
var Router_Parse = function(href) {
href = href || Router_WinLoc.href;
var result = Router_HrefCache.get(href),
query, hash, queryObj, hashObj, params;
if (!result) {
query = href.replace(Router_TrimHashReg, G_EMPTY);
hash = href.replace(Router_TrimQueryReg, G_EMPTY);
queryObj = G_ParseUri(query);
hashObj = G_ParseUri(hash);
params = G_Mix({}, queryObj[G_PARAMS]);
/*#if(!modules.forceEdgeRouter){#*/
G_Mix(params, hashObj[G_PARAMS]);
/*#}#*/
result = {
get: GetParam,
href: href,
srcQuery: query,
srcHash: hash,
query: queryObj,
hash: hashObj,
params: params
};
Router_AttachViewAndPath(result);
Router_HrefCache.set(href, result);
}
if (DEBUG) {
result = Safeguard(result);
}
return result;
};
var Router_Diff = function() {
var location = Router_Parse();
var changed = Router_GetChged(Router_LLoc, Router_LLoc = location);
if (!Router_Silent && changed.a) {
/*#if(modules.updateTitleRouter){#*/
Router_LastChanged = changed.b;
if (Router_LastChanged.path) {
G_DOCUMENT.title = location.title || DefaultTitle;
}
/*#}#*/
Router.fire('changed', /*#if(modules.updateTitleRouter){#*/ Router_LastChanged /*#}else{#*/ Router_LastChanged = changed.b /*#}#*/ );
}
Router_Silent = 0;
if (DEBUG) {
Router_LastChanged = Safeguard(Router_LastChanged);
}
return Router_LastChanged;
};
//var PathTrimFileParamsReg=/(\/)?[^\/]*[=#]$/;//).replace(,'$1').replace(,EMPTY);
//var PathTrimSearch=/\?.*$/;
/**
* 路由对象,操作URL
* @name Router
* @namespace
* @borrows Event.on as on
* @borrows Event.fire as fire
* @borrows Event.off as off
* @beta
* @module router
*/
var Router = G_Mix({
/**
* @lends Router
*/
/**
* 解析href的query和hash,默认href为location.href
* @param {String} [href] href
* @return {Object} 解析的对象
*/
parse: Router_Parse,
/**
* 根据location.href路由并派发相应的事件,同时返回当前href与上一个href差异对象
* @example
* var diff = Magix.Router.diff();
* if(diff.params.page || diff.params.rows){
* console.log('page or rows changed');
* }
*/
diff: Router_Diff,
/**
* 导航到新的地址
* @param {Object|String} pn path或参数字符串或参数对象
* @param {String|Object} [params] 参数对象
* @param {Boolean} [replace] 是否替换当前历史记录
* @example
* var R = Magix.Router;
* R.to('/list?page=2&rows=20');//改变path和相关的参数,地址栏上的其它参数会进行丢弃,不会保留
* R.to('page=2&rows=20');//只修改参数,地址栏上的其它参数会保留
* R.to({//通过对象修改参数,地址栏上的其它参数会保留
* page:2,
* rows:20
* });
* R.to('/list',{//改变path和相关参数,丢弃地址栏上原有的其它参数
* page:2,
* rows:20
* });
*
* //凡是带path的修改地址栏,都会把原来地址栏中的参数丢弃
*/
to: function(pn, params, replace, silent) {
if (!params && G_IsObject(pn)) {
params = pn;
pn = G_EMPTY;
}
var temp = G_ParseUri(pn);
var tParams = temp[G_PARAMS];
var tPath = temp[G_PATH];
var lPath = Router_LLoc[G_PATH]; //历史路径
var lParams = Router_LLoc[G_PARAMS];
var lQuery = Router_LLoc.query[G_PARAMS];
G_Mix(tParams, params); //把路径中解析出来的参数与用户传递的参数进行合并
if (tPath) { //设置路径带参数的形式,如:/abc?q=b&c=e或不带参数 /abc
//tPath = G_Path(lPath, tPath);
if (!Router_Edge) { //pushState不用处理
for (lPath in lQuery) { //未出现在query中的参数设置为空
if (!G_Has(tParams, lPath)) tParams[lPath] = G_EMPTY;
}
}
} else if (lParams) { //只有参数,如:a=b&c=d
tPath = lPath; //使用历史路径
tParams = G_Mix(G_Mix({}, lParams), tParams); //复制原来的参数,合并新的参数
}
Router_Update(tPath, tParams, Router_LLoc, replace, silent, lQuery);
}
/**
* 当location.href有改变化后触发
* @name Router.changed
* @event
* @param {Object} e 事件对象
* @param {Object} e.path 如果path发生改变时,记录从(from)什么值变成(to)什么值的对象
* @param {Object} e.view 如果view发生改变时,记录从(from)什么值变成(to)什么值的对象
* @param {Object} e.params 如果参数发生改变时,记录从(from)什么值变成(to)什么值的对象
* @param {Boolean} e.force 标识是否是第一次强制触发的changed,对于首次加载完Magix,会强制触发一次changed
*/
}, Event);
Magix.Router = Router;