You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

703 lines
20 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/**
* mui.init 5+
* @param {type} $
* @returns {undefined}
*/
(function($) {
var defaultOptions = {
swipeBack: false,
preloadPages: [], //5+ lazyLoad webview
preloadLimit: 10, //预加载窗口的数量限制(一旦超出,先进先出)
keyEventBind: {
backbutton: true,
menubutton: true
},
titleConfig: {
height: "44px",
backgroundColor: "#f7f7f7", //导航栏背景色
bottomBorderColor: "#cccccc", //底部边线颜色
title: { //标题配置
text: "", //标题文字
position: {
top: 0,
left: 0,
width: "100%",
height: "100%"
},
styles: {
color: "#000000",
align: "center",
family: "'Helvetica Neue',Helvetica,sans-serif",
size: "17px",
style: "normal",
weight: "normal",
fontSrc: ""
}
},
back: {
image: {
base64Data: '',
imgSrc: '',
sprite: {
top: '0px',
left: '0px',
width: '100%',
height: '100%'
},
position: {
top: "10px",
left: "10px",
width: "24px",
height: "24px"
}
}
}
}
};
//默认页面动画
var defaultShow = {
event:"titleUpdate",
autoShow: true,
duration: 300,
aniShow: 'slide-in-right',
extras:{}
};
//若执行了显示动画初始化操作,则要覆盖默认配置
if($.options.show) {
defaultShow = $.extend(true, defaultShow, $.options.show);
}
$.currentWebview = null;
$.extend(true, $.global, defaultOptions);
$.extend(true, $.options, defaultOptions);
/**
* 等待动画配置
* @param {type} options
* @returns {Object}
*/
$.waitingOptions = function(options) {
return $.extend(true, {}, {
autoShow: true,
title: '',
modal: false
}, options);
};
/**
* 窗口显示配置
* @param {type} options
* @returns {Object}
*/
$.showOptions = function(options) {
return $.extend(true, {}, defaultShow, options);
};
/**
* 窗口默认配置
* @param {type} options
* @returns {Object}
*/
$.windowOptions = function(options) {
return $.extend({
scalable: false,
bounce: "" //vertical
}, options);
};
/**
* plusReady
* @param {type} callback
* @returns {_L6.$}
*/
$.plusReady = function(callback) {
if(window.plus) {
setTimeout(function() { //解决callback与plusready事件的执行时机问题(典型案例:showWaiting,closeWaiting)
callback();
}, 0);
} else {
document.addEventListener("plusready", function() {
callback();
}, false);
}
return this;
};
/**
* 5+ event(5+没提供之前我自己实现)
* @param {type} webview
* @param {type} eventType
* @param {type} data
* @returns {undefined}
*/
$.fire = function(webview, eventType, data) {
if(webview) {
if(typeof data === 'undefined') {
data = '';
} else if(typeof data === 'boolean' || typeof data === 'number') {
webview.evalJS("typeof mui!=='undefined'&&mui.receive('" + eventType + "'," + data + ")");
return;
} else if($.isPlainObject(data) || $.isArray(data)) {
data = JSON.stringify(data || {}).replace(/\'/g, "\\u0027").replace(/\\/g, "\\u005c");
}
webview.evalJS("typeof mui!=='undefined'&&mui.receive('" + eventType + "','" + data + "')");
}
};
/**
* 5+ event(5+没提供之前我自己实现)
* @param {type} eventType
* @param {type} data
* @returns {undefined}
*/
$.receive = function(eventType, data) {
if(eventType) {
try {
if(data && typeof data === 'string') {
data = JSON.parse(data);
}
} catch(e) {}
$.trigger(document, eventType, data);
}
};
var triggerPreload = function(webview) {
if(!webview.preloaded) { //保证仅触发一次
$.fire(webview, 'preload');
var list = webview.children();
for(var i = 0; i < list.length; i++) {
$.fire(list[i], 'preload');
}
webview.preloaded = true;
}
};
var trigger = function(webview, eventType, timeChecked) {
if(timeChecked) {
if(!webview[eventType + 'ed']) {
$.fire(webview, eventType);
var list = webview.children();
for(var i = 0; i < list.length; i++) {
$.fire(list[i], eventType);
}
webview[eventType + 'ed'] = true;
}
} else {
$.fire(webview, eventType);
var list = webview.children();
for(var i = 0; i < list.length; i++) {
$.fire(list[i], eventType);
}
}
};
/**
* 打开新窗口
* @param {string} url 要打开的页面地址
* @param {string} id 指定页面ID
* @param {object} options 可选:参数,等待,窗口,显示配置{params:{},waiting:{},styles:{},show:{}}
*/
$.openWindow = function(url, id, options) {
if(typeof url === 'object') {
options = url;
url = options.url;
id = options.id || url;
} else {
if(typeof id === 'object') {
options = id;
id = options.id || url;
} else {
id = id || url;
}
}
if(!$.os.plus) {
//TODO 先临时这么处理手机上顶层跳PC上parent跳
if($.os.ios || $.os.android) {
window.top.location.href = url;
} else {
window.parent.location.href = url;
}
return;
}
if(!window.plus) {
return;
}
options = options || {};
var params = options.params || {};
var webview = null,
webviewCache = null,
nShow, nWaiting;
if($.webviews[id]) {
webviewCache = $.webviews[id];
//webview真实存在才能获取
if(plus.webview.getWebviewById(id)) {
webview = webviewCache.webview;
}
} else if(options.createNew !== true) {
webview = plus.webview.getWebviewById(id);
}
if(webview) { //已缓存
//每次show都需要传递动画参数
//预加载的动画参数优先级openWindow配置>preloadPages配置>mui默认配置
nShow = webviewCache ? webviewCache.show : defaultShow;
nShow = options.show ? $.extend(nShow, options.show) : nShow;
nShow.autoShow && webview.show(nShow.aniShow, nShow.duration, function() {
triggerPreload(webview);
trigger(webview, 'pagebeforeshow', false);
});
if(webviewCache) {
webviewCache.afterShowMethodName && webview.evalJS(webviewCache.afterShowMethodName + '(\'' + JSON.stringify(params) + '\')');
}
return webview;
} else { //新窗口
if(!url) {
throw new Error('webview[' + id + '] does not exist');
}
//显示waiting
var waitingConfig = $.waitingOptions(options.waiting);
if(waitingConfig.autoShow) {
nWaiting = plus.nativeUI.showWaiting(waitingConfig.title, waitingConfig.options);
}
//创建页面
options = $.extend(options, {
id: id,
url: url
});
webview = $.createWindow(options);
//显示
nShow = $.showOptions(options.show);
if(nShow.autoShow) {
var showWebview = function() {
//关闭等待框
if(nWaiting) {
nWaiting.close();
}
//显示页面
webview.show(nShow.aniShow, nShow.duration, function() {},nShow.extras);
options.afterShowMethodName && webview.evalJS(options.afterShowMethodName + '(\'' + JSON.stringify(params) + '\')');
};
//titleUpdate触发时机早于loaded更换为titleUpdate后可以更早的显示webview
webview.addEventListener(nShow.event, showWebview, false);
//loaded事件发生后触发预加载和pagebeforeshow事件
webview.addEventListener("loaded", function() {
triggerPreload(webview);
trigger(webview, 'pagebeforeshow', false);
}, false);
}
}
return webview;
};
$.openWindowWithTitle = function(options, titleConfig) {
options = options || {};
var url = options.url;
var id = options.id || url;
if(!$.os.plus) {
//TODO 先临时这么处理手机上顶层跳PC上parent跳
if($.os.ios || $.os.android) {
window.top.location.href = url;
} else {
window.parent.location.href = url;
}
return;
}
if(!window.plus) {
return;
}
var params = options.params || {};
var webview = null,
webviewCache = null,
nShow, nWaiting;
if($.webviews[id]) {
webviewCache = $.webviews[id];
//webview真实存在才能获取
if(plus.webview.getWebviewById(id)) {
webview = webviewCache.webview;
}
} else if(options.createNew !== true) {
webview = plus.webview.getWebviewById(id);
}
if(webview) { //已缓存
//每次show都需要传递动画参数
//预加载的动画参数优先级openWindow配置>preloadPages配置>mui默认配置
nShow = webviewCache ? webviewCache.show : defaultShow;
nShow = options.show ? $.extend(nShow, options.show) : nShow;
nShow.autoShow && webview.show(nShow.aniShow, nShow.duration, function() {
triggerPreload(webview);
trigger(webview, 'pagebeforeshow', false);
});
if(webviewCache) {
webviewCache.afterShowMethodName && webview.evalJS(webviewCache.afterShowMethodName + '(\'' + JSON.stringify(params) + '\')');
}
return webview;
} else { //新窗口
if(!url) {
throw new Error('webview[' + id + '] does not exist');
}
//显示waiting
var waitingConfig = $.waitingOptions(options.waiting);
if(waitingConfig.autoShow) {
nWaiting = plus.nativeUI.showWaiting(waitingConfig.title, waitingConfig.options);
}
//创建页面
options = $.extend(options, {
id: id,
url: url
});
webview = $.createWindow(options);
if(titleConfig) { //处理原生头
$.extend(true, $.options.titleConfig, titleConfig);
var tid = $.options.titleConfig.id ? $.options.titleConfig.id : id + "_title";
var view = new plus.nativeObj.View(tid, {
top: 0,
height: $.options.titleConfig.height,
width: "100%",
dock: "top",
position: "dock"
});
view.drawRect($.options.titleConfig.backgroundColor); //绘制背景色
var _b = parseInt($.options.titleConfig.height) - 1;
view.drawRect($.options.titleConfig.bottomBorderColor, {
top: _b + "px",
left: "0px"
}); //绘制底部边线
//绘制文字
if($.options.titleConfig.title.text){
var _title = $.options.titleConfig.title;
view.drawText(_title.text,_title.position , _title.styles);
}
//返回图标绘制
var _back = $.options.titleConfig.back;
var backClick = null;
//优先字体
//其次是图片
var _backImage = _back.image;
if(_backImage.base64Data || _backImage.imgSrc) {
//TODO 此处需要处理百分比的情况
backClick = {
left:parseInt(_backImage.position.left),
right:parseInt(_backImage.position.left) + parseInt(_backImage.position.width)
};
var bitmap = new plus.nativeObj.Bitmap(id + "_back");
if(_backImage.base64Data) { //优先base64编码字符串
bitmap.loadBase64Data(_backImage.base64Data);
} else { //其次加载图片文件
bitmap.load(_backImage.imgSrc);
}
view.drawBitmap(bitmap,_backImage.sprite , _backImage.position);
}
//处理点击事件
view.setTouchEventRect({
top: "0px",
left: "0px",
width: "100%",
height: "100%"
});
view.interceptTouchEvent(true);
view.addEventListener("click", function(e) {
var x = e.clientX;
//返回按钮点击
if(backClick&& x > backClick.left && x < backClick.right){
if( _back.click && $.isFunction(_back.click)){
_back.click();
}else{
webview.evalJS("window.mui&&mui.back();");
}
}
}, false);
webview.append(view);
}
//显示
nShow = $.showOptions(options.show);
if(nShow.autoShow) {
//titleUpdate触发时机早于loaded更换为titleUpdate后可以更早的显示webview
webview.addEventListener(nShow.event, function () {
//关闭等待框
if(nWaiting) {
nWaiting.close();
}
//显示页面
webview.show(nShow.aniShow, nShow.duration, function() {},nShow.extras);
}, false);
}
}
return webview;
};
/**
* 根据配置信息创建一个webview
* @param {type} options
* @param {type} isCreate
* @returns {webview}
*/
$.createWindow = function(options, isCreate) {
if(!window.plus) {
return;
}
var id = options.id || options.url;
var webview;
if(options.preload) {
if($.webviews[id] && $.webviews[id].webview.getURL()) { //已经cache
webview = $.webviews[id].webview;
} else { //新增预加载窗口
//判断是否携带createNew参数默认为false
if(options.createNew !== true) {
webview = plus.webview.getWebviewById(id);
}
//之前没有,那就新创建
if(!webview) {
webview = plus.webview.create(options.url, id, $.windowOptions(options.styles), $.extend({
preload: true
}, options.extras));
if(options.subpages) {
$.each(options.subpages, function(index, subpage) {
var subpageId = subpage.id || subpage.url;
if(subpageId) { //过滤空对象
var subWebview = plus.webview.getWebviewById(subpageId);
if(!subWebview) { //如果该webview不存在则创建
subWebview = plus.webview.create(subpage.url, subpageId, $.windowOptions(subpage.styles), $.extend({
preload: true
}, subpage.extras));
}
webview.append(subWebview);
}
});
}
}
}
//TODO 理论上子webview也应该计算到预加载队列中但这样就麻烦了要退必须退整体否则可能出现问题
$.webviews[id] = {
webview: webview, //目前仅preload的缓存webview
preload: true,
show: $.showOptions(options.show),
afterShowMethodName: options.afterShowMethodName //就不应该用evalJS。应该是通过事件消息通讯
};
//索引该预加载窗口
var preloads = $.data.preloads;
var index = preloads.indexOf(id);
if(~index) { //删除已存在的(变相调整插入位置)
preloads.splice(index, 1);
}
preloads.push(id);
if(preloads.length > $.options.preloadLimit) {
//先进先出
var first = $.data.preloads.shift();
var webviewCache = $.webviews[first];
if(webviewCache && webviewCache.webview) {
//需要将自己打开的所有页面全部close
//关闭该预加载webview
$.closeAll(webviewCache.webview);
}
//删除缓存
delete $.webviews[first];
}
} else {
if(isCreate !== false) { //直接创建非预加载窗口
webview = plus.webview.create(options.url, id, $.windowOptions(options.styles), options.extras);
if(options.subpages) {
$.each(options.subpages, function(index, subpage) {
var subpageId = subpage.id || subpage.url;
var subWebview = plus.webview.getWebviewById(subpageId);
if(!subWebview) {
subWebview = plus.webview.create(subpage.url, subpageId, $.windowOptions(subpage.styles), subpage.extras);
}
webview.append(subWebview);
});
}
}
}
return webview;
};
/**
* 预加载
*/
$.preload = function(options) {
//调用预加载函数不管是否传递preload参数强制变为true
if(!options.preload) {
options.preload = true;
}
return $.createWindow(options);
};
/**
*关闭当前webview打开的所有webview
*/
$.closeOpened = function(webview) {
var opened = webview.opened();
if(opened) {
for(var i = 0, len = opened.length; i < len; i++) {
var openedWebview = opened[i];
var open_open = openedWebview.opened();
if(open_open && open_open.length > 0) {
//关闭打开的webview
$.closeOpened(openedWebview);
//关闭自己
openedWebview.close("none");
} else {
//如果直接孩子节点,就不用关闭了,因为父关闭的时候,会自动关闭子;
if(openedWebview.parent() !== webview) {
openedWebview.close('none');
}
}
}
}
};
$.closeAll = function(webview, aniShow) {
$.closeOpened(webview);
if(aniShow) {
webview.close(aniShow);
} else {
webview.close();
}
};
/**
* 批量创建webview
* @param {type} options
* @returns {undefined}
*/
$.createWindows = function(options) {
$.each(options, function(index, option) {
//初始化预加载窗口(创建)和非预加载窗口(仅配置,不创建)
$.createWindow(option, false);
});
};
/**
* 创建当前页面的子webview
* @param {type} options
* @returns {webview}
*/
$.appendWebview = function(options) {
if(!window.plus) {
return;
}
var id = options.id || options.url;
var webview;
if(!$.webviews[id]) { //保证执行一遍
//TODO 这里也有隐患比如某个webview不是作为subpage创建的而是作为target webview的话
if(!plus.webview.getWebviewById(id)) {
webview = plus.webview.create(options.url, id, options.styles, options.extras);
}
//之前的实现方案子窗口loaded之后再append到父窗口中
//问题部分子窗口loaded事件发生较晚此时执行父窗口的children方法会返回空导致父子通讯失败
// 比如父页面执行完preload事件后需触发子页面的preload事件此时未append的话就无法触发
//修改方式不再监控loaded事件直接append
//by chb@20150521
// webview.addEventListener('loaded', function() {
plus.webview.currentWebview().append(webview);
// });
$.webviews[id] = options;
}
return webview;
};
//全局webviews
$.webviews = {};
//预加载窗口索引
$.data.preloads = [];
//$.currentWebview
$.plusReady(function() {
$.currentWebview = plus.webview.currentWebview();
});
$.addInit({
name: '5+',
index: 100,
handle: function() {
var options = $.options;
var subpages = options.subpages || [];
if($.os.plus) {
$.plusReady(function() {
//TODO 这里需要判断一下最好等子窗口加载完毕后再调用主窗口的show方法
//或者在openwindow方法中监听实现
$.each(subpages, function(index, subpage) {
$.appendWebview(subpage);
});
//判断是否首页
if(plus.webview.currentWebview() === plus.webview.getWebviewById(plus.runtime.appid)) {
//首页需要自己激活预加载;
//timeout因为子页面loaded之后才append的防止子页面尚未append、从而导致其preload未触发的问题
setTimeout(function() {
triggerPreload(plus.webview.currentWebview());
}, 300);
}
//设置ios顶部状态栏颜色
if($.os.ios && $.options.statusBarBackground) {
plus.navigator.setStatusBarBackground($.options.statusBarBackground);
}
if($.os.android && parseFloat($.os.version) < 4.4) {
//解决Android平台4.4版本以下resume后父窗体标题延迟渲染的问题
if(plus.webview.currentWebview().parent() == null) {
document.addEventListener("resume", function() {
var body = document.body;
body.style.display = 'none';
setTimeout(function() {
body.style.display = '';
}, 10);
});
}
}
});
} else {
//已支持iframe嵌入
// if (subpages.length > 0) {
// var err = document.createElement('div');
// err.className = 'mui-error';
// //文字描述
// var span = document.createElement('span');
// span.innerHTML = '在该浏览器下,不支持创建子页面,具体参考';
// err.appendChild(span);
// var a = document.createElement('a');
// a.innerHTML = '"mui框架适用场景"';
// a.href = 'http://ask.dcloud.net.cn/article/113';
// err.appendChild(a);
// document.body.appendChild(err);
// console.log('在该浏览器下,不支持创建子页面');
// }
}
}
});
window.addEventListener('preload', function() {
//处理预加载部分
var webviews = $.options.preloadPages || [];
$.plusReady(function() {
$.each(webviews, function(index, webview) {
$.createWindow($.extend(webview, {
preload: true
}));
});
});
});
$.supportStatusbarOffset = function() {
return $.os.plus && $.os.ios && parseFloat($.os.version) >= 7;
};
$.ready(function() {
//标识当前环境支持statusbar
if($.supportStatusbarOffset()) {
document.body.classList.add($.className('statusbar'));
}
});
})(mui);