You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by da...@apache.org on 2017/08/17 03:59:28 UTC

[11/43] incubator-weex git commit: * [jsfm] refactor the file structure in runtime

* [jsfm] refactor the file structure in runtime


Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/0729ac59
Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/0729ac59
Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/0729ac59

Branch: refs/heads/0.16-dev
Commit: 0729ac590a7e69aa279de973273fa228507f77f1
Parents: 25b0101
Author: Hanks <zh...@gmail.com>
Authored: Mon Aug 7 15:49:36 2017 +0800
Committer: Hanks <zh...@gmail.com>
Committed: Mon Aug 7 15:49:36 2017 +0800

----------------------------------------------------------------------
 html5/runtime/api/config.js             |  36 +++++
 html5/runtime/api/init.js               | 215 ++++++++++++++++++++++++++
 html5/runtime/api/service.js            |  77 ++++++++++
 html5/runtime/bridge/CallbackManager.js |  58 +++++++
 html5/runtime/bridge/Handler.js         |  91 +++++++++++
 html5/runtime/bridge/Listener.js        | 220 +++++++++++++++++++++++++++
 html5/runtime/bridge/TaskCenter.js      | 133 ++++++++++++++++
 html5/runtime/bridge/normalize.js       |  95 ++++++++++++
 html5/runtime/callback-manager.js       |  58 -------
 html5/runtime/config.js                 |  36 -----
 html5/runtime/handler.js                |  91 -----------
 html5/runtime/index.js                  |  11 +-
 html5/runtime/init.js                   | 215 --------------------------
 html5/runtime/listener.js               | 220 ---------------------------
 html5/runtime/normalize.js              |  98 ------------
 html5/runtime/service.js                |  77 ----------
 html5/runtime/task-center.js            | 132 ----------------
 html5/runtime/utils.js                  |  13 ++
 html5/runtime/vdom/comment.js           |   4 +-
 html5/runtime/vdom/directive.js         |   5 +-
 html5/runtime/vdom/document.js          |  10 +-
 html5/runtime/vdom/element.js           |   4 +-
 html5/runtime/vdom/index.js             |   8 +-
 html5/runtime/vdom/node.js              |   3 +-
 html5/runtime/vdom/operation.js         |   8 -
 25 files changed, 957 insertions(+), 961 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/api/config.js
----------------------------------------------------------------------
diff --git a/html5/runtime/api/config.js b/html5/runtime/api/config.js
new file mode 100644
index 0000000..729ceaf
--- /dev/null
+++ b/html5/runtime/api/config.js
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Document, Element, Comment } from '../vdom'
+import Listener from '../bridge/Listener'
+import { TaskCenter } from '../bridge/TaskCenter'
+
+const config = {
+  Document, Element, Comment, Listener,
+  TaskCenter,
+  sendTasks (...args) {
+    if (typeof callNative === 'function') {
+      return callNative(...args)
+    }
+    return (global.callNative || (() => {}))(...args)
+  }
+}
+
+Document.handler = config.sendTasks
+
+export default config

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/api/init.js
----------------------------------------------------------------------
diff --git a/html5/runtime/api/init.js b/html5/runtime/api/init.js
new file mode 100644
index 0000000..cc770af
--- /dev/null
+++ b/html5/runtime/api/init.js
@@ -0,0 +1,215 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { init as initTaskHandler } from '../bridge/TaskCenter'
+import { registerElement } from '../vdom/element-types'
+import { services, register, unregister } from './service'
+
+let frameworks
+let runtimeConfig
+
+const versionRegExp = /^\s*\/\/ *(\{[^}]*\}) *\r?\n/
+
+/**
+ * Detect a JS Bundle code and make sure which framework it's based to. Each JS
+ * Bundle should make sure that it starts with a line of JSON comment and is
+ * more that one line.
+ * @param  {string} code
+ * @return {object}
+ */
+function checkVersion (code) {
+  let info
+  const result = versionRegExp.exec(code)
+  if (result) {
+    try {
+      info = JSON.parse(result[1])
+    }
+    catch (e) {}
+  }
+  return info
+}
+
+function createServices (id, env, config) {
+  // Init JavaScript services for this instance.
+  const serviceMap = Object.create(null)
+  serviceMap.service = Object.create(null)
+  services.forEach(({ name, options }) => {
+    if (process.env.NODE_ENV === 'development') {
+      console.debug(`[JS Runtime] create service ${name}.`)
+    }
+    const create = options.create
+    if (create) {
+      const result = create(id, env, config)
+      Object.assign(serviceMap.service, result)
+      Object.assign(serviceMap, result.instance)
+    }
+  })
+  delete serviceMap.service.instance
+  Object.freeze(serviceMap.service)
+  return serviceMap
+}
+
+const instanceMap = {}
+
+/**
+ * Check which framework a certain JS Bundle code based to. And create instance
+ * by this framework.
+ * @param {string} id
+ * @param {string} code
+ * @param {object} config
+ * @param {object} data
+ */
+function createInstance (id, code, config, data) {
+  let info = instanceMap[id]
+
+  if (!info) {
+    // Init instance info.
+    info = checkVersion(code) || {}
+    if (!frameworks[info.framework]) {
+      info.framework = 'Weex'
+    }
+
+    // Init instance config.
+    config = JSON.parse(JSON.stringify(config || {}))
+    config.bundleVersion = info.version
+    config.env = JSON.parse(JSON.stringify(global.WXEnvironment || {}))
+    console.debug(`[JS Framework] create an ${info.framework}@${config.bundleVersion} instance from ${config.bundleVersion}`)
+
+    const env = {
+      info,
+      config,
+      created: Date.now(),
+      framework: info.framework
+    }
+    env.services = createServices(id, env, runtimeConfig)
+    instanceMap[id] = env
+
+    return frameworks[info.framework].createInstance(id, code, config, data, env)
+  }
+  return new Error(`invalid instance id "${id}"`)
+}
+
+const methods = {
+  createInstance,
+  registerService: register,
+  unregisterService: unregister
+}
+
+/**
+ * Register methods which init each frameworks.
+ * @param {string} methodName
+ */
+function genInit (methodName) {
+  methods[methodName] = function (...args) {
+    if (methodName === 'registerComponents') {
+      checkComponentMethods(args[0])
+    }
+    for (const name in frameworks) {
+      const framework = frameworks[name]
+      if (framework && framework[methodName]) {
+        framework[methodName](...args)
+      }
+    }
+  }
+}
+
+function checkComponentMethods (components) {
+  if (Array.isArray(components)) {
+    components.forEach((name) => {
+      if (name && name.type && name.methods) {
+        registerElement(name.type, name.methods)
+      }
+    })
+  }
+}
+
+/**
+ * Register methods which will be called for each instance.
+ * @param {string} methodName
+ */
+function genInstance (methodName) {
+  methods[methodName] = function (...args) {
+    const id = args[0]
+    const info = instanceMap[id]
+    if (info && frameworks[info.framework]) {
+      const result = frameworks[info.framework][methodName](...args)
+
+      // Lifecycle methods
+      if (methodName === 'refreshInstance') {
+        services.forEach(service => {
+          const refresh = service.options.refresh
+          if (refresh) {
+            refresh(id, { info, runtime: runtimeConfig })
+          }
+        })
+      }
+      else if (methodName === 'destroyInstance') {
+        services.forEach(service => {
+          const destroy = service.options.destroy
+          if (destroy) {
+            destroy(id, { info, runtime: runtimeConfig })
+          }
+        })
+        delete instanceMap[id]
+      }
+
+      return result
+    }
+    return new Error(`invalid instance id "${id}"`)
+  }
+}
+
+/**
+ * Adapt some legacy method(s) which will be called for each instance. These
+ * methods should be deprecated and removed later.
+ * @param {string} methodName
+ * @param {string} nativeMethodName
+ */
+function adaptInstance (methodName, nativeMethodName) {
+  methods[nativeMethodName] = function (...args) {
+    const id = args[0]
+    const info = instanceMap[id]
+    if (info && frameworks[info.framework]) {
+      return frameworks[info.framework][methodName](...args)
+    }
+    return new Error(`invalid instance id "${id}"`)
+  }
+}
+
+export default function init (config) {
+  runtimeConfig = config || {}
+  frameworks = runtimeConfig.frameworks || {}
+  initTaskHandler()
+
+  // Init each framework by `init` method and `config` which contains three
+  // virtual-DOM Class: `Document`, `Element` & `Comment`, and a JS bridge method:
+  // `sendTasks(...args)`.
+  for (const name in frameworks) {
+    const framework = frameworks[name]
+    framework.init(config)
+  }
+
+  // @todo: The method `registerMethods` will be re-designed or removed later.
+  ; ['registerComponents', 'registerModules', 'registerMethods'].forEach(genInit)
+
+  ; ['destroyInstance', 'refreshInstance', 'receiveTasks', 'getRoot'].forEach(genInstance)
+
+  adaptInstance('receiveTasks', 'callJS')
+
+  return methods
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/api/service.js
----------------------------------------------------------------------
diff --git a/html5/runtime/api/service.js b/html5/runtime/api/service.js
new file mode 100644
index 0000000..6f2e72e
--- /dev/null
+++ b/html5/runtime/api/service.js
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+// JS Services
+
+export const services = []
+
+/**
+ * Register a JavaScript service.
+ * A JavaScript service options could have a set of lifecycle methods
+ * for each Weex instance. For example: create, refresh, destroy.
+ * For the JS framework maintainer if you want to supply some features
+ * which need to work well in different Weex instances, even in different
+ * frameworks separately. You can make a JavaScript service to init
+ * its variables or classes for each Weex instance when it's created
+ * and recycle them when it's destroyed.
+ * @param {object} options Could have { create, refresh, destroy }
+ *                         lifecycle methods. In create method it should
+ *                         return an object of what variables or classes
+ *                         would be injected into the Weex instance.
+ */
+export function register (name, options) {
+  if (has(name)) {
+    console.warn(`Service "${name}" has been registered already!`)
+  }
+  else {
+    options = Object.assign({}, options)
+    services.push({ name, options })
+  }
+}
+
+/**
+ * Unregister a JavaScript service by name
+ * @param {string} name
+ */
+export function unregister (name) {
+  services.some((service, index) => {
+    if (service.name === name) {
+      services.splice(index, 1)
+      return true
+    }
+  })
+}
+
+/**
+ * Check if a JavaScript service with a certain name existed.
+ * @param  {string}  name
+ * @return {Boolean}
+ */
+export function has (name) {
+  return indexOf(name) >= 0
+}
+
+/**
+ * Find the index of a JavaScript service by name
+ * @param  {string} name
+ * @return {number}
+ */
+function indexOf (name) {
+  return services.map(service => service.name).indexOf(name)
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/bridge/CallbackManager.js
----------------------------------------------------------------------
diff --git a/html5/runtime/bridge/CallbackManager.js b/html5/runtime/bridge/CallbackManager.js
new file mode 100644
index 0000000..058fcc0
--- /dev/null
+++ b/html5/runtime/bridge/CallbackManager.js
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import { decodePrimitive } from './normalize'
+
+/**
+ * For general callback management of a certain Weex instance.
+ * Because function can not passed into native, so we create callback
+ * callback id for each function and pass the callback id into native
+ * in fact. And when a callback called from native, we can find the real
+ * callback through the callback id we have passed before.
+ */
+export default class CallbackManager {
+  constructor (instanceId) {
+    this.instanceId = instanceId
+    this.lastCallbackId = 0
+    this.callbacks = {}
+  }
+  add (callback) {
+    this.lastCallbackId++
+    this.callbacks[this.lastCallbackId] = callback
+    return this.lastCallbackId
+  }
+  remove (callbackId) {
+    const callback = this.callbacks[callbackId]
+    delete this.callbacks[callbackId]
+    return callback
+  }
+  consume (callbackId, data, ifKeepAlive) {
+    const callback = this.callbacks[callbackId]
+    if (typeof ifKeepAlive === 'undefined' || ifKeepAlive === false) {
+      delete this.callbacks[callbackId]
+    }
+    if (typeof callback === 'function') {
+      return callback(decodePrimitive(data))
+    }
+    return new Error(`invalid callback id "${callbackId}"`)
+  }
+  close () {
+    this.callbacks = {}
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/bridge/Handler.js
----------------------------------------------------------------------
diff --git a/html5/runtime/bridge/Handler.js b/html5/runtime/bridge/Handler.js
new file mode 100644
index 0000000..71bf4c5
--- /dev/null
+++ b/html5/runtime/bridge/Handler.js
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * @fileOverview
+ * Task handler for communication between javascript and native.
+ */
+
+const handlerMap = {
+  createBody: 'callCreateBody',
+  addElement: 'callAddElement',
+  removeElement: 'callRemoveElement',
+  moveElement: 'callMoveElement',
+  updateAttrs: 'callUpdateAttrs',
+  updateStyle: 'callUpdateStyle',
+  addEvent: 'callAddEvent',
+  removeEvent: 'callRemoveEvent'
+}
+
+/**
+ * Create a task handler.
+ * @param {string} id
+ * @param {function} handler
+ * @return {function} taskHandler
+ */
+export function createHandler (id, handler) {
+  const defaultHandler = handler || global.callNative
+
+  /* istanbul ignore if */
+  if (typeof defaultHandler !== 'function') {
+    console.error('[JS Runtime] no default handler')
+  }
+
+  return function taskHandler (tasks) {
+    /* istanbul ignore if */
+    if (!Array.isArray(tasks)) {
+      tasks = [tasks]
+    }
+    for (let i = 0; i < tasks.length; i++) {
+      const returnValue = dispatchTask(id, tasks[i], defaultHandler)
+      if (returnValue === -1) {
+        return returnValue
+      }
+    }
+  }
+}
+
+/**
+ * Check if there is a corresponding available handler in the environment.
+ * @param {string} module
+ * @param {string} method
+ * @return {boolean}
+ */
+function hasAvailableHandler (module, method) {
+  return module === 'dom'
+    && handlerMap[method]
+    && typeof global[handlerMap[method]] === 'function'
+}
+
+/**
+ * Dispatch the task to the specified handler.
+ * @param {string} id
+ * @param {object} task
+ * @param {function} defaultHandler
+ * @return {number} signal returned from native
+ */
+function dispatchTask (id, task, defaultHandler) {
+  const { module, method, args } = task
+
+  if (hasAvailableHandler(module, method)) {
+    return global[handlerMap[method]](id, ...args, '-1')
+  }
+
+  return defaultHandler(id, [task], '-1')
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/bridge/Listener.js
----------------------------------------------------------------------
diff --git a/html5/runtime/bridge/Listener.js b/html5/runtime/bridge/Listener.js
new file mode 100644
index 0000000..2bf3893
--- /dev/null
+++ b/html5/runtime/bridge/Listener.js
@@ -0,0 +1,220 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+* Create the action object.
+* @param {string} name
+* @param {array} arguments
+* @return {object} action
+*/
+function createAction (name, args = []) {
+  return { module: 'dom', method: name, args: args }
+}
+
+export default class Listener {
+  constructor (id, handler) {
+    this.id = id
+    this.batched = false
+    this.updates = []
+    if (typeof handler === 'function') {
+      Object.defineProperty(this, 'handler', {
+        configurable: true,
+        enumerable: true,
+        writable: true,
+        value: handler
+      })
+    }
+    else {
+      console.error('[JS Runtime] invalid parameter, handler must be a function')
+    }
+  }
+
+  /**
+   * Send the "createFinish" signal.
+   * @param {function} callback
+   * @return {undefined | number} the signal sent by native
+   */
+  createFinish (callback) {
+    const handler = this.handler
+    return handler([createAction('createFinish')], callback)
+  }
+
+  /**
+   * Send the "updateFinish" signal.
+   * @param {function} callback
+   * @return {undefined | number} the signal sent by native
+   */
+  updateFinish (callback) {
+    const handler = this.handler
+    return handler([createAction('updateFinish')], callback)
+  }
+
+  /**
+   * Send the "refreshFinish" signal.
+   * @param {function} callback
+   * @return {undefined | number} the signal sent by native
+   */
+  refreshFinish (callback) {
+    const handler = this.handler
+    return handler([createAction('refreshFinish')], callback)
+  }
+
+  /**
+   * Send the "createBody" signal.
+   * @param {object} element
+   * @return {undefined | number} the signal sent by native
+   */
+  createBody (element) {
+    const body = element.toJSON()
+    const children = body.children
+    delete body.children
+    const actions = [createAction('createBody', [body])]
+    if (children) {
+      actions.push.apply(actions, children.map(child => {
+        return createAction('addElement', [body.ref, child, -1])
+      }))
+    }
+    return this.addActions(actions)
+  }
+
+  /**
+   * Send the "addElement" signal.
+   * @param {object} element
+   * @param {stirng} reference id
+   * @param {number} index
+   * @return {undefined | number} the signal sent by native
+   */
+  addElement (element, ref, index) {
+    if (!(index >= 0)) {
+      index = -1
+    }
+    return this.addActions(createAction('addElement', [ref, element.toJSON(), index]))
+  }
+
+  /**
+   * Send the "removeElement" signal.
+   * @param {stirng} reference id
+   * @return {undefined | number} the signal sent by native
+   */
+  removeElement (ref) {
+    if (Array.isArray(ref)) {
+      const actions = ref.map((r) => createAction('removeElement', [r]))
+      return this.addActions(actions)
+    }
+    return this.addActions(createAction('removeElement', [ref]))
+  }
+
+  /**
+   * Send the "moveElement" signal.
+   * @param {stirng} target reference id
+   * @param {stirng} parent reference id
+   * @param {number} index
+   * @return {undefined | number} the signal sent by native
+   */
+  moveElement (targetRef, parentRef, index) {
+    return this.addActions(createAction('moveElement', [targetRef, parentRef, index]))
+  }
+
+  /**
+   * Send the "updateAttrs" signal.
+   * @param {stirng} reference id
+   * @param {stirng} key
+   * @param {stirng} value
+   * @return {undefined | number} the signal sent by native
+   */
+  setAttr (ref, key, value) {
+    const result = {}
+    result[key] = value
+    return this.addActions(createAction('updateAttrs', [ref, result]))
+  }
+
+  /**
+   * Send the "updateStyle" signal, update a sole style.
+   * @param {stirng} reference id
+   * @param {stirng} key
+   * @param {stirng} value
+   * @return {undefined | number} the signal sent by native
+   */
+  setStyle (ref, key, value) {
+    const result = {}
+    result[key] = value
+    return this.addActions(createAction('updateStyle', [ref, result]))
+  }
+
+  /**
+   * Send the "updateStyle" signal.
+   * @param {stirng} reference id
+   * @param {object} style
+   * @return {undefined | number} the signal sent by native
+   */
+  setStyles (ref, style) {
+    return this.addActions(createAction('updateStyle', [ref, style]))
+  }
+
+  /**
+   * Send the "addEvent" signal.
+   * @param {stirng} reference id
+   * @param {string} event type
+   * @return {undefined | number} the signal sent by native
+   */
+  addEvent (ref, type) {
+    return this.addActions(createAction('addEvent', [ref, type]))
+  }
+
+  /**
+   * Send the "removeEvent" signal.
+   * @param {stirng} reference id
+   * @param {string} event type
+   * @return {undefined | number} the signal sent by native
+   */
+  removeEvent (ref, type) {
+    return this.addActions(createAction('removeEvent', [ref, type]))
+  }
+
+  /**
+   * Default handler.
+   * @param {object | array} actions
+   * @param {function} callback
+   * @return {} anything returned by callback function
+   */
+  handler (actions, cb) {
+    return cb && cb()
+  }
+
+  /**
+   * Add actions into updates.
+   * @param {object | array} actions
+   * @return {undefined | number} the signal sent by native
+   */
+  addActions (actions) {
+    const updates = this.updates
+    const handler = this.handler
+
+    if (!Array.isArray(actions)) {
+      actions = [actions]
+    }
+
+    if (this.batched) {
+      updates.push.apply(updates, actions)
+    }
+    else {
+      return handler(actions)
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/bridge/TaskCenter.js
----------------------------------------------------------------------
diff --git a/html5/runtime/bridge/TaskCenter.js b/html5/runtime/bridge/TaskCenter.js
new file mode 100644
index 0000000..fd4f5ee
--- /dev/null
+++ b/html5/runtime/bridge/TaskCenter.js
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import CallbackManager from './CallbackManager'
+import Element from '../vdom/Element'
+import { typof } from '../utils'
+import { normalizePrimitive } from './normalize'
+
+let fallback = function () {}
+
+// The API of TaskCenter would be re-design.
+export class TaskCenter {
+  constructor (id, sendTasks) {
+    Object.defineProperty(this, 'instanceId', {
+      enumerable: true,
+      value: id
+    })
+    Object.defineProperty(this, 'callbackManager', {
+      enumerable: true,
+      value: new CallbackManager(id)
+    })
+    fallback = sendTasks || function () {}
+  }
+
+  callback (callbackId, data, ifKeepAlive) {
+    return this.callbackManager.consume(callbackId, data, ifKeepAlive)
+  }
+
+  destroyCallback () {
+    return this.callbackManager.close()
+  }
+
+  /**
+   * Normalize a value. Specially, if the value is a function, then generate a function id
+   * and save it to `CallbackManager`, at last return the function id.
+   * @param  {any}        v
+   * @return {primitive}
+   */
+  normalize (v) {
+    const type = typof(v)
+
+    if (v instanceof Element) {
+      return v.ref
+    }
+
+    if (v._isVue && v.$el instanceof Element) {
+      return v.$el.ref
+    }
+
+    if (type === 'Function') {
+      return this.callbackManager.add(v).toString()
+    }
+
+    return normalizePrimitive(v)
+  }
+
+  send (type, params, args, options) {
+    const { action, component, ref, module, method } = params
+
+    args = args.map(arg => this.normalize(arg))
+
+    switch (type) {
+      case 'dom':
+        return this[action](this.instanceId, args)
+      case 'component':
+        return this.componentHandler(this.instanceId, ref, method, args, Object.assign({ component }, options))
+      default:
+        return this.moduleHandler(this.instanceId, module, method, args, options)
+    }
+  }
+
+  callDOM (action, args) {
+    return this[action](this.instanceId, args)
+  }
+
+  callComponent (ref, method, args, options) {
+    return this.componentHandler(this.instanceId, ref, method, args, options)
+  }
+
+  callModule (module, method, args, options) {
+    return this.moduleHandler(this.instanceId, module, method, args, options)
+  }
+}
+
+export function init () {
+  const DOM_METHODS = {
+    createFinish: global.callCreateFinish,
+    updateFinish: global.callUpdateFinish,
+    refreshFinish: global.callRefreshFinish,
+
+    createBody: global.callCreateBody,
+
+    addElement: global.callAddElement,
+    removeElement: global.callRemoveElement,
+    moveElement: global.callMoveElement,
+    updateAttrs: global.callUpdateAttrs,
+    updateStyle: global.callUpdateStyle,
+
+    addEvent: global.callAddEvent,
+    removeEvent: global.callRemoveEvent
+  }
+  const proto = TaskCenter.prototype
+
+  for (const name in DOM_METHODS) {
+    const method = DOM_METHODS[name]
+    proto[name] = method ?
+      (id, args) => method(id, ...args) :
+      (id, args) => fallback(id, [{ module: 'dom', method: name, args }], '-1')
+  }
+
+  proto.componentHandler = global.callNativeComponent ||
+    ((id, ref, method, args, options) =>
+      fallback(id, [{ component: options.component, ref, method, args }]))
+
+  proto.moduleHandler = global.callNativeModule ||
+    ((id, module, method, args) =>
+      fallback(id, [{ module, method, args }]))
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/bridge/normalize.js
----------------------------------------------------------------------
diff --git a/html5/runtime/bridge/normalize.js b/html5/runtime/bridge/normalize.js
new file mode 100644
index 0000000..486167d
--- /dev/null
+++ b/html5/runtime/bridge/normalize.js
@@ -0,0 +1,95 @@
+import { typof } from '../utils'
+
+export function bufferToBase64 (buffer) {
+  if (typeof btoa !== 'function') {
+    return ''
+  }
+  const string = Array.prototype.map.call(
+    new Uint8Array(buffer),
+    code => String.fromCharCode(code)
+  ).join('')
+  return btoa(string) // eslint-disable-line no-undef
+}
+
+export function base64ToBuffer (base64) {
+  if (typeof atob !== 'function') {
+    return new ArrayBuffer(0)
+  }
+  const string = atob(base64) // eslint-disable-line no-undef
+  const array = new Uint8Array(string.length)
+  Array.prototype.forEach.call(string, (ch, i) => {
+    array[i] = ch.charCodeAt(0)
+  })
+  return array.buffer
+}
+
+/**
+ * Normalize a primitive value.
+ * @param  {any}        v
+ * @return {primitive}
+ */
+export function normalizePrimitive (v) {
+  const 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':
+      return v
+
+    case 'ArrayBuffer':
+      return {
+        '@type': 'binary',
+        dataType: type,
+        base64: bufferToBase64(v)
+      }
+
+    case 'Int8Array':
+    case 'Uint8Array':
+    case 'Uint8ClampedArray':
+    case 'Int16Array':
+    case 'Uint16Array':
+    case 'Int32Array':
+    case 'Uint32Array':
+    case 'Float32Array':
+    case 'Float64Array':
+      return {
+        '@type': 'binary',
+        dataType: type,
+        base64: bufferToBase64(v.buffer)
+      }
+
+    default:
+      return JSON.stringify(v)
+  }
+}
+
+export function decodePrimitive (data) {
+  if (typof(data) === 'Object') {
+    // decode base64 into binary
+    if (data['@type'] && data['@type'] === 'binary') {
+      return base64ToBuffer(data.base64 || '')
+    }
+
+    const realData = {}
+    for (const key in data) {
+      realData[key] = decodePrimitive(data[key])
+    }
+    return realData
+  }
+  if (typof(data) === 'Array') {
+    return data.map(decodePrimitive)
+  }
+  return data
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/callback-manager.js
----------------------------------------------------------------------
diff --git a/html5/runtime/callback-manager.js b/html5/runtime/callback-manager.js
deleted file mode 100644
index 058fcc0..0000000
--- a/html5/runtime/callback-manager.js
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { decodePrimitive } from './normalize'
-
-/**
- * For general callback management of a certain Weex instance.
- * Because function can not passed into native, so we create callback
- * callback id for each function and pass the callback id into native
- * in fact. And when a callback called from native, we can find the real
- * callback through the callback id we have passed before.
- */
-export default class CallbackManager {
-  constructor (instanceId) {
-    this.instanceId = instanceId
-    this.lastCallbackId = 0
-    this.callbacks = {}
-  }
-  add (callback) {
-    this.lastCallbackId++
-    this.callbacks[this.lastCallbackId] = callback
-    return this.lastCallbackId
-  }
-  remove (callbackId) {
-    const callback = this.callbacks[callbackId]
-    delete this.callbacks[callbackId]
-    return callback
-  }
-  consume (callbackId, data, ifKeepAlive) {
-    const callback = this.callbacks[callbackId]
-    if (typeof ifKeepAlive === 'undefined' || ifKeepAlive === false) {
-      delete this.callbacks[callbackId]
-    }
-    if (typeof callback === 'function') {
-      return callback(decodePrimitive(data))
-    }
-    return new Error(`invalid callback id "${callbackId}"`)
-  }
-  close () {
-    this.callbacks = {}
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/config.js
----------------------------------------------------------------------
diff --git a/html5/runtime/config.js b/html5/runtime/config.js
deleted file mode 100644
index 695aa66..0000000
--- a/html5/runtime/config.js
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { Document, Element, Comment } from './vdom'
-import Listener from './listener'
-import { TaskCenter } from './task-center'
-
-const config = {
-  Document, Element, Comment, Listener,
-  TaskCenter,
-  sendTasks (...args) {
-    if (typeof callNative === 'function') {
-      return callNative(...args)
-    }
-    return (global.callNative || (() => {}))(...args)
-  }
-}
-
-Document.handler = config.sendTasks
-
-export default config

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/handler.js
----------------------------------------------------------------------
diff --git a/html5/runtime/handler.js b/html5/runtime/handler.js
deleted file mode 100644
index 71bf4c5..0000000
--- a/html5/runtime/handler.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/**
- * @fileOverview
- * Task handler for communication between javascript and native.
- */
-
-const handlerMap = {
-  createBody: 'callCreateBody',
-  addElement: 'callAddElement',
-  removeElement: 'callRemoveElement',
-  moveElement: 'callMoveElement',
-  updateAttrs: 'callUpdateAttrs',
-  updateStyle: 'callUpdateStyle',
-  addEvent: 'callAddEvent',
-  removeEvent: 'callRemoveEvent'
-}
-
-/**
- * Create a task handler.
- * @param {string} id
- * @param {function} handler
- * @return {function} taskHandler
- */
-export function createHandler (id, handler) {
-  const defaultHandler = handler || global.callNative
-
-  /* istanbul ignore if */
-  if (typeof defaultHandler !== 'function') {
-    console.error('[JS Runtime] no default handler')
-  }
-
-  return function taskHandler (tasks) {
-    /* istanbul ignore if */
-    if (!Array.isArray(tasks)) {
-      tasks = [tasks]
-    }
-    for (let i = 0; i < tasks.length; i++) {
-      const returnValue = dispatchTask(id, tasks[i], defaultHandler)
-      if (returnValue === -1) {
-        return returnValue
-      }
-    }
-  }
-}
-
-/**
- * Check if there is a corresponding available handler in the environment.
- * @param {string} module
- * @param {string} method
- * @return {boolean}
- */
-function hasAvailableHandler (module, method) {
-  return module === 'dom'
-    && handlerMap[method]
-    && typeof global[handlerMap[method]] === 'function'
-}
-
-/**
- * Dispatch the task to the specified handler.
- * @param {string} id
- * @param {object} task
- * @param {function} defaultHandler
- * @return {number} signal returned from native
- */
-function dispatchTask (id, task, defaultHandler) {
-  const { module, method, args } = task
-
-  if (hasAvailableHandler(module, method)) {
-    return global[handlerMap[method]](id, ...args, '-1')
-  }
-
-  return defaultHandler(id, [task], '-1')
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/index.js
----------------------------------------------------------------------
diff --git a/html5/runtime/index.js b/html5/runtime/index.js
index 25810ac..85b7525 100644
--- a/html5/runtime/index.js
+++ b/html5/runtime/index.js
@@ -26,14 +26,9 @@
 
 import * as shared from '../shared'
 
-import init from './init'
-import config from './config'
-
-import {
-  register,
-  unregister,
-  has
-} from './service'
+import init from './api/init'
+import config from './api/config'
+import { register, unregister, has } from './api/service'
 
 /* istanbul ignore next */
 function freezePrototype () {

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/init.js
----------------------------------------------------------------------
diff --git a/html5/runtime/init.js b/html5/runtime/init.js
deleted file mode 100644
index ad46a3e..0000000
--- a/html5/runtime/init.js
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import { init as initTaskHandler } from './task-center'
-import { registerElement } from './vdom/element-types'
-import { services, register, unregister } from './service'
-
-let frameworks
-let runtimeConfig
-
-const versionRegExp = /^\s*\/\/ *(\{[^}]*\}) *\r?\n/
-
-/**
- * Detect a JS Bundle code and make sure which framework it's based to. Each JS
- * Bundle should make sure that it starts with a line of JSON comment and is
- * more that one line.
- * @param  {string} code
- * @return {object}
- */
-function checkVersion (code) {
-  let info
-  const result = versionRegExp.exec(code)
-  if (result) {
-    try {
-      info = JSON.parse(result[1])
-    }
-    catch (e) {}
-  }
-  return info
-}
-
-function createServices (id, env, config) {
-  // Init JavaScript services for this instance.
-  const serviceMap = Object.create(null)
-  serviceMap.service = Object.create(null)
-  services.forEach(({ name, options }) => {
-    if (process.env.NODE_ENV === 'development') {
-      console.debug(`[JS Runtime] create service ${name}.`)
-    }
-    const create = options.create
-    if (create) {
-      const result = create(id, env, config)
-      Object.assign(serviceMap.service, result)
-      Object.assign(serviceMap, result.instance)
-    }
-  })
-  delete serviceMap.service.instance
-  Object.freeze(serviceMap.service)
-  return serviceMap
-}
-
-const instanceMap = {}
-
-/**
- * Check which framework a certain JS Bundle code based to. And create instance
- * by this framework.
- * @param {string} id
- * @param {string} code
- * @param {object} config
- * @param {object} data
- */
-function createInstance (id, code, config, data) {
-  let info = instanceMap[id]
-
-  if (!info) {
-    // Init instance info.
-    info = checkVersion(code) || {}
-    if (!frameworks[info.framework]) {
-      info.framework = 'Weex'
-    }
-
-    // Init instance config.
-    config = JSON.parse(JSON.stringify(config || {}))
-    config.bundleVersion = info.version
-    config.env = JSON.parse(JSON.stringify(global.WXEnvironment || {}))
-    console.debug(`[JS Framework] create an ${info.framework}@${config.bundleVersion} instance from ${config.bundleVersion}`)
-
-    const env = {
-      info,
-      config,
-      created: Date.now(),
-      framework: info.framework
-    }
-    env.services = createServices(id, env, runtimeConfig)
-    instanceMap[id] = env
-
-    return frameworks[info.framework].createInstance(id, code, config, data, env)
-  }
-  return new Error(`invalid instance id "${id}"`)
-}
-
-const methods = {
-  createInstance,
-  registerService: register,
-  unregisterService: unregister
-}
-
-/**
- * Register methods which init each frameworks.
- * @param {string} methodName
- */
-function genInit (methodName) {
-  methods[methodName] = function (...args) {
-    if (methodName === 'registerComponents') {
-      checkComponentMethods(args[0])
-    }
-    for (const name in frameworks) {
-      const framework = frameworks[name]
-      if (framework && framework[methodName]) {
-        framework[methodName](...args)
-      }
-    }
-  }
-}
-
-function checkComponentMethods (components) {
-  if (Array.isArray(components)) {
-    components.forEach((name) => {
-      if (name && name.type && name.methods) {
-        registerElement(name.type, name.methods)
-      }
-    })
-  }
-}
-
-/**
- * Register methods which will be called for each instance.
- * @param {string} methodName
- */
-function genInstance (methodName) {
-  methods[methodName] = function (...args) {
-    const id = args[0]
-    const info = instanceMap[id]
-    if (info && frameworks[info.framework]) {
-      const result = frameworks[info.framework][methodName](...args)
-
-      // Lifecycle methods
-      if (methodName === 'refreshInstance') {
-        services.forEach(service => {
-          const refresh = service.options.refresh
-          if (refresh) {
-            refresh(id, { info, runtime: runtimeConfig })
-          }
-        })
-      }
-      else if (methodName === 'destroyInstance') {
-        services.forEach(service => {
-          const destroy = service.options.destroy
-          if (destroy) {
-            destroy(id, { info, runtime: runtimeConfig })
-          }
-        })
-        delete instanceMap[id]
-      }
-
-      return result
-    }
-    return new Error(`invalid instance id "${id}"`)
-  }
-}
-
-/**
- * Adapt some legacy method(s) which will be called for each instance. These
- * methods should be deprecated and removed later.
- * @param {string} methodName
- * @param {string} nativeMethodName
- */
-function adaptInstance (methodName, nativeMethodName) {
-  methods[nativeMethodName] = function (...args) {
-    const id = args[0]
-    const info = instanceMap[id]
-    if (info && frameworks[info.framework]) {
-      return frameworks[info.framework][methodName](...args)
-    }
-    return new Error(`invalid instance id "${id}"`)
-  }
-}
-
-export default function init (config) {
-  runtimeConfig = config || {}
-  frameworks = runtimeConfig.frameworks || {}
-  initTaskHandler()
-
-  // Init each framework by `init` method and `config` which contains three
-  // virtual-DOM Class: `Document`, `Element` & `Comment`, and a JS bridge method:
-  // `sendTasks(...args)`.
-  for (const name in frameworks) {
-    const framework = frameworks[name]
-    framework.init(config)
-  }
-
-  // @todo: The method `registerMethods` will be re-designed or removed later.
-  ; ['registerComponents', 'registerModules', 'registerMethods'].forEach(genInit)
-
-  ; ['destroyInstance', 'refreshInstance', 'receiveTasks', 'getRoot'].forEach(genInstance)
-
-  adaptInstance('receiveTasks', 'callJS')
-
-  return methods
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/listener.js
----------------------------------------------------------------------
diff --git a/html5/runtime/listener.js b/html5/runtime/listener.js
deleted file mode 100644
index 2bf3893..0000000
--- a/html5/runtime/listener.js
+++ /dev/null
@@ -1,220 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/**
-* Create the action object.
-* @param {string} name
-* @param {array} arguments
-* @return {object} action
-*/
-function createAction (name, args = []) {
-  return { module: 'dom', method: name, args: args }
-}
-
-export default class Listener {
-  constructor (id, handler) {
-    this.id = id
-    this.batched = false
-    this.updates = []
-    if (typeof handler === 'function') {
-      Object.defineProperty(this, 'handler', {
-        configurable: true,
-        enumerable: true,
-        writable: true,
-        value: handler
-      })
-    }
-    else {
-      console.error('[JS Runtime] invalid parameter, handler must be a function')
-    }
-  }
-
-  /**
-   * Send the "createFinish" signal.
-   * @param {function} callback
-   * @return {undefined | number} the signal sent by native
-   */
-  createFinish (callback) {
-    const handler = this.handler
-    return handler([createAction('createFinish')], callback)
-  }
-
-  /**
-   * Send the "updateFinish" signal.
-   * @param {function} callback
-   * @return {undefined | number} the signal sent by native
-   */
-  updateFinish (callback) {
-    const handler = this.handler
-    return handler([createAction('updateFinish')], callback)
-  }
-
-  /**
-   * Send the "refreshFinish" signal.
-   * @param {function} callback
-   * @return {undefined | number} the signal sent by native
-   */
-  refreshFinish (callback) {
-    const handler = this.handler
-    return handler([createAction('refreshFinish')], callback)
-  }
-
-  /**
-   * Send the "createBody" signal.
-   * @param {object} element
-   * @return {undefined | number} the signal sent by native
-   */
-  createBody (element) {
-    const body = element.toJSON()
-    const children = body.children
-    delete body.children
-    const actions = [createAction('createBody', [body])]
-    if (children) {
-      actions.push.apply(actions, children.map(child => {
-        return createAction('addElement', [body.ref, child, -1])
-      }))
-    }
-    return this.addActions(actions)
-  }
-
-  /**
-   * Send the "addElement" signal.
-   * @param {object} element
-   * @param {stirng} reference id
-   * @param {number} index
-   * @return {undefined | number} the signal sent by native
-   */
-  addElement (element, ref, index) {
-    if (!(index >= 0)) {
-      index = -1
-    }
-    return this.addActions(createAction('addElement', [ref, element.toJSON(), index]))
-  }
-
-  /**
-   * Send the "removeElement" signal.
-   * @param {stirng} reference id
-   * @return {undefined | number} the signal sent by native
-   */
-  removeElement (ref) {
-    if (Array.isArray(ref)) {
-      const actions = ref.map((r) => createAction('removeElement', [r]))
-      return this.addActions(actions)
-    }
-    return this.addActions(createAction('removeElement', [ref]))
-  }
-
-  /**
-   * Send the "moveElement" signal.
-   * @param {stirng} target reference id
-   * @param {stirng} parent reference id
-   * @param {number} index
-   * @return {undefined | number} the signal sent by native
-   */
-  moveElement (targetRef, parentRef, index) {
-    return this.addActions(createAction('moveElement', [targetRef, parentRef, index]))
-  }
-
-  /**
-   * Send the "updateAttrs" signal.
-   * @param {stirng} reference id
-   * @param {stirng} key
-   * @param {stirng} value
-   * @return {undefined | number} the signal sent by native
-   */
-  setAttr (ref, key, value) {
-    const result = {}
-    result[key] = value
-    return this.addActions(createAction('updateAttrs', [ref, result]))
-  }
-
-  /**
-   * Send the "updateStyle" signal, update a sole style.
-   * @param {stirng} reference id
-   * @param {stirng} key
-   * @param {stirng} value
-   * @return {undefined | number} the signal sent by native
-   */
-  setStyle (ref, key, value) {
-    const result = {}
-    result[key] = value
-    return this.addActions(createAction('updateStyle', [ref, result]))
-  }
-
-  /**
-   * Send the "updateStyle" signal.
-   * @param {stirng} reference id
-   * @param {object} style
-   * @return {undefined | number} the signal sent by native
-   */
-  setStyles (ref, style) {
-    return this.addActions(createAction('updateStyle', [ref, style]))
-  }
-
-  /**
-   * Send the "addEvent" signal.
-   * @param {stirng} reference id
-   * @param {string} event type
-   * @return {undefined | number} the signal sent by native
-   */
-  addEvent (ref, type) {
-    return this.addActions(createAction('addEvent', [ref, type]))
-  }
-
-  /**
-   * Send the "removeEvent" signal.
-   * @param {stirng} reference id
-   * @param {string} event type
-   * @return {undefined | number} the signal sent by native
-   */
-  removeEvent (ref, type) {
-    return this.addActions(createAction('removeEvent', [ref, type]))
-  }
-
-  /**
-   * Default handler.
-   * @param {object | array} actions
-   * @param {function} callback
-   * @return {} anything returned by callback function
-   */
-  handler (actions, cb) {
-    return cb && cb()
-  }
-
-  /**
-   * Add actions into updates.
-   * @param {object | array} actions
-   * @return {undefined | number} the signal sent by native
-   */
-  addActions (actions) {
-    const updates = this.updates
-    const handler = this.handler
-
-    if (!Array.isArray(actions)) {
-      actions = [actions]
-    }
-
-    if (this.batched) {
-      updates.push.apply(updates, actions)
-    }
-    else {
-      return handler(actions)
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/normalize.js
----------------------------------------------------------------------
diff --git a/html5/runtime/normalize.js b/html5/runtime/normalize.js
deleted file mode 100644
index f1e93b8..0000000
--- a/html5/runtime/normalize.js
+++ /dev/null
@@ -1,98 +0,0 @@
-export function typof (v) {
-  const s = Object.prototype.toString.call(v)
-  return s.substring(8, s.length - 1)
-}
-
-export function bufferToBase64 (buffer) {
-  if (typeof btoa !== 'function') {
-    return ''
-  }
-  const string = Array.prototype.map.call(
-    new Uint8Array(buffer),
-    code => String.fromCharCode(code)
-  ).join('')
-  return btoa(string) // eslint-disable-line no-undef
-}
-
-export function base64ToBuffer (base64) {
-  if (typeof atob !== 'function') {
-    return new ArrayBuffer(0)
-  }
-  const string = atob(base64) // eslint-disable-line no-undef
-  const array = new Uint8Array(string.length)
-  Array.prototype.forEach.call(string, (ch, i) => {
-    array[i] = ch.charCodeAt(0)
-  })
-  return array.buffer
-}
-
-/**
- * Normalize a primitive value.
- * @param  {any}        v
- * @return {primitive}
- */
-export function normalizePrimitive (v) {
-  const 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':
-      return v
-
-    case 'ArrayBuffer':
-      return {
-        '@type': 'binary',
-        dataType: type,
-        base64: bufferToBase64(v)
-      }
-
-    case 'Int8Array':
-    case 'Uint8Array':
-    case 'Uint8ClampedArray':
-    case 'Int16Array':
-    case 'Uint16Array':
-    case 'Int32Array':
-    case 'Uint32Array':
-    case 'Float32Array':
-    case 'Float64Array':
-      return {
-        '@type': 'binary',
-        dataType: type,
-        base64: bufferToBase64(v.buffer)
-      }
-
-    default:
-      return JSON.stringify(v)
-  }
-}
-
-export function decodePrimitive (data) {
-  if (typof(data) === 'Object') {
-    // decode base64 into binary
-    if (data['@type'] && data['@type'] === 'binary') {
-      return base64ToBuffer(data.base64 || '')
-    }
-
-    const realData = {}
-    for (const key in data) {
-      realData[key] = decodePrimitive(data[key])
-    }
-    return realData
-  }
-  if (typof(data) === 'Array') {
-    return data.map(decodePrimitive)
-  }
-  return data
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/service.js
----------------------------------------------------------------------
diff --git a/html5/runtime/service.js b/html5/runtime/service.js
deleted file mode 100644
index 6f2e72e..0000000
--- a/html5/runtime/service.js
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-// JS Services
-
-export const services = []
-
-/**
- * Register a JavaScript service.
- * A JavaScript service options could have a set of lifecycle methods
- * for each Weex instance. For example: create, refresh, destroy.
- * For the JS framework maintainer if you want to supply some features
- * which need to work well in different Weex instances, even in different
- * frameworks separately. You can make a JavaScript service to init
- * its variables or classes for each Weex instance when it's created
- * and recycle them when it's destroyed.
- * @param {object} options Could have { create, refresh, destroy }
- *                         lifecycle methods. In create method it should
- *                         return an object of what variables or classes
- *                         would be injected into the Weex instance.
- */
-export function register (name, options) {
-  if (has(name)) {
-    console.warn(`Service "${name}" has been registered already!`)
-  }
-  else {
-    options = Object.assign({}, options)
-    services.push({ name, options })
-  }
-}
-
-/**
- * Unregister a JavaScript service by name
- * @param {string} name
- */
-export function unregister (name) {
-  services.some((service, index) => {
-    if (service.name === name) {
-      services.splice(index, 1)
-      return true
-    }
-  })
-}
-
-/**
- * Check if a JavaScript service with a certain name existed.
- * @param  {string}  name
- * @return {Boolean}
- */
-export function has (name) {
-  return indexOf(name) >= 0
-}
-
-/**
- * Find the index of a JavaScript service by name
- * @param  {string} name
- * @return {number}
- */
-function indexOf (name) {
-  return services.map(service => service.name).indexOf(name)
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/task-center.js
----------------------------------------------------------------------
diff --git a/html5/runtime/task-center.js b/html5/runtime/task-center.js
deleted file mode 100644
index b2f960f..0000000
--- a/html5/runtime/task-center.js
+++ /dev/null
@@ -1,132 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-import CallbackManager from './callback-manager'
-import Element from './vdom/element'
-import { typof, normalizePrimitive } from './normalize'
-
-let fallback = function () {}
-
-// The API of TaskCenter would be re-design.
-export class TaskCenter {
-  constructor (id, sendTasks) {
-    Object.defineProperty(this, 'instanceId', {
-      enumerable: true,
-      value: id
-    })
-    Object.defineProperty(this, 'callbackManager', {
-      enumerable: true,
-      value: new CallbackManager(id)
-    })
-    fallback = sendTasks || function () {}
-  }
-
-  callback (callbackId, data, ifKeepAlive) {
-    return this.callbackManager.consume(callbackId, data, ifKeepAlive)
-  }
-
-  destroyCallback () {
-    return this.callbackManager.close()
-  }
-
-  /**
-   * Normalize a value. Specially, if the value is a function, then generate a function id
-   * and save it to `CallbackManager`, at last return the function id.
-   * @param  {any}        v
-   * @return {primitive}
-   */
-  normalize (v) {
-    const type = typof(v)
-
-    if (v instanceof Element) {
-      return v.ref
-    }
-
-    if (v._isVue && v.$el instanceof Element) {
-      return v.$el.ref
-    }
-
-    if (type === 'Function') {
-      return this.callbackManager.add(v).toString()
-    }
-
-    return normalizePrimitive(v)
-  }
-
-  send (type, params, args, options) {
-    const { action, component, ref, module, method } = params
-
-    args = args.map(arg => this.normalize(arg))
-
-    switch (type) {
-      case 'dom':
-        return this[action](this.instanceId, args)
-      case 'component':
-        return this.componentHandler(this.instanceId, ref, method, args, Object.assign({ component }, options))
-      default:
-        return this.moduleHandler(this.instanceId, module, method, args, options)
-    }
-  }
-
-  callDOM (action, args) {
-    return this[action](this.instanceId, args)
-  }
-
-  callComponent (ref, method, args, options) {
-    return this.componentHandler(this.instanceId, ref, method, args, options)
-  }
-
-  callModule (module, method, args, options) {
-    return this.moduleHandler(this.instanceId, module, method, args, options)
-  }
-}
-
-export function init () {
-  const DOM_METHODS = {
-    createFinish: global.callCreateFinish,
-    updateFinish: global.callUpdateFinish,
-    refreshFinish: global.callRefreshFinish,
-
-    createBody: global.callCreateBody,
-
-    addElement: global.callAddElement,
-    removeElement: global.callRemoveElement,
-    moveElement: global.callMoveElement,
-    updateAttrs: global.callUpdateAttrs,
-    updateStyle: global.callUpdateStyle,
-
-    addEvent: global.callAddEvent,
-    removeEvent: global.callRemoveEvent
-  }
-  const proto = TaskCenter.prototype
-
-  for (const name in DOM_METHODS) {
-    const method = DOM_METHODS[name]
-    proto[name] = method ?
-      (id, args) => method(id, ...args) :
-      (id, args) => fallback(id, [{ module: 'dom', method: name, args }], '-1')
-  }
-
-  proto.componentHandler = global.callNativeComponent ||
-    ((id, ref, method, args, options) =>
-      fallback(id, [{ component: options.component, ref, method, args }]))
-
-  proto.moduleHandler = global.callNativeModule ||
-    ((id, module, method, args) =>
-      fallback(id, [{ module, method, args }]))
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/utils.js
----------------------------------------------------------------------
diff --git a/html5/runtime/utils.js b/html5/runtime/utils.js
new file mode 100644
index 0000000..e91e354
--- /dev/null
+++ b/html5/runtime/utils.js
@@ -0,0 +1,13 @@
+
+/**
+ * Get a unique id.
+ */
+let nextNodeRef = 1
+export function uniqueId () {
+  return (nextNodeRef++).toString()
+}
+
+export function typof (v) {
+  const s = Object.prototype.toString.call(v)
+  return s.substring(8, s.length - 1)
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/vdom/comment.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/comment.js b/html5/runtime/vdom/comment.js
index 24d3482..d7ab115 100644
--- a/html5/runtime/vdom/comment.js
+++ b/html5/runtime/vdom/comment.js
@@ -17,8 +17,8 @@
  * under the License.
  */
 
-import Node from './node'
-import { uniqueId } from './operation'
+import Node from './Node'
+import { uniqueId } from '../utils'
 
 export default class Comment extends Node {
   constructor (value) {

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/vdom/directive.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/directive.js b/html5/runtime/vdom/directive.js
index 97371ca..66f33c7 100644
--- a/html5/runtime/vdom/directive.js
+++ b/html5/runtime/vdom/directive.js
@@ -1,7 +1,4 @@
-function typof (v) {
-  const s = Object.prototype.toString.call(v)
-  return s.substring(8, s.length - 1)
-}
+import { typof } from '../utils'
 
 // match the binding delimiter
 const delimiterRE = /\[\[((?:.|\n)+?)\]\]/g

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/vdom/document.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/document.js b/html5/runtime/vdom/document.js
index 64b6210..58efa5d 100644
--- a/html5/runtime/vdom/document.js
+++ b/html5/runtime/vdom/document.js
@@ -17,11 +17,11 @@
  * under the License.
  */
 
-import Comment from './comment'
-import Element from './element'
-import Listener from '../listener'
-import { TaskCenter } from '../task-center'
-import { createHandler } from '../handler'
+import Comment from './Comment'
+import Element from './Element'
+import Listener from '../bridge/Listener'
+import { TaskCenter } from '../bridge/TaskCenter'
+import { createHandler } from '../bridge/Handler'
 import { addDoc, removeDoc, appendBody, setBody } from './operation'
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/vdom/element.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/element.js b/html5/runtime/vdom/element.js
index 5f5268c..d0115a5 100644
--- a/html5/runtime/vdom/element.js
+++ b/html5/runtime/vdom/element.js
@@ -17,11 +17,10 @@
  * under the License.
  */
 
-import Node from './node'
+import Node from './Node'
 import {
   getDoc,
   getTaskCenter,
-  uniqueId,
   linkParent,
   nextElement,
   previousElement,
@@ -29,6 +28,7 @@ import {
   moveIndex,
   removeIndex
 } from './operation'
+import { uniqueId } from '../utils'
 import { elementTypes, setElement } from './element-types'
 import { filterDirective } from './directive'
 

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/vdom/index.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/index.js b/html5/runtime/vdom/index.js
index 119a761..2c979e8 100644
--- a/html5/runtime/vdom/index.js
+++ b/html5/runtime/vdom/index.js
@@ -16,10 +16,10 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import Node from './node'
-import Element from './element'
-import Comment from './comment'
-import Document from './document'
+import Node from './Node'
+import Element from './Element'
+import Comment from './Comment'
+import Document from './Document'
 
 export {
   elementTypes,

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/vdom/node.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/node.js b/html5/runtime/vdom/node.js
index f36d0a5..645f7df 100644
--- a/html5/runtime/vdom/node.js
+++ b/html5/runtime/vdom/node.js
@@ -17,7 +17,8 @@
  * under the License.
  */
 
-import { getDoc, uniqueId } from './operation'
+import { uniqueId } from '../utils'
+import { getDoc } from './operation'
 
 export default class Node {
   constructor () {

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/0729ac59/html5/runtime/vdom/operation.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/operation.js b/html5/runtime/vdom/operation.js
index a29ac4c..460732a 100644
--- a/html5/runtime/vdom/operation.js
+++ b/html5/runtime/vdom/operation.js
@@ -74,14 +74,6 @@ export function getTaskCenter (id) {
 }
 
 /**
- * Get a unique id.
- */
-let nextNodeRef = 1
-export function uniqueId () {
-  return (nextNodeRef++).toString()
-}
-
-/**
  * Append body node to documentElement.
  * @param {object} document
  * @param {object} node