UNPKG

miniapp-windmill-renderer

Version:

Windmill renderer-side proxy.

1,368 lines (1,304 loc) 39.3 kB
/** * windmill.renderer.js v0.5.1. */ ;console.log('Start windmill renderer (0.5.1) framework. Build at 2019-02-21 10:54'); (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define(factory) : (global['windmill-renderer'] = factory()); }(this, (function () { 'use strict'; // polyfill for Object.assign if (typeof Object.assign != 'function') { Object.defineProperty(Object, 'assign', { writable: true, enumerable: false, configurable: true, value: function objectAssign(target, varArgs) { var arguments$1 = arguments; if (target == null) { throw new TypeError('Cannot convert undefined or null to object'); } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var next = arguments$1[index]; if (next != null) { for (var key in next) { if (Object.prototype.hasOwnProperty.call(next, key)) { to[key] = next[key]; } } } } return to; } }); } /** * Generate uniqueId */ var uniqueId = (function () { var start = 100000; return function getUniqueId() { var id = String(start); start += 1; return id; }; })(); /** * Get current runtime context object */ function getContextObject() { return typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : (new Function('return this'))(); // tslint:disable-line } /** * Set a global API to current context */ function setGlobalAPI(name, value, readonly) { if ( readonly === void 0 ) readonly = true; var context = getContextObject(); try { Object.defineProperty(context, name, { value: value, configurable: true, enumerable: true, writable: !readonly }); } catch (e) { context[name] = value; } } /** * Pick specific methods from a target object */ function pickMethods(object, methods) { var exportObject = {}; methods.forEach(function (method) { if (typeof object[method] === 'function') { exportObject[method] = function () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; return object[method].apply(object, args); }; } }); return exportObject; } function getType(param) { return Object.prototype.toString.call(param).slice(8, -1); } function isPlainObject(param) { return getType(param) === 'Object'; } function deepClone(source) { var result = {}; try { result = JSON.parse(JSON.stringify(source)); } catch (e) { } return result; } function noop() { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; } /** * Logger and Debugger methods */ // real logger of current context, could be override by host context var logger = function () { }; var debuggerEnv = {}; var LOG_LEVELS = ['debug', 'info', 'log', 'warn', 'error']; // override default logger function setLoggerEnv(env) { if (!env || !env.platform) { return console.error('set logger environment failed.'); } Object.assign(debuggerEnv, env); } function isDebugMode() { return debuggerEnv.debugMode === true || debuggerEnv.debugMode === 'true'; // compatible for android } function isForTest() { return typeof process === 'object' && process && process.env && process.env.PURPOSE === 'test'; } /** * Decide wether to print logs for the specified level. */ function shouldPrint(level) { // intercept logger function in test mode if (isForTest()) { if (typeof __test_logger__ === 'function') { logger = __test_logger__; return true; } return false; } // always print log in debugMode if (isDebugMode()) { return true; } // check the log level var defaultLevel = debuggerEnv.logLevel || ('debug'); return LOG_LEVELS.indexOf(level) >= LOG_LEVELS.indexOf(defaultLevel); } var capitalize = function (str) { return str.charAt(0).toUpperCase() + str.slice(1); }; /** * Default logger function, be compatible with different platforms. */ function defaultLogger(level, log) { var role = '[wml-r]'; var str = "[" + (capitalize(level)) + "]" + role + " " + log; { var WEEX_PLATFORMS = ['Android', 'iOS']; if (debuggerEnv.isWeex && WEEX_PLATFORMS.indexOf(debuggerEnv.platform) > -1) { return console.log(str); } else { return console.error(str); } } } logger = defaultLogger; function debug(output) { if (shouldPrint('debug')) { logger('debug', output); } } function warn(output) { if (shouldPrint('warn')) { logger('warn', output); } } function error(output) { if (shouldPrint('error')) { logger('error', output); } } function throwError(errMsg) { error(errMsg); if ("development" !== 'production' && !isForTest()) { throw new Error(errMsg); } } /** * Get the source code of a function. * May not always work well, it should only be used in debug logs. */ function getCode(fn) { if (typeof fn === 'function') { return Function.prototype.toString.call(fn); } return ''; } var network = [ "request", "uploadFile", "downloadFile" ]; var miniApp = [ "setAppShareInfo", "hideLauncherLoading", "setWebViewTop", "setWebViewBackgroundImage", "getConfig", "reLaunch" ]; var mtop = [ "request" ]; var storage = [ "length", "setItem", "getItem", "removeItem", "clearStorage", "getStorageInfo", { name: "getItemSync", sync: true }, { name: "setItemSync", sync: true }, { name: "removeItemSync", sync: true }, { name: "clearStorageSync", sync: true }, { name: "getStorageInfoSync", sync: true } ]; var memoryStorage = [ "setItem", "getItem" ]; var tabBar = [ "show", "hide", "setTabBarItem", "setTabBarStyle", "setTabBarBadge", "removeTabBarBadge", "showTabBarRedDot", "hideTabBarRedDot" ]; var navigator = [ "push", "pop", "openMessagePage", "reloadPage", "getBackStack", "popToHome", "navigateTo", "redirectTo", "switchTab", "navigateBackMiniProgram", "navigateToMiniProgram" ]; var user = [ "login", "logout", "info" ]; var modal = [ "alert", "confirm", "toast", "hideToast", "showLoading", "hideLoading", "prompt" ]; var navigatorBar = [ "show", "hide", "setTitle", "setRightItem", "setStyle", "setDrawer", "setActionSheet", "hasIndexBadge", "scaleIndexBadge", "resetIndexBadge", "getHeight", "openDrawer", "isDrawerOpened", "closeDrawer", "setLeftItem", "showMenu", "hideMenu", "getStatusBarHeight", "setNavigationBar", "hideNavigationBarLoading", "showNavigationBarLoading" ]; var clipboard = [ "writeText", "readText" ]; var picker = [ "pick", "pickDate", "pickTime", "chooseCity" ]; var webSocket = [ "webSocket", "send", "close", "onOpen", "onMessage", "onClose", "onError" ]; var calendar = [ "addEvent", "checkEvent", "removeEvent" ]; var connection = [ "getType", "getDownlinkMax", "onChange" ]; var screen = [ "setAlwaysOn", "setBrightness", "getScreenBrightness" ]; var broadcast = [ "createChannel", "postMessage", "onMessage", "close" ]; var share = [ "doShare" ]; var cookie = [ "read", "write", "getAllObjects" ]; var audio = [ "load", "play", "pause", "stop", "setVolume", "canPlayType" ]; var wopc = [ "getSessionKey", "setSessionKey", "authorize", "getSetting", "openSetting" ]; var WopcMtopPlugin = [ "request" ]; var device = [ "onShake", "vibrate", "vibrateShort", "onUserCaptureScreen", "offUserCaptureScreen", "scan" ]; var keyboard = [ "hideKeyboard" ]; var contact = [ "choosePhoneContact" ]; var location = [ "getLocation" ]; var alipay = [ "tradePay" ]; var userTrack = [ "commit", "commitut", "commitEvent", "customAdvance", "pageAppear", "pageDisappear", "skipPage", "updatePageUtparam", "updateNextPageUtparam" ]; var aluWVJSBridge = [ "userIsLogin", "refreshAlipayCookie" ]; var TBDeviceInfo = [ "getUtdid" ]; var actionSheet = [ "showActionSheet" ]; var image = [ "chooseImage", "previewImage", "compressImage", "getImageInfo", "saveImage" ]; var media = [ "startVoice", "stopVoice" ]; var audioRecord = [ "startRecord", "stopRecord", "cancelRecord" ]; var audioPlayer = [ "playVoice", "pauseVoice", "stopVoice" ]; var file = [ "saveFile", "getFileInfo", "getFileList", "removeFile" ]; var video = [ "chooseVideo", "getVideoInfo", "saveVideoToPhotosAlbum" ]; var phone = [ "makePhoneCall" ]; var compass = [ "startCompass", "stopCompass", "onCompassChange" ]; var tbsecurity = [ "secureToken" ]; var sendMtop = [ "request" ]; var ZCache = [ "prefetch", "fetch" ]; var WVZCache = [ "prefetch", "fetch" ]; var sku = [ "show", "hide" ]; var chat = [ "open" ]; var ucc = [ "uccBind", "uccTrustLogin", "uccUnbind" ]; var address = [ "choose" ]; var taopai = [ "openTaopaiShoot", "openTaopaiEdit" ]; var poi = [ "openPOI" ]; var availableModules$1 = { network: network, miniApp: miniApp, mtop: mtop, storage: storage, memoryStorage: memoryStorage, tabBar: tabBar, navigator: navigator, user: user, modal: modal, navigatorBar: navigatorBar, clipboard: clipboard, picker: picker, webSocket: webSocket, calendar: calendar, connection: connection, screen: screen, broadcast: broadcast, share: share, cookie: cookie, audio: audio, wopc: wopc, WopcMtopPlugin: WopcMtopPlugin, device: device, keyboard: keyboard, contact: contact, location: location, alipay: alipay, userTrack: userTrack, "nuvajs-exec": [ "exec" ], aluWVJSBridge: aluWVJSBridge, TBDeviceInfo: TBDeviceInfo, actionSheet: actionSheet, image: image, media: media, audioRecord: audioRecord, audioPlayer: audioPlayer, file: file, video: video, phone: phone, compass: compass, tbsecurity: tbsecurity, sendMtop: sendMtop, ZCache: ZCache, WVZCache: WVZCache, sku: sku, chat: chat, ucc: ucc, address: address, "private": [ "addProperties" ], taopai: taopai, poi: poi }; /** * Available modules of windmill container. * * It can be used by DSL framework to decide what apis should * a module expose and generate the module proxy object. */ // using the same source file with native SDK /** * Register modules to worker context. */ function registerModules(modules, noWarning) { if ( noWarning === void 0 ) noWarning = false; if (!isPlainObject(modules)) { error('Invalid parameter type! ' + 'The module definition is not a plain object.'); return; } var loop = function ( moduleName ) { var registeredModule = modules[moduleName]; if (availableModules$1[moduleName]) { noWarning || warn("The \"" + moduleName + "\" module has already been registered." + "It would be overridden."); } else { availableModules$1[moduleName] = []; } var moduleDefine = availableModules$1[moduleName]; if (Array.isArray(registeredModule)) { registeredModule.forEach(function (methodDefine) { if (isPlainObject(methodDefine)) { // TODO: omit duplicated when methodDefine is plain object moduleDefine.push(methodDefine); } else if (typeof methodDefine === 'string') { var methodName = methodDefine; if (moduleDefine.indexOf(methodName) === -1) { moduleDefine.push(methodName); } else if (!noWarning) { warn("The \"" + methodName + "\" of the \"" + moduleName + "\" module " + "has already been registered."); } } }); } else { error("Invalid parameter type! The method list of the " + "\"" + moduleName + "\" module should be an Array."); } }; for (var moduleName in modules) loop( moduleName ); } /** * Create a module getter which depends on the worker runtime api */ function createModuleGetter(getRuntime, moduleInterceptor // intercept a module call ) { // cached module proxies var moduleInvoker = moduleInterceptor || function () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; return (ref = getRuntime()).$call.apply(ref, args); var ref; }; // universal module invoker for sync module var syncModuleInvoker = moduleInterceptor || function () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; return (ref = getRuntime()).$callSync.apply(ref, args); var ref; }; /** * Require a specific module. * If the module name is not given, it'll return a universal module api. */ return function requireModule(moduleName) { // return a universal module call api if (!moduleName) { debug("Require a universal module proxy."); return { call: moduleInvoker }; } // warn about unknown module var moduleDefine = availableModules$1[moduleName]; if (!Array.isArray(moduleDefine)) { warn("Require unknown module \"" + moduleName + "\"." + "It may not exist or haven't been registered."); return; } debug(("Require the \"" + moduleName + "\" module.")); // return cached module proxy // if (moduleProxies[moduleName]) { // return moduleProxies[moduleName] // } // create module proxy var apis = {}; moduleDefine.forEach(function (methodDefine) { if (typeof methodDefine === 'string') { apis[methodDefine] = function () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; return moduleInvoker.apply(void 0, [ (moduleName + "." + methodDefine) ].concat( args )); }; } else if (isPlainObject(methodDefine) && methodDefine.name) { var methodName = methodDefine.name; if (methodDefine.sync) { apis[methodName] = function () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; return syncModuleInvoker.apply(void 0, [ (moduleName + "." + methodName) ].concat( args )); }; } else { apis[methodName] = function () { var args = [], len = arguments.length; while ( len-- ) args[ len ] = arguments[ len ]; return moduleInvoker.apply(void 0, [ (moduleName + "." + methodName) ].concat( args )); }; } } }); return apis; }; } /** * Handshake constant event names. * * Worker Renderer * │ W_PAGE_READY │ * │ ---------------------------> │ * │ <--------------------------- │ * │ R_PAGE_READY │ * │ │ * │ │ * │ SEND_STATE │ * │ ---------------------------> │ * │ <--------------------------- │ * │ GOT_STATE │ * │ │ * │ │ * │ W_STATE_CHANGE │ * │ ---------------------------> │ * │ <--------------------------- │ * │ R_STATE_CHANGE │ * │ │ */ /** * Handshake between worker and renderer. */ var R_READY = '[[RendererRuntimeReady]]'; /** * supported app lifecycles * [key]: lifecycle event name * [value]: lifecycle method name in options */ // supported page lifecycles /** * Global apis for communication between worker and native. */ /** * Global apis for communication between renderer and native. */ var POST_MESSAGE = '__windmill_post_message__'; var ON_MESSAGE = '__windmill_on_message__'; /** * The global api to create a standalone context for app. */ /** * The global api of worker runtime apis. */ /** * The global api to register dsl framework in worker context. */ /** * The global api to create a standalone module getter. */ var MODULE_GETTER = '__WINDMILL_MODULE_GETTER__'; /** * Event emitted when any page lifecycle emitted */ /** * Abstract Windmill Runtime APIs */ var AppRuntime = function AppRuntime() { }; AppRuntime.prototype.$on = function $on (event, callback) { if ( callback === void 0 ) callback = noop; debug(("$on \"" + event + "\"")); !event && throwError(("invalid event name " + event + " for windmill.$on.")); callback === noop && throwError(("bind event " + event + " with no callback through windmill.$on.")); return this.bridge.onReceiveMessage({ data: { type: event }, origin: this.label, type: "Event" /* Event */ }, callback); }; AppRuntime.prototype.$once = function $once (event, callback) { if ( callback === void 0 ) callback = noop; debug(("$once \"" + event + "\"")); !event && throwError(("invalid event name " + event + " for windmill.$once.")); callback === noop && throwError(("bind event " + event + " with no callback through windmill.$once.")); return this.bridge.onReceiveMessage({ data: { type: event, once: true }, origin: this.label, type: "Event" /* Event */ }, callback); }; AppRuntime.prototype.$off = function $off (event, listener) { debug(("$off \"" + event + "\"")); !event && throwError(("invalid event name " + event + " for windmill.$off.")); return this.bridge.unbindEventListener((("Event") + "." + event), listener); }; /** * binding lifecycle hooks. * @param lifecycle for app: 'app:launch', etc... for page: 'page@someClientId:load', etc... * @param hook callback function, taking a messageData.result.option as argument. */ AppRuntime.prototype.$cycle = function $cycle (lifecycle, hook) { if ( hook === void 0 ) hook = noop; debug(("$cycle \"" + lifecycle + "\"")); if (!lifecycle || !/^app:|^page(:|@)/.test(lifecycle)) { throwError(("invalid lifecycle name " + lifecycle + " for windmill.$cycle.")); } hook === noop && throwError(("hook lifecycle " + lifecycle + " with no callback function through windmill.$cycle.")); if (this.label !== 'AppWorker' && /^app:/.test(lifecycle)) { throwError("Shouldn't bind lifecycle hook for app in a page client."); } return this.bridge.onReceiveMessage({ type: "Lifecycle" /* Lifecycle */, origin: this.label, data: { type: lifecycle } }, hook); }; AppRuntime.prototype.$decycle = function $decycle (lifecycle, hook) { debug(("$decycle \"" + lifecycle + "\"")); if (!lifecycle || !/^app:|^page(:|@)/.test(lifecycle)) { throwError(("invalid lifecycle name " + lifecycle + " for windmill.$decycle.")); } return this.bridge.unbindEventListener((("Lifecycle") + "." + lifecycle), hook); }; /** * emit a message to local or remote. * @param event for local: just the event name. for remote: use 'eventName@clientId'. * @param data event data. * @param clientId (optional) remote client id or 'AppWorker' for worker. */ AppRuntime.prototype.$emit = function $emit (event, data, clientId) { if ( data === void 0 ) data = {}; debug(("$emit(\"" + event + "\")")); !event && throwError(("invalid event name " + event + " for windmill.$emit.")); // for remote event. emit a event to remote client (or 'AppWorker'). if (clientId && clientId !== this.label) { return this.bridge.postMessage({ data: { type: event, data: data }, origin: this.label, target: clientId, type: "Event" /* Event */ }); } // for local event. return this.bridge.emitEvent(("" + event), data); }; AppRuntime.prototype.$callAsync = function $callAsync (method, params, context, modifier) { var this$1 = this; if ( params === void 0 ) params = {}; if ( context === void 0 ) context = {}; if ( modifier === void 0 ) modifier = {}; debug(("$callAsync \"" + method + "\"")); if (!method) { throwError(("invalid method name " + method + " for windmill.$callAsync.")); } return new Promise(function (resolve, reject) { this$1.bridge.dispatchMessage({ type: "ModuleCall" /* ModuleCall */, origin: modifier.origin || this$1.label, target: modifier.target || 'AppWorker', data: { method: method, params: params, context: context } }, resolve, reject, false); }); }; AppRuntime.prototype.$call = function $call (method, params, successCallback, failureCallback, context, modifier) { if ( params === void 0 ) params = {}; if ( successCallback === void 0 ) successCallback = noop; if ( failureCallback === void 0 ) failureCallback = noop; if ( context === void 0 ) context = {}; if ( modifier === void 0 ) modifier = {}; debug(("$call \"" + method + "\", params: " + (JSON.stringify(params)))); if (!method) { throwError(("invalid method name " + method + " for windmill.$call.")); } return this.bridge.dispatchMessage({ type: "ModuleCall" /* ModuleCall */, origin: modifier.origin || this.label, target: modifier.target || 'AppWorker', data: { method: method, params: params, context: context }, }, successCallback, failureCallback, true); }; AppRuntime.prototype.$registerModules = function $registerModules (modules) { registerModules(modules, true); }; AppRuntime.prototype.$getAvailableModules = function $getAvailableModules () { return deepClone(availableModules$1); }; /** * Event Emitter * Partially implement the events module of Node.js. * https://nodejs.org/api/events.html#events_class_eventemitter */ var eventMapKey = '[[EVENT_MAP]]'; function initListeners(emitter, eventName) { var events = emitter[eventMapKey]; if (!Array.isArray(events[eventName])) { events[eventName] = []; } return events[eventName]; } var EventEmitter = function EventEmitter() { debug("Create new EventEmitter."); Object.defineProperty(this, eventMapKey, { configurable: false, enumerable: false, writable: false, value: {} }); }; EventEmitter.prototype.on = function on (eventName, listener) { debug(("EventEmitter: on(\"" + eventName + "\")")); var listeners = initListeners(this, eventName); listeners.push({ listener: listener, once: false, callCount: 0 }); return this; }; EventEmitter.prototype.once = function once (eventName, listener) { debug(("EventEmitter: once(\"" + eventName + "\")")); var listeners = initListeners(this, eventName); listeners.push({ listener: listener, once: true, callCount: 0 }); return this; }; EventEmitter.prototype.off = function off (eventName, listener) { debug(("EventEmitter: off(\"" + eventName + "\"" + (listener ? ', listener' : '') + ")")); var events = this[eventMapKey]; if (listener) { var listeners = events[eventName]; if (Array.isArray(listeners)) { var index = listeners.findIndex(function (x) { return x.listener === listener; }); if (index !== -1) { listeners.splice(index, 1); } } } else { delete events[eventName]; } return this; }; EventEmitter.prototype.emit = function emit (eventName) { var args = [], len = arguments.length - 1; while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ]; debug(("EventEmitter: emit(\"" + eventName + "\", " + (JSON.stringify(args)) + ")")); var events = this[eventMapKey]; var listeners = events[eventName]; if (Array.isArray(listeners)) { // invoke event listeners events[eventName] = listeners.filter(function (desc) { try { desc.listener.apply(null, args); desc.callCount += 1; } catch (err) { error("Failed to emit the \"" + eventName + "\" event: " + (err.toString()) + "\nSource code: " + (getCode(desc.listener))); // throw the error anyway throw err; } return !desc.once; }); } return this; }; function getMsgModuleFromWeex(weex) { return (weex.requireModule('@weex/windmill') || weex.requireModule('windmill')); } /** * Using native bridge APIs if they are exist. * * It would be more efficient than using module APIs. * https://yuque.antfin-inc.com/taobaoapp/design/app_render_interface */ var bridgeWarningCount = 3; function getMsgModuleFromContext() { var context = getContextObject(); if (typeof context[POST_MESSAGE] === 'function') { var bridge = {}; bridge.postMessage = context[POST_MESSAGE]; bridge.onmessage = function (onMessage) { debug(("Register the \"" + ON_MESSAGE + "\" API.")); setGlobalAPI(ON_MESSAGE, onMessage); }; return bridge; } if (bridgeWarningCount > 0) { bridgeWarningCount--; error(("Can't find available \"" + POST_MESSAGE + "\" API in the context.")); } return false; } // function callNativeBridge(msg: any) { // windmill.postMessage(JSON.stringify(msg)) // } function S4() { return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); } function guid() { return (S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4()); } function callNativeBridge(msg, identify) { windmill.runMessage({ cmd: 'postMessage', identify: identify, data: { message: JSON.stringify(msg), pageid: ("[Page ID : " + (guid()) + "]") } }); } function onNotify(callback) { onmessage = function (msg) { callback(msg); }; } var _msgModuleForWebview; var _onMessageCalledOnce = false; function getMsgModuleFromWebview(identify) { if (!_msgModuleForWebview) { // const context: any = typeof window !== 'undefined' ? window : global _msgModuleForWebview = { postMessage: function postMessage(msg) { // return callWindvane(context, 'Windmill', 'postMessage', msg, () => {}, handleError) return callNativeBridge(msg, identify); }, onmessage: function onmessage(callback) { "development" !== 'production' && console.debug("Set onMessage for webview."); if (_onMessageCalledOnce) { return; } _onMessageCalledOnce = true; // return callWindvane(context, 'Windmill', 'onMessage', {}, callback, handleError) return onNotify(callback); } }; } return _msgModuleForWebview; } /** * in case the messageModule is not ready, we should use lazy module * to cache all the messages assumed to post, and when the one is * ready, then flush the messages. */ var LazyMessageModule = function LazyMessageModule() { this._messages = []; }; LazyMessageModule.prototype.postMessage = function postMessage (message) { debug(("cache message in lazyMsgModule. " + (JSON.stringify(message)))); this._messages.push(message); }; LazyMessageModule.prototype.replaceWith = function replaceWith (msgModule) { isLazyModuleActive = false; this._messageModule = msgModule; this._flush(); }; LazyMessageModule.prototype._flush = function _flush () { var this$1 = this; this._messages.forEach(function (msg) { return this$1._messageModule.postMessage(msg); }); }; var lazyModule = new LazyMessageModule(); var isLazyModuleActive; /** * getMessageModule will return the current messageModule or a lazy module if current one * is not ready yet. The lazy module will cache all the posted messages, and flush them when * a real messageModule (like 'windmill' module for weex) is ready. In the mean time when the * real messageModule is ready, we'll set the onMessage hook to it. * @param weex Weex instance. * @param onMessage set message module's onmessage method with this function. */ function getMessageModule(weex, onMessage, identify) { var msgModule = (weex ? getMsgModuleFromWeex(weex) : (getMsgModuleFromContext() || getMsgModuleFromWebview(identify))); if (!msgModule) { throwError(("can't find message module (" + (weex ? 'windmill module' : 'message module in webview') + ").")); isLazyModuleActive = true; return lazyModule; } // set onmessage method to the message module. if (typeof onMessage === 'function') { debug('Set the "onmessage" method to the bridge through callback.'); try { msgModule.onmessage(onMessage); } catch (e) { error('Failed to set the "onmessage" method to the bridge through callback.'); error(e.toString()); } } // replace lazy msg module. if (isLazyModuleActive) { lazyModule.replaceWith(msgModule); } return msgModule; } var isWeex; // for 'evt@clientId', return 'evt' // for 'evt', return 'evt' itself. function getPureEventName(name) { var match = name.match(/([^@]+)(@\S+)/); return match && match[1] || name; } var RendererBridge = function RendererBridge(weex, option) { var this$1 = this; if ( option === void 0 ) option = {}; debug('Create a new RendererBridge.'); this.identify = option.identify; debug(("[multi-app] The current worker identify is " + (this.identify))); isWeex = option.isWeex; this._emitter = new EventEmitter(); // register onmessage api debug(("Register onmessage api for " + (isWeex ? 'Weex windmill module' : 'webview') + ".")); if (isWeex) { this._weex = weex; } this._onMessage = function (message) { debug(("Receive message from the worker. (message: " + (JSON.stringify(message)) + ")")); if (!isPlainObject(message)) { error("The parameter of onmessage should be a plain object! " + "Not " + (getType(message))); if (typeof message === 'string') { try { message = JSON.parse(message); } catch (e) { error(("Failed to parse the message string. " + (e.toString()))); return; } } } var type = message.type; var msgData = message.data; if ( msgData === void 0 ) msgData = {}; var origin = message.origin; var callbackId = message.callbackId; switch (type) { case "Callback" /* Callback */: { this$1._emitter.emit(("callback@AppWorker:" + callbackId), msgData); } break; case "Event" /* Event */: { var eventType = getPureEventName(msgData.type); this$1._emitter.emit((type + "." + eventType), Object.assign({ origin: origin }, msgData, { type: eventType })); } break; case "Lifecycle" /* Lifecycle */: { this$1._emitter.emit((type + "." + (msgData.lifecycle)), msgData.options); } break; case "SetData" /* SetData */: { this$1._emitter.emit((("Event") + ".setdata"), msgData); } break; default: { throwError(("Unknown message type received from worker: \"" + type + "\"")); } } }; getMessageModule(weex, this._onMessage, this.identify); }; RendererBridge.prototype.dispatchMessage = function dispatchMessage (message, success, fail, keepAlive) { debug(("Dispatch message to worker. (message: " + (JSON.stringify(message)) + ")")); message.callbackId = this._genCallbackId(function (param) { if (param.status && param.status === 'SUCCESS') { return success(param); } // TODO: check return status switch (param.status) { case 'FAILED': break; } error("call API " + (message.data.method) + " " + "from renderer failed: " + (param.status) + ", " + (param.error)); return fail(param); }, { keepAlive: keepAlive }); try { message.keepAlive = true; this.postMessage(message); } catch (err) { fail(err); } }; /** * post message to worker. * @param message MessageObject. */ RendererBridge.prototype.postMessage = function postMessage (message) { try { debug(("Call postMessage to worker. (message: " + (JSON.stringify(message)) + ")")); getMessageModule(this._weex, this._onMessage, this.identify).postMessage(message); } catch (err) { throwError(err.message || err.toString() || 'postMessage failed.'); } }; /** * auto bind a callbackId to a callback listener. * return a callbackId. */ RendererBridge.prototype._genCallbackId = function _genCallbackId (callback, ref) { var keepAlive = ref.keepAlive; var callbackId = ref.callbackId; var id = callbackId ? callbackId : uniqueId(); this._emitter[keepAlive ? 'on' : 'once'](("callback@AppWorker:" + id), callback); return id; }; /** * bind messages with user defined handlers. * type: * 1. event: Event, data.type: xxx * 2. lifecycle: Lifecycle, data.type: 'app:launch' / 'page:show' */ RendererBridge.prototype.onReceiveMessage = function onReceiveMessage (message, callback) { var type = message.type; var data = message.data; if ( data === void 0 ) data = {}; // Event / Lifecycle var name = data.type; if (type === "Event" /* Event */) { name = getPureEventName(name); data.type = name; } var bind = data.once ? 'once' : 'on'; this._emitter[bind]((type + "." + name), callback); }; RendererBridge.prototype.unbindEventListener = function unbindEventListener (event, listener) { this._emitter.off(getPureEventName(event), listener); }; RendererBridge.prototype.emitEvent = function emitEvent (event, data) { if ( data === void 0 ) data = {}; var evtName = getPureEventName(event); this._emitter.emit((("Event") + "." + evtName), { type: evtName, data: data }); }; var RendererRuntime = (function (AppRuntime$$1) { function RendererRuntime(weex, options) { if ( options === void 0 ) options = {}; AppRuntime$$1.call(this); this._name = options.pageName || ''; this.label = options.clientId || ''; if (weex && isPlainObject(weex.config)) { this._name = weex.config.pageName || ''; this.label = weex.config.clientId || ''; if (weex.config.availableModules) { this.$registerModules(weex.config.availableModules); this._gotAvailableModulesFromWeex = true; } else { warn("No available modules in weex options."); } } debug(("Create a new RendererRuntime. (" + (this.label) + ", " + (this._name) + ")")); this.bridge = new RendererBridge(weex, options); this._init(); } if ( AppRuntime$$1 ) RendererRuntime.__proto__ = AppRuntime$$1; RendererRuntime.prototype = Object.create( AppRuntime$$1 && AppRuntime$$1.prototype ); RendererRuntime.prototype.constructor = RendererRuntime; RendererRuntime.prototype._init = function _init () { var this$1 = this; // listening on event from worker and register available modules this.bridge.onReceiveMessage({ origin: 'AppWorker', target: this.label, type: "Event" /* Event */, data: { type: 'registerModules' } }, function (event /* EventData */) { if (event && event.type === 'registerModules') { debug(("Register available modules in renderer. (" + (Object.keys(event.data)) + ")")); this$1.$registerModules(event.data); } }); }; // dispatch ready message RendererRuntime.prototype.ready = function ready () { this.bridge.dispatchMessage({ origin: this.label, target: 'AppWorker', type: "Event" /* Event */, data: { type: R_READY, data: { label: this.label, handshake: !this._gotAvailableModulesFromWeex } } }, noop, noop); }; RendererRuntime.prototype.$getCurrentActivePageId = function $getCurrentActivePageId () { return this.label; }; return RendererRuntime; }(AppRuntime)); var exposedAPIs = [ '$on', '$once', '$off', '$emit', '$cycle', '$decycle', '$call', '$callAsync', '$navTo', '$getAvailableModules', '$getCurrentActivePageId' ]; function index (weex, options) { var isWeex = !!(weex && weex.requireModule); setLoggerEnv({ platform: isWeex && weex.config.env.platform, debugMode: isWeex && weex.config.env.debugMode, isWeex: isWeex }); debug("Setup Windmill renderer framework."); // For weex renderer, use weex.requireModule. For webview // renderer, ignore the weex parameter. if (weex && !weex.requireModule) { options = weex; weex = undefined; } if (!options) { options = {}; } options = Object.assign({}, options); // incase option is frozen. options.isWeex = isWeex; debug(("Instantiate renderer in " + (isWeex ? 'Weex container' : 'WindVane container') + ".")); var renderer = new RendererRuntime(weex, options); var runtime = pickMethods(renderer, exposedAPIs); debug(("Prepare runtime APIs: " + (JSON.stringify(exposedAPIs)))); // whether to expose the module getter api to current context if (options.exposeModuleGetter) { setGlobalAPI(MODULE_GETTER, createModuleGetter(function () { return runtime; })); } renderer.ready(); return runtime; } return index; })));