index.js 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. // this will be preserved during build
  4. var VueFactory = require('./factory');
  5. var instances = {};
  6. /**
  7. * Prepare framework config.
  8. * Nothing need to do actually, just an interface provided to weex runtime.
  9. */
  10. function init () {}
  11. /**
  12. * Reset framework config and clear all registrations.
  13. */
  14. function reset () {
  15. clear(instances);
  16. }
  17. /**
  18. * Delete all keys of an object.
  19. * @param {object} obj
  20. */
  21. function clear (obj) {
  22. for (var key in obj) {
  23. delete obj[key];
  24. }
  25. }
  26. /**
  27. * Create an instance with id, code, config and external data.
  28. * @param {string} instanceId
  29. * @param {string} appCode
  30. * @param {object} config
  31. * @param {object} data
  32. * @param {object} env { info, config, services }
  33. */
  34. function createInstance (
  35. instanceId,
  36. appCode,
  37. config,
  38. data,
  39. env
  40. ) {
  41. if ( appCode === void 0 ) appCode = '';
  42. if ( config === void 0 ) config = {};
  43. if ( env === void 0 ) env = {};
  44. var weex = env.weex;
  45. var document = weex.document;
  46. var instance = instances[instanceId] = {
  47. instanceId: instanceId, config: config, data: data,
  48. document: document
  49. };
  50. var timerAPIs = getInstanceTimer(instanceId, weex.requireModule);
  51. // Each instance has a independent `Vue` module instance
  52. var Vue = instance.Vue = createVueModuleInstance(instanceId, weex);
  53. // The function which create a closure the JS Bundle will run in.
  54. // It will declare some instance variables like `Vue`, HTML5 Timer APIs etc.
  55. var instanceVars = Object.assign({
  56. Vue: Vue,
  57. weex: weex
  58. }, timerAPIs, env.services);
  59. appCode = "(function(global){ \n" + appCode + "\n })(Object.create(this))";
  60. callFunction(instanceVars, appCode);
  61. // Send `createFinish` signal to native.
  62. document.taskCenter.send('dom', { action: 'createFinish' }, []);
  63. return instance
  64. }
  65. /**
  66. * Destroy an instance with id. It will make sure all memory of
  67. * this instance released and no more leaks.
  68. * @param {string} instanceId
  69. */
  70. function destroyInstance (instanceId) {
  71. var instance = instances[instanceId];
  72. if (instance && instance.app instanceof instance.Vue) {
  73. instance.document.destroy();
  74. instance.app.$destroy();
  75. delete instance.document;
  76. delete instance.app;
  77. }
  78. delete instances[instanceId];
  79. }
  80. /**
  81. * Refresh an instance with id and new top-level component data.
  82. * It will use `Vue.set` on all keys of the new data. So it's better
  83. * define all possible meaningful keys when instance created.
  84. * @param {string} instanceId
  85. * @param {object} data
  86. */
  87. function refreshInstance (instanceId, data) {
  88. var instance = instances[instanceId];
  89. if (!instance || !(instance.app instanceof instance.Vue)) {
  90. return new Error(("refreshInstance: instance " + instanceId + " not found!"))
  91. }
  92. for (var key in data) {
  93. instance.Vue.set(instance.app, key, data[key]);
  94. }
  95. // Finally `refreshFinish` signal needed.
  96. instance.document.taskCenter.send('dom', { action: 'refreshFinish' }, []);
  97. }
  98. /**
  99. * Get the JSON object of the root element.
  100. * @param {string} instanceId
  101. */
  102. function getRoot (instanceId) {
  103. var instance = instances[instanceId];
  104. if (!instance || !(instance.app instanceof instance.Vue)) {
  105. return new Error(("getRoot: instance " + instanceId + " not found!"))
  106. }
  107. return instance.app.$el.toJSON()
  108. }
  109. var jsHandlers = {
  110. fireEvent: function (id) {
  111. var args = [], len = arguments.length - 1;
  112. while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
  113. return fireEvent.apply(void 0, [ instances[id] ].concat( args ))
  114. },
  115. callback: function (id) {
  116. var args = [], len = arguments.length - 1;
  117. while ( len-- > 0 ) args[ len ] = arguments[ len + 1 ];
  118. return callback.apply(void 0, [ instances[id] ].concat( args ))
  119. }
  120. };
  121. function fireEvent (instance, nodeId, type, e, domChanges) {
  122. var el = instance.document.getRef(nodeId);
  123. if (el) {
  124. return instance.document.fireEvent(el, type, e, domChanges)
  125. }
  126. return new Error(("invalid element reference \"" + nodeId + "\""))
  127. }
  128. function callback (instance, callbackId, data, ifKeepAlive) {
  129. var result = instance.document.taskCenter.callback(callbackId, data, ifKeepAlive);
  130. instance.document.taskCenter.send('dom', { action: 'updateFinish' }, []);
  131. return result
  132. }
  133. /**
  134. * Accept calls from native (event or callback).
  135. *
  136. * @param {string} id
  137. * @param {array} tasks list with `method` and `args`
  138. */
  139. function receiveTasks (id, tasks) {
  140. var instance = instances[id];
  141. if (instance && Array.isArray(tasks)) {
  142. var results = [];
  143. tasks.forEach(function (task) {
  144. var handler = jsHandlers[task.method];
  145. var args = [].concat( task.args );
  146. /* istanbul ignore else */
  147. if (typeof handler === 'function') {
  148. args.unshift(id);
  149. results.push(handler.apply(void 0, args));
  150. }
  151. });
  152. return results
  153. }
  154. return new Error(("invalid instance id \"" + id + "\" or tasks"))
  155. }
  156. /**
  157. * Create a fresh instance of Vue for each Weex instance.
  158. */
  159. function createVueModuleInstance (instanceId, weex) {
  160. var exports = {};
  161. VueFactory(exports, weex.document);
  162. var Vue = exports.Vue;
  163. var instance = instances[instanceId];
  164. // patch reserved tag detection to account for dynamically registered
  165. // components
  166. var weexRegex = /^weex:/i;
  167. var isReservedTag = Vue.config.isReservedTag || (function () { return false; });
  168. var isRuntimeComponent = Vue.config.isRuntimeComponent || (function () { return false; });
  169. Vue.config.isReservedTag = function (name) {
  170. return (!isRuntimeComponent(name) && weex.supports(("@component/" + name))) ||
  171. isReservedTag(name) ||
  172. weexRegex.test(name)
  173. };
  174. Vue.config.parsePlatformTagName = function (name) { return name.replace(weexRegex, ''); };
  175. // expose weex-specific info
  176. Vue.prototype.$instanceId = instanceId;
  177. Vue.prototype.$document = instance.document;
  178. // expose weex native module getter on subVue prototype so that
  179. // vdom runtime modules can access native modules via vnode.context
  180. Vue.prototype.$requireWeexModule = weex.requireModule;
  181. // Hack `Vue` behavior to handle instance information and data
  182. // before root component created.
  183. Vue.mixin({
  184. beforeCreate: function beforeCreate () {
  185. var options = this.$options;
  186. // root component (vm)
  187. if (options.el) {
  188. // set external data of instance
  189. var dataOption = options.data;
  190. var internalData = (typeof dataOption === 'function' ? dataOption() : dataOption) || {};
  191. options.data = Object.assign(internalData, instance.data);
  192. // record instance by id
  193. instance.app = this;
  194. }
  195. }
  196. });
  197. /**
  198. * @deprecated Just instance variable `weex.config`
  199. * Get instance config.
  200. * @return {object}
  201. */
  202. Vue.prototype.$getConfig = function () {
  203. if (instance.app instanceof Vue) {
  204. return instance.config
  205. }
  206. };
  207. return Vue
  208. }
  209. /**
  210. * Generate HTML5 Timer APIs. An important point is that the callback
  211. * will be converted into callback id when sent to native. So the
  212. * framework can make sure no side effect of the callback happened after
  213. * an instance destroyed.
  214. * @param {[type]} instanceId [description]
  215. * @param {[type]} moduleGetter [description]
  216. * @return {[type]} [description]
  217. */
  218. function getInstanceTimer (instanceId, moduleGetter) {
  219. var instance = instances[instanceId];
  220. var timer = moduleGetter('timer');
  221. var timerAPIs = {
  222. setTimeout: function () {
  223. var args = [], len = arguments.length;
  224. while ( len-- ) args[ len ] = arguments[ len ];
  225. var handler = function () {
  226. args[0].apply(args, args.slice(2));
  227. };
  228. timer.setTimeout(handler, args[1]);
  229. return instance.document.taskCenter.callbackManager.lastCallbackId.toString()
  230. },
  231. setInterval: function () {
  232. var args = [], len = arguments.length;
  233. while ( len-- ) args[ len ] = arguments[ len ];
  234. var handler = function () {
  235. args[0].apply(args, args.slice(2));
  236. };
  237. timer.setInterval(handler, args[1]);
  238. return instance.document.taskCenter.callbackManager.lastCallbackId.toString()
  239. },
  240. clearTimeout: function (n) {
  241. timer.clearTimeout(n);
  242. },
  243. clearInterval: function (n) {
  244. timer.clearInterval(n);
  245. }
  246. };
  247. return timerAPIs
  248. }
  249. /**
  250. * Call a new function body with some global objects.
  251. * @param {object} globalObjects
  252. * @param {string} code
  253. * @return {any}
  254. */
  255. function callFunction (globalObjects, body) {
  256. var globalKeys = [];
  257. var globalValues = [];
  258. for (var key in globalObjects) {
  259. globalKeys.push(key);
  260. globalValues.push(globalObjects[key]);
  261. }
  262. globalKeys.push(body);
  263. var result = new (Function.prototype.bind.apply( Function, [ null ].concat( globalKeys) ));
  264. return result.apply(void 0, globalValues)
  265. }
  266. exports.init = init;
  267. exports.reset = reset;
  268. exports.createInstance = createInstance;
  269. exports.destroyInstance = destroyInstance;
  270. exports.refreshInstance = refreshInstance;
  271. exports.getRoot = getRoot;
  272. exports.receiveTasks = receiveTasks;