text: import Vue from 'weex/runtime/index'
  import renderer from 'weex/runtime/config'
  
  Vue.weexVersion = '__WEEX_VERSION__'
  export { Vue }
  
  const {
    instances,
    modules,
    components
  } = renderer
  
  let activeId
  const oriIsReservedTag = (Vue && Vue.config && typeof Vue.config.isReservedTag === 'function') ? Vue.config.isReservedTag : function () {}
  
  /**
   * Prepare framework config, basically about the virtual-DOM and JS bridge.
   * @param {object} cfg
   */
  export function init (cfg) {
    renderer.Document = cfg.Document
    renderer.Element = cfg.Element
    renderer.Comment = cfg.Comment
    renderer.sendTasks = cfg.sendTasks
  }
  
  /**
   * Reset framework config and clear all registrations.
   */
  export function reset () {
    clear(instances)
    clear(modules)
    clear(components)
    delete renderer.Document
    delete renderer.Element
    delete renderer.Comment
    delete renderer.sendTasks
    Vue.config.isReservedTag = oriIsReservedTag
  }
  
  /**
   * Delete all keys of an object.
   * @param {object} obj
   */
  function clear (obj) {
    for (const key in obj) {
      delete obj[key]
    }
  }
  
  /**
   * Create an instance with id, code, config and external data.
   * @param {string} instanceId
   * @param {string} appCode
   * @param {object} config
   * @param {object} data
   * @param {object} env { info, config, services }
   */
  export function createInstance (
    instanceId,
    appCode = '',
    config = {},
    data,
    env = {}
  ) {
    // Set active instance id and put some information into `instances` map.
    activeId = instanceId
  
    // Virtual-DOM object.
    const 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.
    const callbacks = []
  
    // The latest callback id, incremental.
    const callbackId = 1
  
    instances[instanceId] = {
      instanceId, config, data,
      document, callbacks, callbackId
    }
  
    // Prepare native module getter and HTML5 Timer APIs.
    const moduleGetter = genModuleGetter(instanceId)
    const timerAPIs = getInstanceTimer(instanceId, moduleGetter)
  
    // Prepare `weex` instance variable.
    const weexInstanceVar = {
      config,
      document,
      requireModule: moduleGetter
    }
    Object.freeze(weexInstanceVar)
  
    // Each instance has a independent `Vue` variable and it should have
    // all top-level public APIs.
    const subVue = Vue.extend({})
  
    // ensure plain-object components are extended from the subVue
    subVue.options._base = subVue
  
    // expose global utility
    ;['util', 'set', 'delete', 'nextTick', 'version', 'weexVersion', 'config'].forEach(name => {
      subVue[name] = Vue[name]
    })
  
    // The function which create a closure the JS Bundle will run in.
    // It will declare some instance variables like `Vue`, HTML5 Timer APIs etc.
    const instanceVars = Object.assign({
      Vue: subVue,
      weex: weexInstanceVar,
      __weex_require_module__: weexInstanceVar.requireModule // deprecated
    }, timerAPIs)
    callFunction(instanceVars, appCode)
  
    // Send `createFinish` signal to native.
    renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'createFinish', args: [] }], -1)
  }
  
  /**
   * Destroy an instance with id. It will make sure all memory of
   * this instance released and no more leaks.
   * @param {string} instanceId
   */
  export function destroyInstance (instanceId) {
    const instance = instances[instanceId] || {}
    if (instance.app instanceof Vue) {
      instance.app.$destroy()
    }
    delete instances[instanceId]
  }
  
  /**
   * Refresh an instance with id and new top-level component data.
   * It will use `Vue.set` on all keys of the new data. So it's better
   * define all possible meaningful keys when instance created.
   * @param {string} instanceId
   * @param {object} data
   */
  export function refreshInstance (instanceId, data) {
    const instance = instances[instanceId] || {}
    if (!(instance.app instanceof Vue)) {
      return new Error(`refreshInstance: instance ${instanceId} not found!`)
    }
    for (const key in data) {
      Vue.set(instance.app, key, data[key])
    }
    // Finally `refreshFinish` signal needed.
    renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'refreshFinish', args: [] }], -1)
  }
  
  /**
   * Get the JSON object of the root element.
   * @param {string} instanceId
   */
  export function getRoot (instanceId) {
    const instance = instances[instanceId] || {}
    if (!(instance.app instanceof Vue)) {
      return new Error(`getRoot: instance ${instanceId} not found!`)
    }
    return instance.app.$el.toJSON()
  }
  
  /**
   * 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
   */
  export function receiveTasks (instanceId, tasks) {
    const instance = instances[instanceId] || {}
    if (!(instance.app instanceof Vue)) {
      return new Error(`receiveTasks: instance ${instanceId} not found!`)
    }
    const { callbacks, document } = instance
    tasks.forEach(task => {
      // `fireEvent` case: find the event target and fire.
      if (task.method === 'fireEvent') {
        const [nodeId, type, e, domChanges] = task.args
        const el = document.getRef(nodeId)
        document.fireEvent(el, type, e, domChanges)
      }
      // `callback` case: find the callback by id and call it.
      if (task.method === 'callback') {
        const [callbackId, data, ifKeepAlive] = task.args
        const 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
          }
        }
      }
    })
    // Finally `updateFinish` signal needed.
    renderer.sendTasks(instanceId + '', [{ module: 'dom', method: 'updateFinish', args: [] }], -1)
  }
  
  /**
   * Register native modules information.
   * @param {object} newModules
   */
  export function registerModules (newModules) {
    for (const name in newModules) {
      if (!modules[name]) {
        modules[name] = {}
      }
      newModules[name].forEach(method => {
        if (typeof method === 'string') {
          modules[name][method] = true
        } else {
          modules[name][method.name] = method.args
        }
      })
    }
  }
  
  /**
   * Register native components information.
   * @param {array} newComponents
   */
  export function registerComponents (newComponents) {
    const config = Vue.config
    const newConfig = {}
    if (Array.isArray(newComponents)) {
      newComponents.forEach(component => {
        if (!component) {
          return
        }
        if (typeof component === 'string') {
          components[component] = true
          newConfig[component] = true
        } else if (typeof component === 'object' && typeof component.type === 'string') {
          components[component.type] = component
          newConfig[component.type] = true
        }
      })
      const oldIsReservedTag = config.isReservedTag
      config.isReservedTag = name => {
        return newConfig[name] || oldIsReservedTag(name)
      }
    }
  }
  
  // Hack `Vue` behavior to handle instance information and data
  // before root component created.
  Vue.mixin({
    beforeCreate () {
      const options = this.$options
      const parentOptions = (options.parent && options.parent.$options) || {}
  
      // root component (vm)
      if (options.el) {
        // record instance info
        const instance = instances[activeId] || {}
        this.$instanceId = activeId
        options.instanceId = activeId
        this.$document = instance.document
  
        // set external data of instance
        const dataOption = options.data
        const internalData = (typeof dataOption === 'function' ? dataOption() : dataOption) || {}
        options.data = Object.assign(internalData, instance.data)
  
        // record instance by id
        instance.app = this
  
        activeId = undefined
      } else {
        this.$instanceId = options.instanceId = parentOptions.instanceId
      }
    }
  })
  
  /**
   * @deprecated Just instance variable `weex.config`
   * Get instance config.
   * @return {object}
   */
  Vue.prototype.$getConfig = function () {
    const instance = instances[this.$instanceId] || {}
    if (instance.app instanceof Vue) {
      return instance.config
    }
  }
  
  /**
   * Generate native module getter. Each native module has several
   * methods to call. And all the hebaviors 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.
   * @param  {string}  instanceId
   * @return {function}
   */
  function genModuleGetter (instanceId) {
    const instance = instances[instanceId]
    return function (name) {
      const nativeModule = modules[name] || []
      const output = {}
      for (const methodName in nativeModule) {
        output[methodName] = (...args) => {
          const finalArgs = args.map(value => {
            return normalize(value, instance)
          })
          renderer.sendTasks(instanceId + '', [{ module: name, method: methodName, args: finalArgs }], -1)
        }
      }
      return output
    }
  }
  
  /**
   * Generate HTML5 Timer APIs. An important point is that the callback
   * will be converted into callback id when sent to native. So the
   * framework can make sure no side effect of the callabck happened after
   * an instance destroyed.
   * @param  {[type]} instanceId   [description]
   * @param  {[type]} moduleGetter [description]
   * @return {[type]}              [description]
   */
  function getInstanceTimer (instanceId, moduleGetter) {
    const instance = instances[instanceId]
    const timer = moduleGetter('timer')
    const timerAPIs = {
      setTimeout: (...args) => {
        const handler = function () {
          args[0](...args.slice(2))
        }
        timer.setTimeout(handler, args[1])
        return instance.callbackId.toString()
      },
      setInterval: (...args) => {
        const handler = function () {
          args[0](...args.slice(2))
        }
        timer.setInterval(handler, args[1])
        return instance.callbackId.toString()
      },
      clearTimeout: (n) => {
        timer.clearTimeout(n)
      },
      clearInterval: (n) => {
        timer.clearInterval(n)
      }
    }
    return timerAPIs
  }
  
  /**
   * Call a new function body with some global objects.
   * @param  {object} globalObjects
   * @param  {string} code
   * @return {any}
   */
  function callFunction (globalObjects, body) {
    const globalKeys = []
    const globalValues = []
    for (const key in globalObjects) {
      globalKeys.push(key)
      globalValues.push(globalObjects[key])
    }
    globalKeys.push(body)
  
    const result = new Function(...globalKeys)
    return result(...globalValues)
  }
  
  /**
   * 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}
   */
  function normalize (v, instance) {
    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':
        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)
    }
  }
  
  /**
   * 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) {
    const s = Object.prototype.toString.call(v)
    return s.substring(8, s.length - 1).toLowerCase()
  }
  