/***
* Created by lzq on 2018/11/02
* Router.js
*/
import $ from "jquery";
import React from 'react';
import PropTypes from 'prop-types';
import CtMobileFactory from "./CtMobile";
import Constant from "./Constant";
import {Consumer} from "./GlobalContext";
/**
* 初始化
* @access private
*/
function initial() {
const self = this;
onHashchange = onHashchange.bind(this);
/***
* 注册hashchange事件
*/
window.addEventListener("hashchange", onHashchange, false);
/***
* 注册页面转场事件
*/
$(window.document).on("pageBeforeChange", function (e, params) {
self.setParameter(params);
});
}
/**
* hashchange的回调函数
* @access private
* @callback
*/
function onHashchange() {
// #page1_134567890232323?id=123_456
const hash = window.location.hash;
hashChange.call(this, hash);
}
/**
* hashChange的处理
* @access private
* @param {string} hash - 哈希值
* @param {Object} option - {
* reload :[true | false]
* }
*/
function hashChange(hash, option) {
const self = this;
/***
* 转场的id(没有重复)
* @type {string}
*/
let id = "";
if (hash) {
id = hash.indexOf("?") !== -1
? hash.substring(1, hash.indexOf("?"))
: hash.substring(1);
const index = id.lastIndexOf("_");
/***
* 用户自定义的锚点跳转
*/
if (index === -1) {
return false;
} else {
const pageId = id.substring(0, index);
/***
* 用户自定义的锚点跳转
*/
if (!window.document.querySelector("[data-ct-data-role='page'],#" + pageId)) {
return false;
}
self.ctmobile.fireEvent(window.document, "pageBeforeChange", [CtMobileFactory.getUrlParam(hash)]);
}
}
/***
* 首页
*/
else {
self.ctmobile.fireEvent(window.document, "pageBeforeChange", [CtMobileFactory.getUrlParam(hash)]);
if (/*如果栈顶元素的pageId == 模板中第一页的id*/
self.ctmobile.getFirstPage().getPageId() === self.ctmobile.getFirstPageId()) {
// 说明是从模板的第一页进入主应用的
id = self.ctmobile.getFirstPage().getId();
}
else {
// 不是从模板的第一页进入主应用的
self.ctmobile.createPage(self.ctmobile.getId(self.ctmobile.getFirstPageId()), (Component) => {
Component.start(Constant._SLIDEDURATION, () => {
self.removeFirstPage();
});
});
return;
}
}
//hash ? (hash.indexOf("?") != -1
// ? id = hash.substring(1, hash.indexOf("?"))
// : id = hash.substring(1))
// : id = root.getFirstPage().getId();
/***
* 新的页面
*/
const curPage = self.getPageById(id);
if (!curPage) {
self.ctmobile.createPage(id, (Component) => {
Component.start(Constant._SLIDEDURATION, function () {
// 如果不改变浏览器历史且历史栈长度大于1
if (option && option.reload && self.getHistoryLength() > 1) {
const preHistoryIndex = self.getHistoryLength() - 2;
const preHistoryPage = self.getPageByIndex(preHistoryIndex);
if (preHistoryPage) {
preHistoryPage.finish(0, null, option);
}
}
});
});
}
/***
* 回退
*/
else {
// 如果刷新的是栈顶的页面
if (id === self.getLastPage().getId()) {
return false;
} else {
// 依次出栈
const index = self.ctmobile.indexOfById(id);
for (let i = self.getHistoryLength() - 1; i > index; i--) {
if (i === index + 1) {
self.getPageByIndex(i).finish(Constant._SLIDEDURATION);
} else {
self.getPageByIndex(i).finish(0);
}
}
}
}
}
/**
* history中是否有pageId开头的page对象
* @access private
* @param {string} pageId
* @return {number}
*/
function indexOfHistoryByPageId(pageId) {
if (pageId.indexOf("?") !== -1) {
pageId = pageId.substring(0, pageId.indexOf("?"));
}
let index = -1;
for (let i = 0, len = this.getHistoryLength(); i < len; i++) {
if (this.getPageByIndex(i).getPageId() === pageId) {
index = i;
break;
}
}
return index;
}
/**
* Link
* @class Link
* @classdesc 用来管理跳转的链接
*/
class Link extends React.Component {
render() {
const {className = '', style = {}} = this.props;
return (
<Consumer>
{(ctmobile) => {
return (
<a
className={className}
style={style}
onClick={() => {
const {pageId = '', parameter = '', reload = ctmobile.config.linkCaptureReload} = this.props;
const href = `${"#" + pageId}?pageId=${pageId}${parameter}`;
ctmobile.router.startPage(href, {
reload,
});
}}>{this.props.children}</a>
);
}}
</Consumer>
);
}
}
/**
* CheckLinkComponentProps
* @param {string} className - className
* @param {Object} style - style
* @param {string} pageId - pageId
* @param {string} parameter - 页面参数
* @param {boolean} reload - 是否替换历史
*/
Link.propTypes = {
className: PropTypes.string,
style: PropTypes.object,
pageId: PropTypes.string,
parameter: PropTypes.string,
reload: PropTypes.bool
};
/**
* Back
* @class Back
* @classdesc 用来管理返回的操作
*/
class Back extends React.Component {
render() {
const {className = '', style = {}} = this.props;
return (
<Consumer>
{(ctmobile) => {
return (
<a
className={className}
style={style}
onClick={() => {
ctmobile.router.go(-1);
}}>{this.props.children}</a>
);
}}
</Consumer>
);
}
}
/**
* CheckBackComponentProps
* @param {string} className - className
* @param {Object} style - style
*/
Back.propTypes = {
className: PropTypes.string,
style: PropTypes.object,
};
export {Link, Back};
/**
* Router
* @class Router
* @classdesc 管理所有路由相关操作
*/
class Router {
/**
* @constructor
* @param {CtMobile} ctmobile
*/
constructor(ctmobile) {
Object.assign(this, {
ctmobile,
parameter: null,
history: [],
});
initial.call(this);
}
/**
* 页面跳转
* @param {string} href (pageId = pageId + params) 如: page1?a=1&b=2;
* @param {object} option {
* reload : [true | false]
* }
*/
startPage(href, option) {
const self = this;
let pageId = "", paramsQuery = "";
// Ajax加载
if (href.indexOf("#") !== 0) {
let params = CtMobileFactory.getUrlParam(href);
pageId = params.pageId;
delete params.pageId;
let paramsQueryArr = [];
for (let p in params) {
if (params.hasOwnProperty(p)) {
paramsQueryArr.push(p + "=" + params[p]);
}
}
paramsQuery = paramsQueryArr.join("&") ? ("?" + paramsQueryArr.join("&")) : "";
dispatcher();
}
// 本地锚点加载
else {
href = href.substring(1);
if (href.indexOf("?") !== -1) {
paramsQuery = href.substring(href.indexOf("?"));
pageId = href.substring(0, href.indexOf("?"));
} else {
pageId = href;
}
dispatcher();
}
/***
* 分派
*/
function dispatcher() {
/***
* 获取page的模式
*/
const mode = self.ctmobile.getPageConfigAttribute(pageId, 'mode');
let hash;
//if(/*单例*/mode === "single" || /*完全单例*/mode === "singleInstance" || /*带返回值的完全单例*/mode === "singleInstanceResult") {
if (mode.toLowerCase().indexOf("single") !== -1) {
const index = indexOfHistoryByPageId.call(self, pageId);
if (/*_history中没有以pageId开头的page*/index === -1) {
//1.用pageId生成真实的id转换锚点
//if("singleInstance" === mode || "singleInstanceResult" === mode) {
if (mode/*.toLowerCase()*/.indexOf("singleInstance") !== -1) {
if (self.ctmobile.getSingleInstance(pageId)) {
hash = self.ctmobile.getSingleInstance(pageId).getId();
} else {
hash = self.ctmobile.getId(pageId + paramsQuery);
}
} else {
hash = self.ctmobile.getId(pageId + paramsQuery);
}
if (option && option.reload) {
window.history.replaceState(null, "", "#" + hash);
hashChange.call(self, "#" + hash, option);
} else {
window.location.hash = "#" + hash;
}
} else {
if (/*如果page不是栈顶,依次出栈,调用finish*/
self.ctmobile.getPageByIndex(index) !== self.getLastPage()
) {
// 假如现在_history中的顺序是1,2,3,4,5,6,3是单例,现在要跳转到3,那么6,5,4所代表的page依次调用finish方法
// history.go(找到3相对于当前页的阈值)
self.go(-(self.getHistoryLength() - 1 - index));
} else {
self.ctmobile.fireEvent(self.getLastPage().getPageDOM(), "pageShow");
}
}
} else if (/*多例*/mode === "standard" || mode === "result") {
//1.用pageId生成真实的id转换锚点
hash = self.ctmobile.getId(pageId + paramsQuery);
if (option && option.reload) {
window.history.replaceState(null, "", "#" + hash);
hashChange.call(self, "#" + hash, option);
} else {
window.location.hash = "#" + hash;
}
}
}
}
/**
* 跳转到指定的历史
* @param {number} index - 历史位置
* 注释:当前的位置为0 index负值为回退,index正数为前进 都以1开始
* 例如 -1 为当前页之前的页面,1为当前页之后的页面,0为刷新当前页面
*/
go(index) {
window.history.go(index);
}
/**
* 返回
*/
back() {
this.go(-1);
}
/**
* 设置转场参数
* @param {Object} parameter
*/
setParameter(parameter) {
this.parameter = parameter;
}
/**
* 获取转场的参数
* @return {Object}
*/
getParameter() {
let parameter = Object.assign({}, this.parameter);
if(parameter.pageId) {
delete parameter.pageId;
}
return parameter;
}
/**
* 根据ID获取page对象
* @param {string} id
* @return {*}
*/
getPageById(id) {
return this.history[this.ctmobile.indexOfById(id)];
}
/**
* 根据索引获取page对象
* @param {number} index
* @returns {*}
*/
getPageByIndex(index) {
return this.history[index];
}
/**
* 获取历史记录中的栈顶的元素
* @returns {*}
*/
getLastPage() {
return this.history[this.history.length - 1];
}
/**
* 获取历史栈长度
* @return {number}
*/
getHistoryLength() {
return this.history.length;
}
/**
* 添加页面的历史栈
* @param {Page} page
*/
addPage(page) {
this.history.push(page);
}
/**
* 删除历史栈中的第一个page
*/
removeFirstPage() {
this.history.shift();
}
/**
* 删除历史栈中的最后一个页面
*/
removeLastPage() {
this.history.pop();
}
/**
* 删除历史栈中指定的页面
* @param {number} index
*/
removePageByIndex(index) {
this.history.splice(index, 1);
}
}
export default Router;