|
|
@@ -34,7 +34,7 @@ function init (cfg) {
|
|
|
renderer.Document = cfg.Document;
|
|
|
renderer.Element = cfg.Element;
|
|
|
renderer.Comment = cfg.Comment;
|
|
|
- renderer.sendTasks = cfg.sendTasks;
|
|
|
+ renderer.compileBundle = cfg.compileBundle;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -47,7 +47,7 @@ function reset () {
|
|
|
delete renderer.Document;
|
|
|
delete renderer.Element;
|
|
|
delete renderer.Comment;
|
|
|
- delete renderer.sendTasks;
|
|
|
+ delete renderer.compileBundle;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -82,18 +82,9 @@ function createInstance (
|
|
|
// Virtual-DOM object.
|
|
|
var document = new renderer.Document(instanceId, config.bundleUrl);
|
|
|
|
|
|
- // All function/callback of parameters before sent to native
|
|
|
- // will be converted as an id. So `callbacks` is used to store
|
|
|
- // these real functions. When a callback invoked and won't be
|
|
|
- // called again, it should be removed from here automatically.
|
|
|
- var callbacks = [];
|
|
|
-
|
|
|
- // The latest callback id, incremental.
|
|
|
- var callbackId = 1;
|
|
|
-
|
|
|
var instance = instances[instanceId] = {
|
|
|
instanceId: instanceId, config: config, data: data,
|
|
|
- document: document, callbacks: callbacks, callbackId: callbackId
|
|
|
+ document: document
|
|
|
};
|
|
|
|
|
|
// Prepare native module getter and HTML5 Timer APIs.
|
|
|
@@ -104,6 +95,7 @@ function createInstance (
|
|
|
var weexInstanceVar = {
|
|
|
config: config,
|
|
|
document: document,
|
|
|
+ supports: supports,
|
|
|
requireModule: moduleGetter
|
|
|
};
|
|
|
Object.freeze(weexInstanceVar);
|
|
|
@@ -118,11 +110,16 @@ function createInstance (
|
|
|
weex: weexInstanceVar,
|
|
|
// deprecated
|
|
|
__weex_require_module__: weexInstanceVar.requireModule // eslint-disable-line
|
|
|
- }, timerAPIs);
|
|
|
- callFunction(instanceVars, appCode);
|
|
|
+ }, timerAPIs, env.services);
|
|
|
+
|
|
|
+ if (!callFunctionNative(instanceVars, appCode)) {
|
|
|
+ // If failed to compile functionBody on native side,
|
|
|
+ // fallback to 'callFunction()'.
|
|
|
+ callFunction(instanceVars, appCode);
|
|
|
+ }
|
|
|
|
|
|
// Send `createFinish` signal to native.
|
|
|
- renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'createFinish', args: [] }], -1);
|
|
|
+ instance.document.taskCenter.send('dom', { action: 'createFinish' }, []);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -133,6 +130,7 @@ function createInstance (
|
|
|
function destroyInstance (instanceId) {
|
|
|
var instance = instances[instanceId];
|
|
|
if (instance && instance.app instanceof instance.Vue) {
|
|
|
+ instance.document.destroy();
|
|
|
instance.app.$destroy();
|
|
|
}
|
|
|
delete instances[instanceId];
|
|
|
@@ -154,7 +152,7 @@ function refreshInstance (instanceId, data) {
|
|
|
instance.Vue.set(instance.app, key, data[key]);
|
|
|
}
|
|
|
// Finally `refreshFinish` signal needed.
|
|
|
- renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'refreshFinish', args: [] }], -1);
|
|
|
+ instance.document.taskCenter.send('dom', { action: 'refreshFinish' }, []);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -169,49 +167,57 @@ function getRoot (instanceId) {
|
|
|
return instance.app.$el.toJSON()
|
|
|
}
|
|
|
|
|
|
+var jsHandlers = {
|
|
|
+ fireEvent: function (id) {
|
|
|
+ var args = [], len = arguments.length - 1;
|
|
|
+ while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
|
|
|
+
|
|
|
+ return fireEvent.apply(void 0, [ instances[id] ].concat( args ))
|
|
|
+ },
|
|
|
+ callback: function (id) {
|
|
|
+ var args = [], len = arguments.length - 1;
|
|
|
+ while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
|
|
|
+
|
|
|
+ return callback.apply(void 0, [ instances[id] ].concat( args ))
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+function fireEvent (instance, nodeId, type, e, domChanges) {
|
|
|
+ var el = instance.document.getRef(nodeId);
|
|
|
+ if (el) {
|
|
|
+ return instance.document.fireEvent(el, type, e, domChanges)
|
|
|
+ }
|
|
|
+ return new Error(("invalid element reference \"" + nodeId + "\""))
|
|
|
+}
|
|
|
+
|
|
|
+function callback (instance, callbackId, data, ifKeepAlive) {
|
|
|
+ var result = instance.document.taskCenter.callback(callbackId, data, ifKeepAlive);
|
|
|
+ instance.document.taskCenter.send('dom', { action: 'updateFinish' }, []);
|
|
|
+ return result
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
- * Receive tasks from native. Generally there are two types of tasks:
|
|
|
- * 1. `fireEvent`: an device actions or user actions from native.
|
|
|
- * 2. `callback`: invoke function which sent to native as a parameter before.
|
|
|
- * @param {string} instanceId
|
|
|
- * @param {array} tasks
|
|
|
+ * Accept calls from native (event or callback).
|
|
|
+ *
|
|
|
+ * @param {string} id
|
|
|
+ * @param {array} tasks list with `method` and `args`
|
|
|
*/
|
|
|
-function receiveTasks (instanceId, tasks) {
|
|
|
- var instance = instances[instanceId];
|
|
|
- if (!instance || !(instance.app instanceof instance.Vue)) {
|
|
|
- return new Error(("receiveTasks: instance " + instanceId + " not found!"))
|
|
|
- }
|
|
|
- var callbacks = instance.callbacks;
|
|
|
- var document = instance.document;
|
|
|
- tasks.forEach(function (task) {
|
|
|
- // `fireEvent` case: find the event target and fire.
|
|
|
- if (task.method === 'fireEvent') {
|
|
|
- var ref = task.args;
|
|
|
- var nodeId = ref[0];
|
|
|
- var type = ref[1];
|
|
|
- var e = ref[2];
|
|
|
- var domChanges = ref[3];
|
|
|
- var el = document.getRef(nodeId);
|
|
|
- document.fireEvent(el, type, e, domChanges);
|
|
|
- }
|
|
|
- // `callback` case: find the callback by id and call it.
|
|
|
- if (task.method === 'callback') {
|
|
|
- var ref$1 = task.args;
|
|
|
- var callbackId = ref$1[0];
|
|
|
- var data = ref$1[1];
|
|
|
- var ifKeepAlive = ref$1[2];
|
|
|
- var callback = callbacks[callbackId];
|
|
|
- if (typeof callback === 'function') {
|
|
|
- callback(data);
|
|
|
- // Remove the callback from `callbacks` if it won't called again.
|
|
|
- if (typeof ifKeepAlive === 'undefined' || ifKeepAlive === false) {
|
|
|
- callbacks[callbackId] = undefined;
|
|
|
- }
|
|
|
+function receiveTasks (id, tasks) {
|
|
|
+ var instance = instances[id];
|
|
|
+ if (instance && Array.isArray(tasks)) {
|
|
|
+ var results = [];
|
|
|
+ tasks.forEach(function (task) {
|
|
|
+ var handler = jsHandlers[task.method];
|
|
|
+ var args = [].concat( task.args );
|
|
|
+ /* istanbul ignore else */
|
|
|
+ if (typeof handler === 'function') {
|
|
|
+ args.unshift(id);
|
|
|
+ results.push(handler.apply(void 0, args));
|
|
|
}
|
|
|
- }
|
|
|
- });
|
|
|
- // Finally `updateFinish` signal needed.
|
|
|
- renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'updateFinish', args: [] }], -1);
|
|
|
+ });
|
|
|
+ return results
|
|
|
+ }
|
|
|
+ return new Error(("invalid instance id \"" + id + "\" or tasks"))
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -235,6 +241,18 @@ function registerModules (newModules) {
|
|
|
for (var name in newModules) loop( name );
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Check whether the module or the method has been registered.
|
|
|
+ * @param {String} module name
|
|
|
+ * @param {String} method name (optional)
|
|
|
+ */
|
|
|
+function isRegisteredModule (name, method) {
|
|
|
+ if (typeof method === 'string') {
|
|
|
+ return !!(modules[name] && modules[name][method])
|
|
|
+ }
|
|
|
+ return !!modules[name]
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Register native components information.
|
|
|
* @param {array} newComponents
|
|
|
@@ -254,6 +272,35 @@ function registerComponents (newComponents) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * Check whether the component has been registered.
|
|
|
+ * @param {String} component name
|
|
|
+ */
|
|
|
+function isRegisteredComponent (name) {
|
|
|
+ return !!components[name]
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Detects whether Weex supports specific features.
|
|
|
+ * @param {String} condition
|
|
|
+ */
|
|
|
+function supports (condition) {
|
|
|
+ if (typeof condition !== 'string') { return null }
|
|
|
+
|
|
|
+ var res = condition.match(/^@(\w+)\/(\w+)(\.(\w+))?$/i);
|
|
|
+ if (res) {
|
|
|
+ var type = res[1];
|
|
|
+ var name = res[2];
|
|
|
+ var method = res[4];
|
|
|
+ switch (type) {
|
|
|
+ case 'module': return isRegisteredModule(name, method)
|
|
|
+ case 'component': return isRegisteredComponent(name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return null
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* Create a fresh instance of Vue for each Weex instance.
|
|
|
*/
|
|
|
@@ -314,9 +361,7 @@ function createVueModuleInstance (instanceId, moduleGetter) {
|
|
|
* Generate native module getter. Each native module has several
|
|
|
* methods to call. And all the behaviors is instance-related. So
|
|
|
* this getter will return a set of methods which additionally
|
|
|
- * send current instance id to native when called. Also the args
|
|
|
- * will be normalized into "safe" value. For example function arg
|
|
|
- * will be converted into a callback id.
|
|
|
+ * send current instance id to native when called.
|
|
|
* @param {string} instanceId
|
|
|
* @return {function}
|
|
|
*/
|
|
|
@@ -326,15 +371,23 @@ function genModuleGetter (instanceId) {
|
|
|
var nativeModule = modules[name] || [];
|
|
|
var output = {};
|
|
|
var loop = function ( methodName ) {
|
|
|
- output[methodName] = function () {
|
|
|
- var args = [], len = arguments.length;
|
|
|
- while ( len-- ) args[ len ] = arguments[ len ];
|
|
|
-
|
|
|
- var finalArgs = args.map(function (value) {
|
|
|
- return normalize(value, instance)
|
|
|
- });
|
|
|
- renderer.sendTasks(instanceId + '', [{ module: name, method: methodName, args: finalArgs }], -1);
|
|
|
- };
|
|
|
+ Object.defineProperty(output, methodName, {
|
|
|
+ enumerable: true,
|
|
|
+ configurable: true,
|
|
|
+ get: function proxyGetter () {
|
|
|
+ return function () {
|
|
|
+ var args = [], len = arguments.length;
|
|
|
+ while ( len-- ) args[ len ] = arguments[ len ];
|
|
|
+
|
|
|
+ return instance.document.taskCenter.send('module', { module: name, method: methodName }, args)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ set: function proxySetter (val) {
|
|
|
+ if (typeof val === 'function') {
|
|
|
+ return instance.document.taskCenter.send('module', { module: name, method: methodName }, [val])
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
for (var methodName in nativeModule) loop( methodName );
|
|
|
@@ -362,8 +415,9 @@ function getInstanceTimer (instanceId, moduleGetter) {
|
|
|
var handler = function () {
|
|
|
args[0].apply(args, args.slice(2));
|
|
|
};
|
|
|
+
|
|
|
timer.setTimeout(handler, args[1]);
|
|
|
- return instance.callbackId.toString()
|
|
|
+ return instance.document.taskCenter.callbackManager.lastCallbackId.toString()
|
|
|
},
|
|
|
setInterval: function () {
|
|
|
var args = [], len = arguments.length;
|
|
|
@@ -372,8 +426,9 @@ function getInstanceTimer (instanceId, moduleGetter) {
|
|
|
var handler = function () {
|
|
|
args[0].apply(args, args.slice(2));
|
|
|
};
|
|
|
+
|
|
|
timer.setInterval(handler, args[1]);
|
|
|
- return instance.callbackId.toString()
|
|
|
+ return instance.document.taskCenter.callbackManager.lastCallbackId.toString()
|
|
|
},
|
|
|
clearTimeout: function (n) {
|
|
|
timer.clearTimeout(n);
|
|
|
@@ -405,52 +460,55 @@ function callFunction (globalObjects, body) {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * Convert all type of values into "safe" format to send to native.
|
|
|
- * 1. A `function` will be converted into callback id.
|
|
|
- * 2. An `Element` object will be converted into `ref`.
|
|
|
- * The `instance` param is used to generate callback id and store
|
|
|
- * function if necessary.
|
|
|
- * @param {any} v
|
|
|
- * @param {object} instance
|
|
|
- * @return {any}
|
|
|
+ * Call a new function generated on the V8 native side.
|
|
|
+ *
|
|
|
+ * This function helps speed up bundle compiling. Normally, the V8
|
|
|
+ * engine needs to download, parse, and compile a bundle on every
|
|
|
+ * visit. If 'compileBundle()' is available on native side,
|
|
|
+ * the downloding, parsing, and compiling steps would be skipped.
|
|
|
+ * @param {object} globalObjects
|
|
|
+ * @param {string} body
|
|
|
+ * @return {boolean}
|
|
|
*/
|
|
|
-function normalize (v, instance) {
|
|
|
- var type = typof(v);
|
|
|
-
|
|
|
- switch (type) {
|
|
|
- case 'undefined':
|
|
|
- case 'null':
|
|
|
- return ''
|
|
|
- case 'regexp':
|
|
|
- return v.toString()
|
|
|
- case 'date':
|
|
|
- return v.toISOString()
|
|
|
- case 'number':
|
|
|
- case 'string':
|
|
|
- case 'boolean':
|
|
|
- case 'array':
|
|
|
- case 'object':
|
|
|
- if (v instanceof renderer.Element) {
|
|
|
- return v.ref
|
|
|
- }
|
|
|
- return v
|
|
|
- case 'function':
|
|
|
- instance.callbacks[++instance.callbackId] = v;
|
|
|
- return instance.callbackId.toString()
|
|
|
- default:
|
|
|
- return JSON.stringify(v)
|
|
|
+function callFunctionNative (globalObjects, body) {
|
|
|
+ if (typeof renderer.compileBundle !== 'function') {
|
|
|
+ return false
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
-/**
|
|
|
- * Get the exact type of an object by `toString()`. For example call
|
|
|
- * `toString()` on an array will be returned `[object Array]`.
|
|
|
- * @param {any} v
|
|
|
- * @return {string}
|
|
|
- */
|
|
|
-function typof (v) {
|
|
|
- var s = Object.prototype.toString.call(v);
|
|
|
- return s.substring(8, s.length - 1).toLowerCase()
|
|
|
+ var fn = void 0;
|
|
|
+ var isNativeCompileOk = false;
|
|
|
+ var script = '(function (';
|
|
|
+ var globalKeys = [];
|
|
|
+ var globalValues = [];
|
|
|
+ for (var key in globalObjects) {
|
|
|
+ globalKeys.push(key);
|
|
|
+ globalValues.push(globalObjects[key]);
|
|
|
+ }
|
|
|
+ for (var i = 0; i < globalKeys.length - 1; ++i) {
|
|
|
+ script += globalKeys[i];
|
|
|
+ script += ',';
|
|
|
+ }
|
|
|
+ script += globalKeys[globalKeys.length - 1];
|
|
|
+ script += ') {';
|
|
|
+ script += body;
|
|
|
+ script += '} )';
|
|
|
+
|
|
|
+ try {
|
|
|
+ var weex = globalObjects.weex || {};
|
|
|
+ var config = weex.config || {};
|
|
|
+ fn = renderer.compileBundle(script,
|
|
|
+ config.bundleUrl,
|
|
|
+ config.bundleDigest,
|
|
|
+ config.codeCachePath);
|
|
|
+ if (fn && typeof fn === 'function') {
|
|
|
+ fn.apply(void 0, globalValues);
|
|
|
+ isNativeCompileOk = true;
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error(e);
|
|
|
+ }
|
|
|
+
|
|
|
+ return isNativeCompileOk
|
|
|
}
|
|
|
|
|
|
exports.init = init;
|
|
|
@@ -461,4 +519,7 @@ exports.refreshInstance = refreshInstance;
|
|
|
exports.getRoot = getRoot;
|
|
|
exports.receiveTasks = receiveTasks;
|
|
|
exports.registerModules = registerModules;
|
|
|
+exports.isRegisteredModule = isRegisteredModule;
|
|
|
exports.registerComponents = registerComponents;
|
|
|
+exports.isRegisteredComponent = isRegisteredComponent;
|
|
|
+exports.supports = supports;
|