/** * 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);