You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by ha...@apache.org on 2018/01/03 07:13:32 UTC

[01/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Repository: incubator-weex
Updated Branches:
  refs/heads/master d724832ff -> c0b2cfd50


http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/runtime/legacy-framework.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/runtime/legacy-framework.js b/test/js-framework/unit/runtime/legacy-framework.js
new file mode 100644
index 0000000..c9e37d0
--- /dev/null
+++ b/test/js-framework/unit/runtime/legacy-framework.js
@@ -0,0 +1,348 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const {
+  expect
+} = chai
+chai.use(sinonChai)
+
+import runtime from '../../../../runtime/api'
+import frameworks from '../../../../runtime/frameworks'
+import defaultConfig from '../../../../runtime/frameworks/legacy/config'
+import { init as resetTaskHandler } from '../../../../runtime/bridge/TaskCenter'
+
+const { init, config } = runtime
+config.frameworks = frameworks
+runtime.setNativeConsole()
+
+import Vm from '../../../../runtime/frameworks/legacy/vm'
+import { clearModules, getModule } from '../../../../runtime/frameworks/legacy/app/register'
+
+const framework = init(config)
+
+function clearRefs (json) {
+  delete json.ref
+  if (json.children) {
+    json.children.forEach(clearRefs)
+  }
+}
+
+describe('framework entry', () => {
+  const oriCallNative = global.callNative
+  const oriCallAddElement = global.callAddElement
+  const oriDocumentHandler = config.Document.handler
+  const callNativeSpy = sinon.spy()
+  const callAddElementSpy = sinon.spy()
+  const documentHandlerSpy = sinon.spy()
+  const instanceId = Date.now() + ''
+
+  before(() => {
+    global.callNative = (id, tasks, callbackId) => {
+      callNativeSpy(id, tasks, callbackId)
+      /* istanbul ignore if */
+      if (callbackId !== '-1') {
+        framework.callJS(id, [{
+          method: 'callback',
+          args: [callbackId, null, true]
+        }])
+      }
+    }
+    global.callAddElement = (name, id, ref, json, index, callbackId) => {
+      callAddElementSpy(name, ref, json, index, callbackId)
+      /* istanbul ignore if */
+      if (callbackId !== '-1') {
+        framework.callJS(id, [{
+          method: 'callback',
+          args: [callbackId, null, true]
+        }])
+      }
+    }
+    config.Document.handler = oriDocumentHandler
+    resetTaskHandler()
+  })
+
+  afterEach(() => {
+    callNativeSpy.reset()
+    callAddElementSpy.reset()
+    documentHandlerSpy.reset()
+  })
+
+  after(() => {
+    config.Document.handler = oriDocumentHandler
+    global.callNative = oriCallNative
+    global.callAddElement = oriCallAddElement
+  })
+
+  describe('createInstance', () => {
+    it('a simple bundle', () => {
+      const code = `
+        define('@weex-component/main',
+          function(require, exports, module) {
+            module.exports = {
+              data: function() {
+                return {
+                  text: 'Hello World',
+                  showText: true
+                };
+              }
+            }
+
+            ;module.exports.style = {}
+
+            ;module.exports.template = {
+              'type': 'container',
+              'children': [{
+                'type': 'text',
+                'shown': function() {
+                  return this.showText
+                },
+                'attr': {
+                  'value': function() {
+                    return this.text
+                  }
+                }
+              }]
+            }
+          }
+        )
+
+        bootstrap('@weex-component/main')
+      `
+      framework.createInstance(instanceId, code)
+      expect(callNativeSpy.callCount).to.be.equal(2)
+      expect(callAddElementSpy.callCount).to.be.equal(1)
+
+      expect(callNativeSpy.firstCall.args[0]).to.be.equal(instanceId)
+      expect(callNativeSpy.firstCall.args[1]).to.deep.equal([{
+        module: 'dom',
+        method: 'createBody',
+        args: [{
+          ref: '_root',
+          type: 'container',
+          attr: {},
+          style: {}
+        }]
+      }])
+      // expect(callNativeSpy.firstCall.args[2]).to.not.equal('-1')
+
+      expect(callAddElementSpy.firstCall.args[0]).to.be.equal(instanceId)
+      delete callAddElementSpy.firstCall.args[1].ref
+      expect(callAddElementSpy.firstCall.args[1]).to.deep.equal({
+        type: 'text',
+        attr: { value: 'Hello World' },
+        style: {}
+      })
+
+      // expect(callNativeSpy.secondCall.args[2]).to.not.equal('-1')
+
+      expect(callNativeSpy.secondCall.args[0]).to.be.equal(instanceId)
+      expect(callNativeSpy.secondCall.args[1]).to.deep.equal([{
+        module: 'dom',
+        method: 'createFinish',
+        args: []
+      }])
+      // expect(callNativeSpy.thirdCall.args[2]).to.not.equal('-1')
+    })
+
+    it('with a exist instanceId', () => {
+      const code = ''
+      const result = framework.createInstance(instanceId, code)
+      expect(result).to.be.an.instanceof(Error)
+    })
+  })
+
+  describe('getRoot', () => {
+    it('with a exist instanceId', () => {
+      const json = framework.getRoot(instanceId)
+      expect(json.ref).eql('_root')
+      clearRefs(json)
+      const expectJSON = {
+        type: 'container',
+        attr: {},
+        style: {},
+        children: [{
+          type: 'text',
+          attr: {
+            value: 'Hello World'
+          },
+          style: {}
+        }]
+      }
+      expect(json).to.be.deep.equal(expectJSON)
+    })
+  })
+
+  describe('callJS', () => {
+    it('fireEvent with no params', () => {
+      framework.callJS()
+    })
+
+    it('fireEvent with a exist instanceId', () => {
+      framework.callJS(instanceId, [{
+        method: 'fireEvent',
+        args: []
+      }])
+    })
+
+    it('callback with a exist instanceId', () => {
+      framework.callJS(instanceId, [{
+        method: 'callback',
+        args: []
+      }])
+    })
+
+    it('with a non-exist instanceId', () => {
+      const result = framework.callJS('123', [{
+        method: 'fireEvent',
+        args: []
+      }])
+      expect(result).to.be.an.instanceof(Error)
+    })
+
+    it('with a non-array tasks', () => {
+      const result = framework.callJS(instanceId, {
+        method: 'fireEvent',
+        args: []
+      })
+      expect(result).to.be.an.instanceof(Error)
+    })
+  })
+
+  describe('refreshInstance', () => {
+    it('modify showText to false', () => {
+      const json = framework.getRoot(instanceId)
+      const textRef = json.children[0].ref
+      framework.refreshInstance(instanceId, { showText: false })
+      expect(callNativeSpy.callCount).to.be.equal(2)
+      expect(callNativeSpy.firstCall.args[0]).to.be.equal(instanceId)
+      expect(callNativeSpy.firstCall.args[1]).to.deep.equal([{
+        module: 'dom',
+        method: 'removeElement',
+        args: [textRef]
+      }])
+      // expect(callNativeSpy.firstCall.args[2]).to.be.equal('-1')
+
+      expect(callNativeSpy.secondCall.args[0]).to.be.equal(instanceId)
+      expect(callNativeSpy.secondCall.args[1]).to.deep.equal([{
+        module: 'dom',
+        method: 'refreshFinish',
+        args: []
+      }])
+      // expect(callNativeSpy.secondCall.args[2]).to.be.equal('-1')
+    })
+
+    it('with a non-exist instanceId', () => {
+      const result = framework.refreshInstance('123', {})
+      expect(result).to.be.an.instanceof(Error)
+    })
+  })
+
+  describe('destroyInstance', () => {
+    it('with no params', () => {
+      framework.destroyInstance()
+    })
+
+    it('with a exist instanceId', () => {
+      const result = framework.destroyInstance(instanceId)
+      expect(result[instanceId]).to.be.undefined
+    })
+
+    it('with a non-exist instanceId', () => {
+      const result = framework.destroyInstance('123')
+      expect(result).to.be.an.instanceof(Error)
+    })
+  })
+
+  describe('registerComponents', () => {
+    it('with old format', () => {
+      framework.registerComponents(['a', 'b', 'c'])
+      expect(defaultConfig.nativeComponentMap).to.contain.keys('a', 'b', 'c')
+    })
+
+    it('with new format', () => {
+      framework.registerComponents([{
+        type: 'd',
+        append: 'tree'
+      }])
+      expect(defaultConfig.nativeComponentMap).to.contain.keys('d')
+      expect(defaultConfig.nativeComponentMap['d']).to.be.deep.equal({
+        type: 'd',
+        append: 'tree'
+      })
+    })
+
+    it('with non-array', () => {
+      framework.registerComponents({
+        type: 'e'
+      })
+      expect(defaultConfig.nativeComponentMap).not.contain.keys('e')
+    })
+
+    it('with methods', () => {
+      const components = [{
+        type: 'x',
+        methods: ['foo', 'bar']
+      }, {
+        type: 'y',
+        methods: []
+      }, {
+        type: 'z',
+        methods: null
+      }]
+      framework.registerComponents(components)
+      expect(defaultConfig.nativeComponentMap).to.contain.keys('x', 'y', 'z')
+    })
+  })
+
+  describe('register modules', () => {
+    it('with object of modules', () => {
+      clearModules()
+      const modules = {
+        a: [{
+          name: 'b',
+          args: ['string']
+        }]
+      }
+
+      framework.registerModules(modules)
+      expect(getModule('b')).an.object
+      clearModules()
+    })
+  })
+
+  describe('register methods', () => {
+    it('with object of methods', () => {
+      const methods = {
+        a: sinon.spy()
+      }
+
+      framework.registerMethods(methods)
+      expect(Vm.prototype.a).a.function
+      delete Vm.prototype.a
+    })
+  })
+})
+
+describe('config', () => {
+  it('config is an object', () => {
+    init({})
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/runtime/runner/helper.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/runtime/runner/helper.js b/test/js-framework/unit/runtime/runner/helper.js
new file mode 100644
index 0000000..79714fc
--- /dev/null
+++ b/test/js-framework/unit/runtime/runner/helper.js
@@ -0,0 +1,73 @@
+/*
+ * 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 runtime from '../../../../../runtime/api'
+import frameworks from '../../../../../runtime/frameworks'
+import services from '../../../../../runtime/services'
+
+const defaultModules = {
+  animation: ['transition'],
+  clipboard: ['getString', 'setString'],
+  dom: ['scrollToElement', 'getComponentRect', 'addRule'],
+  modal: ['alert', 'confirm', 'prompt', 'toast'],
+  navigator: ['push', 'pop'],
+  picker: ['pick', 'pickDate', 'pickTime'],
+  storage: ['setItem', 'getItem', 'removeItem', 'length', 'getAllKeys'],
+  stream: ['fetch'],
+  timer: ['setTimeout', 'setInterval'],
+  WebSocket: ['send', 'close', 'onopen', 'onmessage', 'onclose', 'onerror'],
+  webview: ['goBack', 'goForward', 'reload']
+}
+
+const defaultComponents = [
+  'div', 'text', 'image', 'input', 'a', 'switch', 'scroller', 'list', 'cell',
+  'slider', 'indicator', 'loading', 'refresh', 'textarea', 'web', 'video',
+  'embed', 'richtext'
+]
+
+export function createContext () {
+  const currentConfig = Object.assign({}, runtime.config)
+  currentConfig.frameworks = frameworks
+
+  for (const serviceName in services) {
+    runtime.service.register(serviceName, services[serviceName])
+  }
+
+  const context = runtime.init(currentConfig)
+
+  context.registerModules(defaultModules)
+  context.registerComponents(defaultComponents)
+
+  return context
+}
+
+// execute code and return the generated vdom tree
+export function execute (type, code) {
+  const context = createContext()
+  return new Promise(resolve => {
+    const id = String(Date.now() + Math.random())
+    context.createInstance(id, `
+      // { "framework": "${type}" }
+      ${code}
+    `)
+    setTimeout(() => {
+      resolve(context.getRoot(id))
+    }, 10)
+  })
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/runtime/runner/vanilla.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/runtime/runner/vanilla.js b/test/js-framework/unit/runtime/runner/vanilla.js
new file mode 100644
index 0000000..2bb6226
--- /dev/null
+++ b/test/js-framework/unit/runtime/runner/vanilla.js
@@ -0,0 +1,59 @@
+/*
+ * 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 { expect } from 'chai'
+import { execute } from './helper'
+
+describe('Vanilla Examples', () => {
+  it('execute vanilla code', (done) => {
+    execute('Vanilla', `
+      const body = document.createElement('div')
+      document.documentElement.appendChild(body)
+    `).then($root => {
+      expect($root.type).to.be.equal('div')
+    }).then(done)
+  })
+
+  it('instert child nodes', (done) => {
+    execute('Vanilla', `
+      const body = document.createElement('div')
+      body.appendChild(document.createElement('text'))
+      body.appendChild(document.createElement('image'))
+      document.documentElement.appendChild(body)
+    `).then($root => {
+      expect($root.type).to.be.equal('div')
+      expect($root.children[0].type).to.be.equal('text')
+      expect($root.children[1].type).to.be.equal('image')
+    }).then(done)
+  })
+
+  it('add text nodes', (done) => {
+    execute('Vanilla', `
+      const body = document.createElement('div')
+      const text = document.createElement('text')
+      text.setAttr('value', 'whatever')
+      body.appendChild(text)
+      document.documentElement.appendChild(body)
+    `).then($root => {
+      expect($root.type).to.be.equal('div')
+      expect($root.children[0].type).to.be.equal('text')
+      expect($root.children[0].attr.value).to.be.equal('whatever')
+    }).then(done)
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/runtime/runner/vue.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/runtime/runner/vue.js b/test/js-framework/unit/runtime/runner/vue.js
new file mode 100644
index 0000000..bdb0006
--- /dev/null
+++ b/test/js-framework/unit/runtime/runner/vue.js
@@ -0,0 +1,67 @@
+/*
+ * 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 { expect } from 'chai'
+import { execute } from './helper'
+import { compile } from 'weex-template-compiler'
+
+function toFunction (content) {
+  return `function () { ${content} }`
+}
+
+function compileAndExecute (template, additional) {
+  const { render, staticRenderFns } = compile(template)
+  return execute('Vue', `
+    new Vue({
+      el: '#root',
+      render: ${toFunction(render)},
+      staticRenderFns: [${staticRenderFns.map(toFunction).join(',')}],
+      ${additional}
+    })
+  `)
+}
+
+describe('Vue Examples', () => {
+  it('execute Vue code', (done) => {
+    execute('Vue', `
+      new Vue({
+        el: 'body',
+        render (h) {
+          return h('div', {}, [h('text', {}, 'Hello World')])
+        }
+      })
+    `).then($root => {
+      expect($root.type).to.be.equal('div')
+      expect($root.children[0].type).to.be.equal('text')
+      expect($root.children[0].attr.value).to.be.equal('Hello World')
+    }).then(done)
+  })
+
+  it('compile and execute', (done) => {
+    compileAndExecute(`
+      <div>
+        <text>Hello World</text>
+      </div>
+    `).then($root => {
+      expect($root.type).to.be.equal('div')
+      expect($root.children[0].type).to.be.equal('text')
+      expect($root.children[0].attr.value).to.be.equal('Hello World')
+    }).then(done)
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/runtime/utils.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/runtime/utils.js b/test/js-framework/unit/runtime/utils.js
new file mode 100644
index 0000000..3b1e3b8
--- /dev/null
+++ b/test/js-framework/unit/runtime/utils.js
@@ -0,0 +1,41 @@
+/*
+ * 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 { expect } from 'chai'
+import * as utils from '../../../../runtime/shared/utils'
+
+describe('utils', () => {
+  it('isEmpty', () => {
+    expect(utils.isEmpty()).to.be.true
+    expect(utils.isEmpty(0)).to.be.true
+    expect(utils.isEmpty(null)).to.be.true
+    expect(utils.isEmpty(undefined)).to.be.true
+    expect(utils.isEmpty(false)).to.be.true
+    expect(utils.isEmpty(true)).to.be.true
+    expect(utils.isEmpty(NaN)).to.be.true
+    expect(utils.isEmpty([])).to.be.true
+    expect(utils.isEmpty({})).to.be.true
+    expect(utils.isEmpty(Object.create(null))).to.be.true
+    expect(utils.isEmpty(Object.create({}))).to.be.true
+    expect(utils.isEmpty(Object.create({ x: '' }))).to.be.true
+
+    expect(utils.isEmpty({ abc: '' })).to.be.false
+    expect(utils.isEmpty([0])).to.be.false
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/runtime/vdom/index.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/runtime/vdom/index.js b/test/js-framework/unit/runtime/vdom/index.js
new file mode 100644
index 0000000..763b202
--- /dev/null
+++ b/test/js-framework/unit/runtime/vdom/index.js
@@ -0,0 +1,639 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import {
+  Document,
+  Element,
+  Comment,
+  isWeexElement,
+  registerElement,
+  clearWeexElements
+} from '../../../../../runtime/vdom'
+
+describe('document constructor', () => {
+  it('create & destroy document', () => {
+    const doc = new Document('foo', 'http://path/to/url')
+    expect(doc).is.an.object
+    expect(doc.id).eql('foo')
+    expect(doc.URL).eql('http://path/to/url')
+    expect(doc.documentElement).is.an.object
+    expect(doc.documentElement.role).equal('documentElement')
+    doc.destroy()
+  })
+})
+
+describe('component methods management', () => {
+  before(() => {
+    registerElement('x', ['foo', 'bar'])
+    registerElement('y', [])
+    registerElement('z')
+  })
+
+  after(() => {
+    clearWeexElements()
+  })
+
+  it('has registered element types', () => {
+    expect(isWeexElement('x')).to.be.true
+  })
+
+  it('will call component method', () => {
+    const spy = sinon.spy()
+    const doc = new Document('test', '', spy)
+    const x = new Element('x')
+    const y = new Element('y')
+    const z = new Element('z')
+    const n = new Element('n')
+    expect(x.foo).is.function
+    expect(x.bar).is.function
+    expect(x.baz).is.undefined
+    expect(y.foo).is.undefined
+    expect(z.foo).is.undefined
+    expect(n.foo).is.undefined
+
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    doc.body.appendChild(x)
+    doc.body.appendChild(y)
+    doc.body.appendChild(z)
+    doc.body.appendChild(n)
+    expect(spy.args.length).eql(5)
+
+    x.foo(1, 2, 3)
+    expect(spy.args.length).eql(6)
+    expect(spy.args[5]).eql([[{ component: 'x', method: 'foo', ref: x.ref, args: [1, 2, 3] }]])
+  })
+})
+
+describe('document methods', () => {
+  let doc
+
+  beforeEach(() => {
+    doc = new Document('foo', null, function () {})
+  })
+
+  afterEach(() => {
+    doc.destroy()
+  })
+
+  it('open & close with a listener', () => {
+    expect(doc.listener.batched).is.false
+    doc.close()
+    expect(doc.listener.batched).is.true
+    doc.open()
+    expect(doc.listener.batched).is.false
+  })
+
+  it('create body', () => {
+    const ele = doc.createBody('container',
+      { attr: { id: 'a' }, style: { fontSize: 16 }})
+    expect(ele.role).equal('body')
+    expect(ele.attr).to.have.a.property('id', 'a')
+    expect(ele.style).to.have.a.property('fontSize', 16)
+    expect(ele).have.a.property('ref')
+    expect(ele).have.a.property('children')
+    expect(ele).have.a.property('pureChildren')
+    expect(ele).have.a.property('insertBefore')
+    expect(ele).have.a.property('setStyle')
+
+    const ref = ele.ref
+    expect(doc.nodeMap[ref]).equal(ele)
+  })
+
+  it('create element', () => {
+    const ele = doc.createElement('container',
+      { attr: { id: 'a' }, style: { fontSize: 16 }})
+    expect(ele.attr).to.have.a.property('id', 'a')
+    expect(ele.style).to.have.a.property('fontSize', 16)
+    expect(ele).have.a.property('ref')
+    expect(ele).have.a.property('children')
+    expect(ele).have.a.property('pureChildren')
+    expect(ele).have.a.property('insertBefore')
+    expect(ele).have.a.property('setStyle')
+
+    expect(doc.nodeMap[ele.ref]).is.undefined
+    doc.documentElement.appendChild(ele)
+    expect(doc.nodeMap[ele.ref]).equal(ele)
+  })
+
+  it('create comment', () => {
+    const comment = doc.createComment('start')
+    expect(comment).have.a.property('ref')
+    expect(comment).not.have.a.property('appendChild')
+    expect(comment).have.a.property('value', 'start')
+    expect(comment.toString()).eql('<!-- start -->')
+
+    const ref = comment.ref
+    expect(doc.nodeMap[ref]).is.undefined
+    doc.documentElement.appendChild(comment)
+    expect(doc.nodeMap[ref]).equal(comment)
+  })
+})
+
+describe('Element in document methods', () => {
+  let doc, el, el2, el3
+
+  beforeEach(() => {
+    doc = new Document('foo', null, function () {})
+    el = new Element('bar', {
+      attr: { a: 11, b: 12 },
+      style: { c: 13, d: 14 },
+      classStyle: { a: 211, c: 213 }
+    })
+    el2 = new Element('baz', {
+      attr: { a: 21, b: 22 },
+      style: { c: 23, d: 24 },
+      classStyle: { a: 221, c: 223 }
+    })
+    el3 = new Element('qux', {
+      attr: { a: 31, b: 32 },
+      style: { c: 33, d: 34 },
+      classStyle: { a: 231, c: 233 }
+    })
+  })
+
+  afterEach(() => {
+    doc.destroy()
+  })
+
+  it('init correctly', () => {
+    expect(el).is.an.object
+    expect(el.type).eql('bar')
+    expect(el.attr).eql({ a: 11, b: 12 })
+    expect(el.style).eql({ c: 13, d: 14 })
+    expect(el.event).eql({})
+    expect(el.children).eql([])
+    expect(el.pureChildren).eql([])
+    expect(doc.nodeMap).is.an.object
+    expect(doc.documentElement).is.an.object
+    expect(Object.keys(doc.nodeMap)).eql([doc.documentElement.ref])
+    doc.documentElement.appendChild(el)
+    doc.documentElement.appendChild(el2)
+    el2.appendChild(el3)
+    expect(Object.keys(doc.nodeMap)).eql([
+      doc.documentElement.ref,
+      el.ref
+    ])
+  })
+
+  it('has correct exports', () => {
+    const ref = el.ref
+    const finalStyle = el.toStyle()
+    expect(finalStyle).eql({ a: 211, c: 13, d: 14 })
+    expect(el.toJSON()).eql({
+      ref: ref, type: 'bar',
+      attr: el.attr, style: finalStyle
+    })
+    expect(el.toString()).eql(
+      '<bar attr={"a":11,"b":12} style={"a":211,"c":13,"d":14}></bar>')
+  })
+
+  it('createBody', () => {
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    expect(doc.body).is.an.object
+    expect(doc.body.role).eql('body')
+    expect(doc.body.type).eql('r')
+    expect(doc.body.docId).eql('foo')
+  })
+
+  it('appendChild', () => {
+    el.appendChild(el2)
+    expect(el.children.length).eql(1)
+    expect(el.children[0]).equal(el2)
+    expect(el2.parentNode.ref).eql(el.ref)
+
+    expect(el.docId).is.not.ok
+    expect(el2.docId).is.not.ok
+
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    doc.body.appendChild(el)
+    expect(doc.body.children.length).eql(1)
+    expect(el.parentNode.ref).eql(doc.body.ref)
+    expect(el.docId).is.ok
+    expect(el2.docId).is.ok
+
+    expect(el3.docId).is.not.ok
+    el.appendChild(el3)
+    expect(el.children.length).eql(2)
+    expect(el.children[0]).equal(el2)
+    expect(el.children[1]).equal(el3)
+    expect(el3.parentNode.ref).eql(el.ref)
+    expect(el3.docId).is.ok
+
+    expect(el2.previousSibling).is.not.ok
+    expect(el2.nextSibling).eql(el3)
+    expect(el3.previousSibling).eql(el2)
+    expect(el3.nextSibling).is.not.ok
+  })
+
+  it('insertBefore', () => {
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    doc.body.appendChild(el)
+    expect(el.parentNode.ref).eql(doc.body.ref)
+
+    el.appendChild(el2)
+    expect(el2.parentNode.ref).eql(el.ref)
+    expect(el.children.length).eql(1)
+    expect(el.children[0]).equal(el2)
+
+    el.insertBefore(el3, el2)
+    expect(el.children.length).eql(2)
+    expect(el.children[0]).equal(el3)
+    expect(el.children[1]).equal(el2)
+    expect(el3.parentNode.ref).eql(el.ref)
+
+    expect(el.docId).eql('foo')
+    expect(el2.docId).eql('foo')
+    expect(el3.docId).eql('foo')
+
+    expect(el3.previousSibling).is.not.ok
+    expect(el3.nextSibling).eql(el2)
+    expect(el2.previousSibling).eql(el3)
+    expect(el2.nextSibling).is.not.ok
+
+    el.insertBefore(el2, el3)
+    expect(el.children.length).eql(2)
+    expect(el.children[0]).equal(el2)
+    expect(el.children[1]).equal(el3)
+
+    expect(el.docId).eql('foo')
+    expect(el2.docId).eql('foo')
+    expect(el3.docId).eql('foo')
+
+    expect(el2.previousSibling).is.not.ok
+    expect(el2.nextSibling).eql(el3)
+    expect(el3.previousSibling).eql(el2)
+    expect(el3.nextSibling).is.not.ok
+  })
+
+  it('insertAfter', () => {
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    doc.body.appendChild(el)
+    expect(el.parentNode.ref).eql(doc.body.ref)
+
+    el.appendChild(el2)
+    el.insertAfter(el3, el2)
+    expect(el.children.length).eql(2)
+    expect(el.children[0]).equal(el2)
+    expect(el.children[1]).equal(el3)
+    expect(el2.parentNode.ref).eql(el.ref)
+    expect(el3.parentNode.ref).eql(el.ref)
+
+    expect(el.docId).eql('foo')
+    expect(el2.docId).eql('foo')
+    expect(el3.docId).eql('foo')
+
+    expect(el2.previousSibling).is.not.ok
+    expect(el2.nextSibling).eql(el3)
+    expect(el3.previousSibling).eql(el2)
+    expect(el3.nextSibling).is.not.ok
+
+    el.insertAfter(el2, el3, true)
+    expect(el.children.length).eql(2)
+    expect(el.children[0]).equal(el3)
+    expect(el.children[1]).equal(el2)
+
+    expect(el3.previousSibling).is.not.ok
+    expect(el3.nextSibling).eql(el2)
+    expect(el2.previousSibling).eql(el3)
+    expect(el2.nextSibling).is.not.ok
+  })
+
+  it('removeChild', () => {
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    doc.body.appendChild(el)
+    doc.body.appendChild(el2)
+    el2.appendChild(el3)
+
+    expect(el.docId).eql('foo')
+    expect(el2.docId).eql('foo')
+    expect(el3.docId).eql('foo')
+    doc.body.removeChild(el)
+    expect(doc.body.children.length).equal(1)
+    expect(doc.body.children[0]).equal(el2)
+    expect(el.docId).is.not.ok
+    expect(el2.docId).eql('foo')
+    expect(el3.docId).eql('foo')
+    doc.body.removeChild(el2)
+    expect(doc.body.children.length).equal(0)
+    expect(el.docId).is.not.ok
+    expect(el2.docId).is.not.ok
+    expect(el3.docId).is.not.ok
+  })
+
+  it('clear', () => {
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    doc.body.appendChild(el)
+    doc.body.appendChild(el2)
+    doc.body.appendChild(el3)
+
+    expect(el.docId).eql('foo')
+    expect(el2.docId).eql('foo')
+    expect(el3.docId).eql('foo')
+
+    doc.body.clear()
+    expect(doc.body.children.length).equal(0)
+    expect(el.docId).is.not.ok
+    expect(el2.docId).is.not.ok
+    expect(el3.docId).is.not.ok
+  })
+
+  it('modify attr, style, event', () => {
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    doc.body.appendChild(el)
+
+    el.setAttr('a', 21)
+    expect(el.toJSON().attr).eql({ a: 21, b: 12 })
+    el.setAttr('a', 22, true)
+    expect(el.toJSON().attr).eql({ a: 22, b: 12 })
+    el.setAttr('a', 23, false)
+    expect(el.toJSON().attr).eql({ a: 23, b: 12 })
+
+    el.setStyle('c', 21)
+    expect(el.toJSON().style).eql({ a: 211, c: 21, d: 14 })
+    el.setStyle('c', 22, true)
+    expect(el.toJSON().style).eql({ a: 211, c: 22, d: 14 })
+    el.setStyle('c', 23, false)
+    expect(el.toJSON().style).eql({ a: 211, c: 23, d: 14 })
+
+    el.setClassStyle({ a: 311, c: 313 })
+    expect(el.toJSON().style).eql({ a: 311, c: 23, d: 14 })
+
+    const handler = function () {}
+    el.addEvent('click', handler)
+    expect(el.toJSON().event).eql(['click'])
+    expect(el.event.click.handler).equal(handler)
+    el.removeEvent('click')
+    expect(el.event.click).is.undefined
+  })
+})
+
+describe('Node', () => {
+  let doc, el, el2, el3, c, c2, c3, spy
+
+  beforeEach(() => {
+    spy = sinon.spy()
+    doc = new Document('foo', '', spy)
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    el = new Element('bar')
+    el2 = new Element('baz')
+    el3 = new Element('qux')
+    c = new Comment('aaa')
+    c2 = new Comment('bbb')
+    c3 = new Comment('ccc')
+  })
+
+  afterEach(() => {
+    doc.destroy()
+  })
+
+  it('prev and next', () => {
+    expect(el.previousSibling).is.not.ok
+    expect(el.nextSibling).is.not.ok
+
+    doc.body.appendChild(el)
+    doc.body.appendChild(el2)
+    doc.body.appendChild(c)
+    doc.body.appendChild(el3)
+    doc.body.appendChild(c2)
+    doc.body.appendChild(c3)
+
+    expect(el.previousSibling).is.not.ok
+    expect(el2.previousSibling).equal(el)
+    expect(c.previousSibling).equal(el2)
+    expect(el3.previousSibling).equal(c)
+    expect(c2.previousSibling).equal(el3)
+    expect(c3.previousSibling).equal(c2)
+
+    expect(el.nextSibling).equal(el2)
+    expect(el2.nextSibling).equal(c)
+    expect(c.nextSibling).equal(el3)
+    expect(el3.nextSibling).equal(c2)
+    expect(c2.nextSibling).equal(c3)
+    expect(c3.nextSibling).is.not.ok
+  })
+
+  it('tree operations with renderer', () => {
+    doc.body.appendChild(el)
+    el.appendChild(el2)
+    el.appendChild(el3)
+
+    expect(spy.args.length).eql(4)
+    el.insertBefore(el3, el2) // [el3, el2]
+    expect(spy.args.length).eql(5)
+    expect(spy.args[4][0]).eql([{
+      module: 'dom', method: 'moveElement',
+      args: [el3.ref, el.ref, 0]
+    }])
+    el.insertAfter(el3, el2) // [el2, el3]
+    expect(spy.args.length).eql(6)
+    expect(spy.args[5][0]).eql([{
+      module: 'dom', method: 'moveElement',
+      args: [el3.ref, el.ref, 2]
+    }])
+    el.removeChild(el2) // [el3]
+    expect(spy.args.length).eql(7)
+    expect(spy.args[6][0]).eql([{
+      module: 'dom', method: 'removeElement',
+      args: [el2.ref]
+    }])
+    el.clear() // []
+    expect(spy.args.length).eql(8)
+    expect(spy.args[7][0]).eql([{
+      module: 'dom', method: 'removeElement',
+      args: [el3.ref]
+    }])
+  })
+})
+
+describe('complicated situations', () => {
+  let doc, el, el2, el3, c, c2, c3, spy
+
+  beforeEach(() => {
+    spy = sinon.spy()
+    doc = new Document('foo', '', spy)
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    el = new Element('bar')
+    el2 = new Element('baz')
+    el3 = new Element('qux')
+    c = new Comment('aaa')
+    c2 = new Comment('bbb')
+    c3 = new Comment('ccc')
+  })
+
+  afterEach(() => {
+    doc.destroy()
+  })
+
+  it('move a node to its original position', () => {
+    doc.body.appendChild(el)
+    doc.body.appendChild(el2)
+    doc.body.appendChild(el3)
+
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, el2, el3])
+    expect(doc.body.pureChildren).eql([el, el2, el3])
+
+    doc.body.insertAfter(el2, el)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, el2, el3])
+    expect(doc.body.pureChildren).eql([el, el2, el3])
+
+    doc.body.insertAfter(el3, el2, true)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, el2, el3])
+    expect(doc.body.pureChildren).eql([el, el2, el3])
+
+    doc.body.insertBefore(el, el2)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, el2, el3])
+    expect(doc.body.pureChildren).eql([el, el2, el3])
+
+    doc.body.insertBefore(el2, el3, true)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, el2, el3])
+    expect(doc.body.pureChildren).eql([el, el2, el3])
+
+    doc.body.insertAfter(c, el)
+    doc.body.insertAfter(c2, el2)
+    doc.body.insertAfter(c3, el3)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, c, el2, c2, el3, c3])
+    expect(doc.body.pureChildren).eql([el, el2, el3])
+
+    doc.body.insertBefore(el, c)
+    doc.body.insertBefore(el, c, true)
+    doc.body.insertBefore(c, el2)
+    doc.body.insertBefore(c, el2, true)
+    doc.body.insertAfter(c, el)
+    doc.body.insertAfter(c, el, true)
+    doc.body.insertAfter(el2, c)
+    doc.body.insertAfter(el2, c, true)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, c, el2, c2, el3, c3])
+    expect(doc.body.pureChildren).eql([el, el2, el3])
+
+    // move to another place that not change the pureChildren
+    doc.body.insertBefore(el, el2)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([c, el, el2, c2, el3, c3])
+    expect(doc.body.pureChildren).eql([el, el2, el3])
+
+    doc.body.insertBefore(el, c)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, c, el2, c2, el3, c3])
+    expect(doc.body.pureChildren).eql([el, el2, el3])
+
+    doc.body.insertAfter(c, el2)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, el2, c, c2, el3, c3])
+    expect(doc.body.pureChildren).eql([el, el2, el3])
+
+    doc.body.insertBefore(c2, c)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, el2, c2, c, el3, c3])
+    expect(doc.body.pureChildren).eql([el, el2, el3])
+  })
+
+  it('insert before a comment', () => {
+    doc.body.appendChild(el)
+    doc.body.appendChild(c)
+    doc.body.appendChild(c2)
+    doc.body.appendChild(el2)
+    expect(spy.args.length).eql(3)
+    expect(doc.body.children).eql([el, c, c2, el2])
+    expect(doc.body.pureChildren).eql([el, el2])
+
+    doc.body.insertBefore(el3, c)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, el3, c, c2, el2])
+    expect(doc.body.pureChildren).eql([el, el3, el2])
+    expect(spy.args[3][0]).eql([{
+      module: 'dom', method: 'addElement',
+      args: [doc.body.ref, el3.toJSON(), 1] }])
+  })
+
+  it('insert before a comment which has no more element after', () => {
+    doc.body.appendChild(el)
+    doc.body.appendChild(el2)
+    doc.body.appendChild(c)
+    doc.body.appendChild(c2)
+    expect(spy.args.length).eql(3)
+    expect(doc.body.children).eql([el, el2, c, c2])
+    expect(doc.body.pureChildren).eql([el, el2])
+
+    doc.body.insertBefore(el3, c)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, el2, el3, c, c2])
+    expect(doc.body.pureChildren).eql([el, el2, el3])
+    expect(spy.args[3][0]).eql([{
+      module: 'dom', method: 'addElement',
+      args: [doc.body.ref, el3.toJSON(), 2] }])
+  })
+
+  it('insert after a comment', () => {
+    doc.body.appendChild(el)
+    doc.body.appendChild(c)
+    doc.body.appendChild(c2)
+    doc.body.appendChild(el2)
+    expect(spy.args.length).eql(3)
+    expect(doc.body.children).eql([el, c, c2, el2])
+    expect(doc.body.pureChildren).eql([el, el2])
+
+    doc.body.insertAfter(el3, c2)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([el, c, c2, el3, el2])
+    expect(doc.body.pureChildren).eql([el, el3, el2])
+    expect(spy.args[3][0]).eql([{
+      module: 'dom', method: 'addElement',
+      args: [doc.body.ref, el3.toJSON(), 1] }])
+  })
+
+  it('insert after a comment which has no more element before', () => {
+    doc.body.appendChild(c)
+    doc.body.appendChild(c2)
+    doc.body.appendChild(el)
+    doc.body.appendChild(el2)
+    expect(spy.args.length).eql(3)
+    expect(doc.body.children).eql([c, c2, el, el2])
+    expect(doc.body.pureChildren).eql([el, el2])
+
+    doc.body.insertAfter(el3, c2)
+    expect(spy.args.length).eql(4)
+    expect(doc.body.children).eql([c, c2, el3, el, el2])
+    expect(doc.body.pureChildren).eql([el3, el, el2])
+    expect(spy.args[3][0]).eql([{
+      module: 'dom', method: 'addElement',
+      args: [doc.body.ref, el3.toJSON(), 0] }])
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/runtime/vdom/listener.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/runtime/vdom/listener.js b/test/js-framework/unit/runtime/vdom/listener.js
new file mode 100644
index 0000000..dab4b7e
--- /dev/null
+++ b/test/js-framework/unit/runtime/vdom/listener.js
@@ -0,0 +1,381 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import { Document } from '../../../../../runtime/vdom'
+import Listener from '../../../../../runtime/bridge/Listener'
+
+describe('dom listener basic', () => {
+  it('works with no id', () => {
+    const doc = new Document(null, null, null)
+    doc.destroy()
+  })
+
+  it('works with no handler', () => {
+    const oriCallNative = global.callNative
+    const oriCallAddElement = global.callAddElement
+    const oriDocumentHandler = Document.handler
+
+    Document.handler = null
+    global.callNative = function () { return -1 }
+    global.callAddElement = function () { return -1 }
+
+    const doc = new Document('foo1', null, null)
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    const el = doc.createElement('a')
+    doc.body.appendChild(el)
+    doc.destroy()
+
+    global.callNative = oriCallNative
+    global.callAddElement = oriCallAddElement
+    Document.handler = oriDocumentHandler
+  })
+
+  it('works with an handler', () => {
+    const doc = new Document('foo2', null, function () {})
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    doc.destroy()
+  })
+
+  it('works with an handler', () => {
+    const doc = new Document('foo2', null, function () {})
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+    doc.destroy()
+  })
+})
+
+describe('dom listener details', () => {
+  let doc, spy
+
+  beforeEach(() => {
+    spy = sinon.spy()
+    doc = new Document('foo', '', spy, Listener)
+  })
+
+  afterEach(() => {
+    doc.destroy()
+  })
+
+  it('create document again', (done) => {
+    const documentElement = doc.createDocumentElement()
+
+    expect(documentElement).eql(doc.documentElement)
+    done()
+  })
+
+  it('create body', (done) => {
+    const body = doc.createBody('r', {
+      attr: { a: 1 }, style: { b: 2 }
+    })
+
+    doc.documentElement.appendChild(body)
+
+    expect(spy.args.length).eql(1)
+    expect(spy.args[0]).eql([[{
+      module: 'dom', method: 'createBody',
+      args: [{ type: 'r', ref: '_root', attr: { a: 1 }, style: { b: 2 }}]
+    }], '-1'])
+    done()
+  })
+
+  it('document appendChild', (done) => {
+    const el = doc.createElement('father')
+    const el2 = doc.createElement('child1')
+    const el3 = doc.createElement('child2')
+
+    el.appendChild(el2)
+    el.appendChild(el3)
+
+    doc.documentElement.appendChild(el)
+
+    expect(doc.documentElement.children.length).eql(1)
+    expect(doc.documentElement.children[0]).eql(el)
+
+    done()
+  })
+
+  it('document insertBefore', (done) => {
+    const body = doc.createBody('r', {
+      attr: { a: 1 }, style: { b: 2 }
+    })
+
+    doc.documentElement.insertBefore(body)
+
+    expect(spy.args.length).eql(1)
+    expect(spy.args[0]).eql([[{
+      module: 'dom', method: 'createBody',
+      args: [{ type: 'r', ref: '_root', attr: { a: 1 }, style: { b: 2 }}]
+    }], '-1'])
+    done()
+  })
+
+  it('document insertBefore with comment', (done) => {
+    const body = doc.createBody('r', {
+      attr: { a: 1 }, style: { b: 2 }
+    })
+    const el = doc.createComment('asd')
+
+    doc.documentElement.appendChild(el)
+    doc.documentElement.insertBefore(body, el)
+
+    expect(doc.documentElement.children.length).eql(2)
+    expect(doc.documentElement.children[0]).eql(body)
+    done()
+  })
+
+  it('document fireEvent with no element', (done) => {
+    doc.fireEvent(null)
+    done()
+  })
+
+  it('document fireEvent with domChanges', (done) => {
+    const el = doc.createElement('el', {
+      attr: { a: 1 }, style: { b: 2 }
+    })
+
+    doc.documentElement.appendChild(el)
+
+    doc.fireEvent(el, null, null, {})
+
+    expect(el.attr).eql({ a: 1 })
+    expect(el.style).eql({ b: 2 })
+
+    doc.fireEvent(el, null, null, {
+      attrs: { a: 1 }, style: { b: 2 }
+    })
+
+    expect(el.attr).eql({ a: 1 })
+    expect(el.style).eql({ b: 2 })
+
+    doc.fireEvent(el, null, null, {
+      attrs: { a: 2 }, style: { b: 3 }
+    })
+
+    expect(el.attr).eql({ a: 2 })
+    expect(el.style).eql({ b: 3 })
+    done()
+  })
+
+  it('toString has pureChildren', (done) => {
+    const el = doc.createElement('el')
+    const el1 = doc.createElement('el1')
+    el.appendChild(el1)
+    expect(el.toString()).eql('<el attr={} style={}><el1 attr={} style={}></el1></el>')
+
+    done()
+  })
+
+  it('removeChild', (done) => {
+    const el = doc.createElement('el')
+    const el1 = doc.createElement('el1')
+
+    el.removeChild(el1)
+    el.removeChild(el1, true)
+
+    done()
+  })
+
+  it('removeEvent', (done) => {
+    const el = doc.createElement('el')
+    el.removeEvent(null)
+    done()
+  })
+
+  it('add element', (done) => {
+    const body = doc.createBody('r')
+
+    doc.documentElement.appendChild(body)
+
+    expect(spy.args[0]).eql([[{
+      module: 'dom', method: 'createBody',
+      args: [body.toJSON()]
+    }], '-1'])
+
+    const el = doc.createElement('a')
+    el.setAttr('x', 1)
+    el.addEvent('click', () => {})
+    doc.body.appendChild(el)
+
+    expect(spy.args[1]).eql([[{
+      module: 'dom', method: 'addElement',
+      args: ['_root', el.toJSON(), -1]
+    }], '-1'])
+
+    const el2 = doc.createElement('b')
+    doc.body.insertBefore(el2, el) // [el2, el]
+
+    const el3 = doc.createElement('c')
+    doc.body.insertAfter(el3, el) // [el2, el, el3]
+
+    expect(spy.args.length).eql(4)
+    expect(spy.args[2]).eql([[{
+      module: 'dom', method: 'addElement',
+      args: ['_root', el2.toJSON(), 0]
+    }], '-1'])
+    expect(spy.args[3]).eql([[{
+      module: 'dom', method: 'addElement',
+      args: ['_root', el3.toJSON(), 2]
+    }], '-1'])
+
+    done()
+  })
+
+  it('bind listener for element props', () => {
+    const el = doc.createElement('bar')
+    doc.createBody('r')
+    doc.documentElement.appendChild(doc.body)
+
+    expect(spy.args.length).eql(1)
+    expect(spy.args[0][0]).eql([{
+      module: 'dom', method: 'createBody',
+      args: [doc.body.toJSON()]
+    }])
+
+    el.setAttr('a', 1)
+    el.setStyle('a', 2)
+    el.setClassStyle({ a: 3, b: 4 })
+    el.addEvent('click', () => {})
+    el.addEvent('appear', () => {})
+    el.removeEvent('appear')
+    doc.body.appendChild(el)
+
+    expect(spy.args.length).eql(2)
+    expect(spy.args[1][0]).eql([{
+      module: 'dom', method: 'addElement',
+      args: ['_root', el.toJSON(), -1]
+    }])
+
+    el.setAttr('a', 11, true)
+    expect(spy.args.length).eql(2)
+    el.setAttr('a', 12)
+    expect(spy.args.length).eql(3)
+    expect(spy.args[2][0]).eql([{
+      module: 'dom', method: 'updateAttrs',
+      args: [el.ref, { a: 12 }]
+    }])
+    el.setAttr('a', 12, false)
+    expect(spy.args.length).eql(4)
+    expect(spy.args[3][0]).eql([{
+      module: 'dom', method: 'updateAttrs',
+      args: [el.ref, { a: 12 }]
+    }])
+
+    el.setStyle('a', 13, true)
+    expect(spy.args.length).eql(4)
+    el.setStyle('a', 14)
+    expect(spy.args.length).eql(5)
+    expect(spy.args[4][0]).eql([{
+      module: 'dom', method: 'updateStyle',
+      args: [el.ref, { a: 14 }]
+    }])
+    el.setStyle('a', 14, false)
+    expect(spy.args.length).eql(6)
+    expect(spy.args[5][0]).eql([{
+      module: 'dom', method: 'updateStyle',
+      args: [el.ref, { a: 14 }]
+    }])
+
+    el.setClassStyle({ a: 13, b: 14 })
+    expect(spy.args[6][0]).eql([{
+      module: 'dom', method: 'updateStyle',
+      args: [el.ref, { a: 14, b: 14 }]
+    }])
+    expect(spy.args.length).eql(7)
+    el.addEvent('click', () => {})
+    expect(spy.args.length).eql(7)
+    el.addEvent('appear', () => {})
+    expect(spy.args.length).eql(8)
+    expect(spy.args[7][0]).eql([{
+      module: 'dom', method: 'addEvent',
+      args: [el.ref, 'appear']
+    }])
+
+    el.removeEvent('click')
+    expect(spy.args.length).eql(9)
+    expect(spy.args[8][0]).eql([{
+      module: 'dom', method: 'removeEvent',
+      args: [el.ref, 'click']
+    }])
+    el.removeEvent('appear')
+    expect(spy.args.length).eql(10)
+    expect(spy.args[9][0]).eql([{
+      module: 'dom', method: 'removeEvent',
+      args: [el.ref, 'appear']
+    }])
+
+    el.setAttr('a', 1)
+    el.setStyle('a', 2)
+    el.setClassStyle({ a: 3, b: 4 })
+    el.addEvent('click', () => {})
+    el.addEvent('appear', () => {})
+    el.removeEvent('appear')
+    expect(spy.args.length).eql(16)
+    expect(spy.args.slice(10).map(c => c[0][0])).eql([
+      { module: 'dom', method: 'updateAttrs', args: [el.ref, { a: 1 }] },
+      { module: 'dom', method: 'updateStyle', args: [el.ref, { a: 2 }] },
+      { module: 'dom', method: 'updateStyle', args: [el.ref, { a: 2, b: 4 }] },
+      { module: 'dom', method: 'addEvent', args: [el.ref, 'click'] },
+      { module: 'dom', method: 'addEvent', args: [el.ref, 'appear'] },
+      { module: 'dom', method: 'removeEvent', args: [el.ref, 'appear'] }
+    ])
+  })
+})
+
+describe('listener', () => {
+  let spy, listener
+
+  beforeEach(() => {
+    spy = sinon.spy()
+    listener = new Listener('1', spy)
+  })
+
+  it('removeElement many', () => {
+    listener.removeElement(['1', '2', '3', '4'])
+
+    expect(spy.args[0][0]).eql([
+      { module: 'dom', method: 'removeElement', args: ['1'] },
+      { module: 'dom', method: 'removeElement', args: ['2'] },
+      { module: 'dom', method: 'removeElement', args: ['3'] },
+      { module: 'dom', method: 'removeElement', args: ['4'] }
+    ])
+
+    listener = null
+  })
+
+  it('no-handler', () => {
+    listener = new Listener('1')
+    listener.handler(
+      { module: 'dom', method: 'removeElement', args: ['1'] },
+      spy
+    )
+    expect(spy).callCount(1)
+  })
+
+  afterEach(() => {
+    spy = null
+    listener = null
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/runtime/vdom/node.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/runtime/vdom/node.js b/test/js-framework/unit/runtime/vdom/node.js
new file mode 100644
index 0000000..172718f
--- /dev/null
+++ b/test/js-framework/unit/runtime/vdom/node.js
@@ -0,0 +1,39 @@
+/*
+ * 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 chai from 'chai'
+const { expect } = chai
+
+import {
+  Node
+} from '../../../../../runtime/vdom'
+
+describe('Node', () => {
+  it('create node', () => {
+    const node = new Node()
+    expect(node).is.an.object
+    expect(node.nodeId).to.be.a('string')
+    expect(node.ref).eql(node.nodeId)
+    expect(node.children).eql([])
+    expect(node.pureChildren).eql([])
+    expect(node.parentNode).to.be.null
+    expect(node.nextSibling).to.be.null
+    expect(node.previousSibling).to.be.null
+    node.destroy()
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/shared/BroadcastChannel.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/shared/BroadcastChannel.js b/test/js-framework/unit/shared/BroadcastChannel.js
new file mode 100644
index 0000000..608b65c
--- /dev/null
+++ b/test/js-framework/unit/shared/BroadcastChannel.js
@@ -0,0 +1,231 @@
+/*
+ * 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 { expect } from 'chai'
+import sinon from 'sinon'
+
+import service from '../../../../runtime/services/broadcast-channel/index'
+import { MessageEvent } from '../../../../runtime/services/broadcast-channel/message-event'
+const { BroadcastChannel } = service.create('foo').instance
+
+describe('BroadcastChannel', () => {
+  it('is a function', () => {
+    expect(BroadcastChannel).is.an('function')
+  })
+
+  it('has standard APIs', () => {
+    const JamesBond = new BroadcastChannel('007')
+    expect(JamesBond.name).is.an('string')
+    expect(JamesBond.onmessage).is.an('null')
+    expect(JamesBond.postMessage).is.an('function')
+    expect(JamesBond.close).is.an('function')
+
+    expect(JamesBond).to.have.ownProperty('name')
+    expect(JamesBond).to.have.ownProperty('onmessage')
+    expect(JamesBond).not.to.have.ownProperty('postMessage')
+    expect(JamesBond).not.to.have.ownProperty('close')
+    JamesBond.close()
+  })
+
+  it('name attribute is readonly', () => {
+    const Wolverine = new BroadcastChannel('Logan')
+    expect(Wolverine.name).to.equal('Logan')
+    Wolverine.name = 'Wolverine'
+    expect(Wolverine.name).to.equal('Logan')
+    Wolverine.close()
+  })
+
+  describe('basci usage', () => {
+    const Hulk = new BroadcastChannel('Avengers')
+    const Stack = new BroadcastChannel('Avengers')
+    const Steven = new BroadcastChannel('Avengers')
+    const Logan = new BroadcastChannel('Mutants')
+    const Erik = new BroadcastChannel('Mutants')
+
+    beforeEach(() => {
+      Hulk.onmessage = sinon.spy()
+      Stack.onmessage = sinon.spy()
+      Steven.onmessage = sinon.spy()
+      Logan.onmessage = sinon.spy()
+    })
+
+    afterEach(() => {
+      Hulk.onmessage = null
+      Stack.onmessage = null
+      Steven.onmessage = null
+      Logan.onmessage = null
+      Erik.onmessage = null
+    })
+
+    it('trigger onmessage', () => {
+      Hulk.postMessage('Hulk Smash !!!')
+      expect(Hulk.onmessage.callCount).to.be.equal(0)
+      expect(Logan.onmessage.callCount).to.be.equal(0)
+      expect(Stack.onmessage.callCount).to.be.equal(1)
+      expect(Steven.onmessage.callCount).to.be.equal(1)
+    })
+
+    it('don\'t trigger onmessage itself', () => {
+      Logan.postMessage('Any one here ?')
+      expect(Hulk.onmessage.callCount).to.be.equal(0)
+      expect(Logan.onmessage.callCount).to.be.equal(0)
+      expect(Stack.onmessage.callCount).to.be.equal(0)
+      expect(Steven.onmessage.callCount).to.be.equal(0)
+    })
+
+    it('send multi messages', () => {
+      Hulk.postMessage('Hulk Smash !!!')
+      Logan.postMessage('I will fight you !')
+      Stack.postMessage('whatever')
+      Hulk.postMessage('whatever')
+      Stack.postMessage('whatever')
+      Steven.postMessage('whatever')
+      Stack.postMessage('whatever')
+
+      expect(Hulk.onmessage.callCount).to.be.equal(4)
+      expect(Logan.onmessage.callCount).to.be.equal(0)
+      expect(Stack.onmessage.callCount).to.be.equal(3)
+      expect(Steven.onmessage.callCount).to.be.equal(5)
+    })
+
+    it('send string message', () => {
+      Stack.postMessage('I am Iron-Man.')
+
+      expect(Hulk.onmessage.callCount).to.be.equal(1)
+      expect(Steven.onmessage.callCount).to.be.equal(1)
+
+      const event = Hulk.onmessage.firstCall.args[0]
+      expect(event).is.an('object')
+      expect(event.data).is.a('string')
+      expect(event.data).to.be.equal('I am Iron-Man.')
+    })
+
+    it('send object message', () => {
+      const message = {
+        type: 'SOKOVIA ACCORDS',
+        approvedCountry: 117,
+        content: 'The Avengers shall no longer be a private organization.'
+      }
+
+      Stack.postMessage(message)
+
+      const event = Steven.onmessage.firstCall.args[0]
+      expect(event).is.an('object')
+      expect(event.data).is.a('object')
+      expect(event.data).to.deep.equal(message)
+    })
+
+    it('close channel', () => {
+      Hulk.close()
+
+      Steven.postMessage('come to fight !')
+      expect(Hulk.onmessage.callCount).to.be.equal(0)
+      expect(Stack.onmessage.callCount).to.be.equal(1)
+      expect(Steven.onmessage.callCount).to.be.equal(0)
+    })
+
+    it('send message after close', () => {
+      Hulk.close()
+
+      expect(() => { Hulk.postMessage('I am leaving.') }).to.throw(Error)
+
+      expect(Hulk.onmessage.callCount).to.be.equal(0)
+      expect(Logan.onmessage.callCount).to.be.equal(0)
+      expect(Stack.onmessage.callCount).to.be.equal(0)
+      expect(Steven.onmessage.callCount).to.be.equal(0)
+    })
+
+    it('MessageEvent dafault parameters', () => {
+      const event = new MessageEvent()
+
+      expect(event).is.an('object')
+      expect(event.data).to.be.null
+      expect(event.type).to.be.equal('message')
+      expect(event.origin).to.be.equal('')
+      expect(event.target).to.be.null
+      expect(event.source).to.be.null
+      expect(event.timeStamp).to.be.a('number')
+      expect(event.ports).to.be.an('array')
+    })
+
+    it('MessageEvent constructor', () => {
+      const source = { type: 'empty' }
+      const event = new MessageEvent('custom', {
+        source,
+        data: 'Nothing',
+        origin: 'http://127.0.0.1',
+        ports: ['8080']
+      })
+
+      expect(event).is.an('object')
+      expect(event.data).to.be.equal('Nothing')
+      expect(event.type).to.be.equal('custom')
+      expect(event.origin).to.be.equal('http://127.0.0.1')
+      expect(event.target).to.be.null
+      expect(event.source).to.deep.equal(source)
+      expect(event.timeStamp).to.be.a('number')
+      expect(event.ports).to.deep.equal(['8080'])
+    })
+
+    it('use MessageEvent', () => {
+      Steven.postMessage('Be Together.')
+
+      const event = Stack.onmessage.firstCall.args[0]
+      expect(event).is.an('object')
+      expect(event.data).to.be.equal('Be Together.')
+      expect(event.type).to.be.equal('message')
+      expect(event.origin).to.be.equal('')
+      expect(event.target).to.be.null
+      expect(event.timeStamp).to.be.a('number')
+    })
+
+    it('invalid usage', () => {
+      const stranger = {
+        name: 'stranger',
+        close: Erik.close,
+        postMessage: Erik.postMessage
+      }
+
+      stranger.postMessage('hello world.')
+
+      expect(Hulk.onmessage.callCount).to.be.equal(0)
+      expect(Logan.onmessage.callCount).to.be.equal(0)
+      expect(Stack.onmessage.callCount).to.be.equal(0)
+      expect(Steven.onmessage.callCount).to.be.equal(0)
+
+      stranger.close()
+    })
+
+    it('close all', () => {
+      Hulk.close()
+      Stack.close()
+      Steven.close()
+      Logan.close()
+      Erik.close()
+
+      // close again
+      expect(() => {
+        Hulk.close()
+        Stack.close()
+        Steven.close()
+        Logan.close()
+        Erik.close()
+      }).to.not.throw
+    })
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/shared/arrayFrom.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/shared/arrayFrom.js b/test/js-framework/unit/shared/arrayFrom.js
new file mode 100644
index 0000000..c75ea5f
--- /dev/null
+++ b/test/js-framework/unit/shared/arrayFrom.js
@@ -0,0 +1,90 @@
+/*
+ * 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 chai from 'chai'
+const { expect } = chai
+
+// remove original Array.from
+// Array.from = null
+
+import '../../../../runtime/shared/polyfill/arrayFrom'
+
+/* eslint-disable */
+describe('Array.from', () => {
+  it('is a function', () => {
+    expect(Array.from).is.an('function')
+  })
+
+  it('requires an array-like object', () => {
+    expect(() => Array.from()).to.throw(TypeError)
+    expect(() => Array.from(null)).to.throw(TypeError)
+	})
+
+  it('throws with invalid lengths', () => {
+    expect(() => Array.from({ 'length': Infinity })).to.throw(RangeError)
+    expect(() => Array.from({ 'length': Math.pow(2, 32) })).to.throw(RangeError)
+	})
+
+  it('swallows negative lengths', () => {
+    expect(Array.from({ length: -1 }).length).to.equal(0)
+    expect(Array.from({ length: -0 }).length).to.equal(0)
+    expect(Array.from({ length: -Infinity }).length).to.equal(0)
+	})
+
+  it('works with primitives', () => {
+    expect(Array.from(false)).to.deep.equal([])
+    expect(Array.from(true)).to.deep.equal([])
+    expect(Array.from(0)).to.deep.equal([])
+    expect(Array.from(-0)).to.deep.equal([])
+    expect(Array.from(1)).to.deep.equal([])
+    expect(Array.from(-1)).to.deep.equal([])
+    expect(Array.from(Infinity)).to.deep.equal([])
+    expect(Array.from(-Infinity)).to.deep.equal([])
+	})
+
+  it('works with primitive strings', () => {
+    expect(Array.from('')).to.deep.equal([])
+    expect(Array.from('abc')).to.deep.equal(['a', 'b', 'c'])
+    expect(Array.from('a\u5F20\u7FF0')).to.deep.equal(['a', '\u5F20', '\u7FF0'])
+    expect(Array.from('x\uD834\uDF06Y')).to.deep.equal(['x', '\uD834\uDF06', 'Y'])
+	})
+
+  it('works with objects', () => {
+    expect(Array.from({})).to.deep.equal([])
+    expect(Array.from({ name: 'abc' })).to.deep.equal([])
+  })
+
+  it('works with arrays', () => {
+    expect(Array.from([])).to.deep.equal([])
+    expect(Array.from(['a', 2, false, {}])).to.deep.equal(['a', 2, false, {}])
+  })
+
+  it('throws with an invalid mapping function', () => {
+    expect(() => Array.from([], null)).to.throw(TypeError)
+    expect(() => Array.from([], false)).to.throw(TypeError)
+    expect(() => Array.from([], 'true')).to.throw(TypeError)
+    expect(() => Array.from([], 42)).to.throw(TypeError)
+    expect(() => Array.from([], {})).to.throw(TypeError)
+    expect(() => Array.from([], [])).to.throw(TypeError)
+	})
+
+  it('works with mapping function', () => {
+    expect(Array.from([1, 2, 3], n => n * n)).to.deep.equal([1, 4, 9])
+    expect(Array.from('abc', x => x.toUpperCase())).to.deep.equal(['A', 'B', 'C'])
+	})
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/shared/console.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/shared/console.js b/test/js-framework/unit/shared/console.js
new file mode 100644
index 0000000..dcf0b4e
--- /dev/null
+++ b/test/js-framework/unit/shared/console.js
@@ -0,0 +1,227 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import {
+  setNativeConsole,
+  resetNativeConsole
+} from '../../../../runtime/shared/env/console'
+
+const oriEnv = global.WXEnvironment
+const oriConsole = global.console
+
+describe.skip('polyfill for console', () => {
+  const scope = {}
+  const nativeLogSpy = sinon.spy()
+
+  before(() => {
+    delete global.console
+    global.nativeLog = function (...args) {
+      nativeLogSpy(...args)
+    }
+    global.WXEnvironment = {
+      platform: 'iOS',
+      logLevel: 'log'
+    }
+    setNativeConsole()
+    scope.console = global.console
+    // global.console = oriConsole // no output for these test case
+  })
+
+  after(() => {
+    global.WXEnvironment = oriEnv
+    resetNativeConsole()
+    expect(global.console).eql(oriConsole)
+  })
+
+  beforeEach(() => {
+    nativeLogSpy.reset()
+  })
+
+  it('export apis', () => {
+    expect(setNativeConsole).is.a('function')
+    expect(resetNativeConsole).is.a('function')
+  })
+
+  it('nativeLog', () => {
+    expect(global.nativeLog).is.a('function')
+    global.nativeLog('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(1)
+  })
+
+  it('console apis', () => {
+    expect(scope.console).is.a('object')
+    expect(scope.console.debug).is.a('function')
+    expect(scope.console.log).is.a('function')
+    expect(scope.console.info).is.a('function')
+    expect(scope.console.warn).is.a('function')
+    expect(scope.console.error).is.a('function')
+  })
+
+  it('no debug message', () => {
+    scope.console.debug('a debug message')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+  })
+
+  it('a log message', () => {
+    scope.console.log('a log message')
+    expect(nativeLogSpy.callCount).to.be.equal(1)
+    expect(nativeLogSpy.firstCall.args).to.deep.equal(
+      ['a log message', '__LOG']
+    )
+  })
+
+  it('multi log message', () => {
+    scope.console.log('A', 'B', 'C')
+    scope.console.log('D', 'E')
+    expect(nativeLogSpy.callCount).to.be.equal(2)
+    expect(nativeLogSpy.firstCall.args).to.deep.equal(
+      ['A', 'B', 'C', '__LOG']
+    )
+    expect(nativeLogSpy.lastCall.args).to.deep.equal(
+      ['D', 'E', '__LOG']
+    )
+  })
+
+  it('an info message', () => {
+    scope.console.info('a info message')
+    expect(nativeLogSpy.callCount).to.be.equal(1)
+    expect(nativeLogSpy.firstCall.args).to.deep.equal(
+      ['a info message', '__INFO']
+    )
+  })
+
+  it('a warn message', () => {
+    scope.console.warn('a warn message')
+    expect(nativeLogSpy.callCount).to.be.equal(1)
+    expect(nativeLogSpy.firstCall.args).eql(
+      ['a warn message', '__WARN']
+    )
+  })
+
+  it('a error message', () => {
+    scope.console.error('a error message')
+    expect(nativeLogSpy.callCount).to.be.equal(1)
+    expect(nativeLogSpy.firstCall.args).eql(
+      ['a error message', '__ERROR']
+    )
+  })
+
+  it('debug logLevel', () => {
+    global.WXEnvironment.logLevel = 'debug'
+    scope.console.debug('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(1)
+    scope.console.log('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(2)
+    scope.console.info('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(3)
+    scope.console.warn('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(4)
+    scope.console.error('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(5)
+  })
+
+  it('log logLevel', () => {
+    global.WXEnvironment.logLevel = 'log'
+    scope.console.debug('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.log('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(1)
+    scope.console.info('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(2)
+    scope.console.warn('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(3)
+    scope.console.error('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(4)
+  })
+
+  it('info logLevel', () => {
+    global.WXEnvironment.logLevel = 'info'
+    scope.console.debug('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.log('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.info('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(1)
+    scope.console.warn('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(2)
+    scope.console.error('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(3)
+  })
+
+  it('warn logLevel', () => {
+    global.WXEnvironment.logLevel = 'warn'
+    scope.console.debug('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.log('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.info('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.warn('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(1)
+    scope.console.error('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(2)
+  })
+
+  it('error logLevel', () => {
+    global.WXEnvironment.logLevel = 'error'
+    scope.console.debug('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.log('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.info('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.warn('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.error('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(1)
+  })
+
+  it('off logLevel', () => {
+    global.WXEnvironment.logLevel = 'off'
+    scope.console.debug('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.log('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.info('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.warn('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.error('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+  })
+
+  it('unknown logLevel', () => {
+    global.WXEnvironment.logLevel = 'log'
+    scope.console.debug('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(0)
+    scope.console.log('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(1)
+    scope.console.info('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(2)
+    scope.console.warn('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(3)
+    scope.console.error('whatever')
+    expect(nativeLogSpy.callCount).to.be.equal(4)
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/shared/index.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/shared/index.js b/test/js-framework/unit/shared/index.js
new file mode 100644
index 0000000..1a6f815
--- /dev/null
+++ b/test/js-framework/unit/shared/index.js
@@ -0,0 +1,73 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import * as shared from '../../../../runtime/shared/'
+
+describe('a polyfill of', () => {
+  it('Promise', () => {
+    expect(typeof Promise).to.be.equal('function')
+    expect(() => {
+      new Promise(sinon.spy()).then(sinon.spy())
+      Promise.all()
+      Promise.race()
+      Promise.resolve()
+      Promise.reject()
+    }).to.not.throw
+  })
+
+  it('Object.assign', () => {
+    expect(typeof Object.assign).to.be.equal('function')
+  })
+
+  it('setTimeout', () => {
+    expect(typeof setTimeout).to.be.equal('function')
+  })
+
+  it('console.log', () => {
+    expect(typeof console.log).to.be.equal('function')
+  })
+})
+
+describe('freeze the prototype of build-in objects', function () {
+  before(() => {
+    shared.freezePrototype()
+  })
+
+  it('Object & Object.prototype', () => {
+    expect(Object).to.be.frozen
+    // expect(Object.prototype).to.be.frozen
+  })
+  it('Array & Array.prototype', () => {
+    expect(Array).to.be.frozen
+    expect(Array.prototype).to.be.frozen
+  })
+
+  it('String.prototype', () => { expect(String.prototype).to.be.frozen })
+  it('Number.prototype', () => { expect(Number.prototype).to.be.frozen })
+  it('Boolean.prototype', () => { expect(Boolean.prototype).to.be.frozen })
+  it('Error.prototype', () => { expect(Error.prototype).to.be.frozen })
+  it('Date.prototype', () => { expect(Date.prototype).to.be.frozen })
+  it('Math.prototype', () => { expect(Math.prototype).to.be.frozen })
+  it('RegExp.prototype', () => { expect(RegExp.prototype).to.be.frozen })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/shared/objectAssign.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/shared/objectAssign.js b/test/js-framework/unit/shared/objectAssign.js
new file mode 100644
index 0000000..b20329f
--- /dev/null
+++ b/test/js-framework/unit/shared/objectAssign.js
@@ -0,0 +1,50 @@
+/*
+ * 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 chai from 'chai'
+const { expect } = chai
+
+import '../../../../runtime/shared/polyfill/objectAssign'
+
+describe('object.assign', () => {
+  it('is a function', () => {
+    expect(Object.assign).is.an('function')
+  })
+
+  it('source to taget', () => {
+    const target = {
+      a: 'a',
+      b: 'b'
+    }
+    const src1 = {
+      b: 'bb',
+      c: 'c'
+    }
+    const src2 = {
+      d: 'd',
+      a: 'aa'
+    }
+    Object.assign(target, src1, src2)
+    expect(target).to.deep.equal({
+      a: 'aa',
+      b: 'bb',
+      c: 'c',
+      d: 'd'
+    })
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/shared/objectSetPrototypeOf.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/shared/objectSetPrototypeOf.js b/test/js-framework/unit/shared/objectSetPrototypeOf.js
new file mode 100644
index 0000000..b6fe66e
--- /dev/null
+++ b/test/js-framework/unit/shared/objectSetPrototypeOf.js
@@ -0,0 +1,42 @@
+/*
+ * 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 chai from 'chai'
+const { expect } = chai
+
+import '../../../../runtime/shared/polyfill/objectSetPrototypeOf'
+
+/* eslint-disable */
+describe('Object.setPrototypeOf', () => {
+  it('is a function', () => {
+    expect(Object.setPrototypeOf).is.an('function')
+  })
+
+  it('be able to set prototype', () => {
+    const JonSnow = { name: 'Jon Snow' }
+    Object.setPrototypeOf(JonSnow, {
+      know: function() { return 'nothing' }
+    })
+
+    expect(Object.hasOwnProperty.call(JonSnow, 'name')).to.be.true
+    expect(Object.hasOwnProperty.call(JonSnow, 'know')).to.be.false
+
+    expect(JonSnow.know()).to.equal('nothing')
+
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/vanilla/index.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/vanilla/index.js b/test/js-framework/unit/vanilla/index.js
new file mode 100644
index 0000000..49ea29a
--- /dev/null
+++ b/test/js-framework/unit/vanilla/index.js
@@ -0,0 +1,159 @@
+/*
+ * 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 { expect } from 'chai'
+
+import vanilla from '../../../../runtime/frameworks/vanilla'
+import runtime from '../../../../runtime/api'
+
+// clear ref property in vdom
+function clearRefs (json) {
+  delete json.ref
+  if (json.children) {
+    json.children.forEach(clearRefs)
+  }
+}
+
+describe('vanilla test', () => {
+  it('standard APIs', () => {
+    expect(vanilla.init).to.be.a('function')
+    expect(vanilla.createInstance).to.be.a('function')
+    expect(vanilla.destroyInstance).to.be.a('function')
+    expect(vanilla.getRoot).to.be.a('function')
+    expect(vanilla.receiveTasks).to.be.a('function')
+  })
+
+  it('create & destroy instance', () => {
+    const id = 'basic-demo'
+    vanilla.init(runtime.config)
+    vanilla.createInstance(id, `
+      var body = document.createBody()
+      var div = document.createElement('div')
+      body.appendChild(div)
+    `, {})
+    vanilla.destroyInstance(id)
+  })
+
+  it('getRoot', () => {
+    const id = 'sample-demo'
+    const code = `
+      var body = document.createBody()
+      var div = document.createElement('div')
+      div.setStyle('color', 'red')
+      var span = document.createElement('span')
+      span.setAttr('value', 'text node')
+      div.appendChild(span)
+      body.appendChild(div)
+    `
+    vanilla.init(runtime.config)
+    vanilla.createInstance(id, code, {})
+
+    const vdom = vanilla.getRoot(id)
+    clearRefs(vdom)
+    expect(vdom).to.deep.equal({
+      // ref: '_root',
+      type: 'div',
+      attr: {},
+      style: {},
+      children: [
+        {
+          // ref: '8',
+          type: 'div',
+          attr: {},
+          style: {
+            color: 'red'
+          },
+          children: [
+            {
+              // ref: '9',
+              type: 'span',
+              attr: {
+                value: 'text node'
+              },
+              style: {}
+            }
+          ]
+        }
+      ]
+    })
+
+    vanilla.destroyInstance(id)
+  })
+
+  describe('receiveTasks', () => {
+    const id = 'task-demo'
+
+    before(() => {
+      vanilla.init(runtime.config)
+      vanilla.createInstance(id, `
+        var body = document.createBody()
+        body.addEvent('click', function() {
+          this.setStyle('width', '200')
+        })
+        document.addCallback(function(message) {
+          body.setAttr('from', message)
+        })
+        `, {})
+    })
+
+    after(() => {
+      vanilla.destroyInstance(id)
+    })
+
+    it('initial state', () => {
+      // initial style and attr in empty
+      expect(vanilla.getRoot(id).style).to.deep.equal({})
+      expect(vanilla.getRoot(id).attr).to.deep.equal({})
+
+      // empty task
+      vanilla.receiveTasks(id, [])
+    })
+
+    it('invalid tasks', () => {
+      vanilla.receiveTasks('invalid id', [])
+      vanilla.receiveTasks(id, {})
+      vanilla.receiveTasks(id, [{
+        method: 'whatever',
+        args: []
+      }])
+    })
+
+    it('fireEvent', () => {
+      vanilla.receiveTasks(id, [{
+        method: 'fireEvent',
+        args: ['_root', 'click']
+      }])
+      expect(vanilla.getRoot(id).style).to.deep.equal({ width: '200' })
+    })
+
+    it('callback', () => {
+      vanilla.receiveTasks(id, [{
+        method: 'callback',
+        args: ['1', 'callback']
+      }])
+      expect(vanilla.getRoot(id).attr).to.deep.equal({ from: 'callback' })
+
+      // last callback
+      vanilla.receiveTasks(id, [{
+        method: 'callback',
+        args: ['1', 'last message', true]
+      }])
+      expect(vanilla.getRoot(id).attr).to.deep.equal({ from: 'last message' })
+    })
+  })
+})


[11/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/runtime/legacy-framework.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/runtime/legacy-framework.js b/html5/test/unit/runtime/legacy-framework.js
deleted file mode 100644
index a82d23b..0000000
--- a/html5/test/unit/runtime/legacy-framework.js
+++ /dev/null
@@ -1,348 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const {
-  expect
-} = chai
-chai.use(sinonChai)
-
-import runtime from '../../../runtime'
-import frameworks from '../../../frameworks'
-import defaultConfig from '../../../frameworks/legacy/config'
-import { init as resetTaskHandler } from '../../../runtime/bridge/TaskCenter'
-
-const { init, config } = runtime
-config.frameworks = frameworks
-runtime.setNativeConsole()
-
-import Vm from '../../../frameworks/legacy/vm'
-import { clearModules, getModule } from '../../../frameworks/legacy/app/register'
-
-const framework = init(config)
-
-function clearRefs (json) {
-  delete json.ref
-  if (json.children) {
-    json.children.forEach(clearRefs)
-  }
-}
-
-describe('framework entry', () => {
-  const oriCallNative = global.callNative
-  const oriCallAddElement = global.callAddElement
-  const oriDocumentHandler = config.Document.handler
-  const callNativeSpy = sinon.spy()
-  const callAddElementSpy = sinon.spy()
-  const documentHandlerSpy = sinon.spy()
-  const instanceId = Date.now() + ''
-
-  before(() => {
-    global.callNative = (id, tasks, callbackId) => {
-      callNativeSpy(id, tasks, callbackId)
-      /* istanbul ignore if */
-      if (callbackId !== '-1') {
-        framework.callJS(id, [{
-          method: 'callback',
-          args: [callbackId, null, true]
-        }])
-      }
-    }
-    global.callAddElement = (name, id, ref, json, index, callbackId) => {
-      callAddElementSpy(name, ref, json, index, callbackId)
-      /* istanbul ignore if */
-      if (callbackId !== '-1') {
-        framework.callJS(id, [{
-          method: 'callback',
-          args: [callbackId, null, true]
-        }])
-      }
-    }
-    config.Document.handler = oriDocumentHandler
-    resetTaskHandler()
-  })
-
-  afterEach(() => {
-    callNativeSpy.reset()
-    callAddElementSpy.reset()
-    documentHandlerSpy.reset()
-  })
-
-  after(() => {
-    config.Document.handler = oriDocumentHandler
-    global.callNative = oriCallNative
-    global.callAddElement = oriCallAddElement
-  })
-
-  describe('createInstance', () => {
-    it('a simple bundle', () => {
-      const code = `
-        define('@weex-component/main',
-          function(require, exports, module) {
-            module.exports = {
-              data: function() {
-                return {
-                  text: 'Hello World',
-                  showText: true
-                };
-              }
-            }
-
-            ;module.exports.style = {}
-
-            ;module.exports.template = {
-              'type': 'container',
-              'children': [{
-                'type': 'text',
-                'shown': function() {
-                  return this.showText
-                },
-                'attr': {
-                  'value': function() {
-                    return this.text
-                  }
-                }
-              }]
-            }
-          }
-        )
-
-        bootstrap('@weex-component/main')
-      `
-      framework.createInstance(instanceId, code)
-      expect(callNativeSpy.callCount).to.be.equal(2)
-      expect(callAddElementSpy.callCount).to.be.equal(1)
-
-      expect(callNativeSpy.firstCall.args[0]).to.be.equal(instanceId)
-      expect(callNativeSpy.firstCall.args[1]).to.deep.equal([{
-        module: 'dom',
-        method: 'createBody',
-        args: [{
-          ref: '_root',
-          type: 'container',
-          attr: {},
-          style: {}
-        }]
-      }])
-      // expect(callNativeSpy.firstCall.args[2]).to.not.equal('-1')
-
-      expect(callAddElementSpy.firstCall.args[0]).to.be.equal(instanceId)
-      delete callAddElementSpy.firstCall.args[1].ref
-      expect(callAddElementSpy.firstCall.args[1]).to.deep.equal({
-        type: 'text',
-        attr: { value: 'Hello World' },
-        style: {}
-      })
-
-      // expect(callNativeSpy.secondCall.args[2]).to.not.equal('-1')
-
-      expect(callNativeSpy.secondCall.args[0]).to.be.equal(instanceId)
-      expect(callNativeSpy.secondCall.args[1]).to.deep.equal([{
-        module: 'dom',
-        method: 'createFinish',
-        args: []
-      }])
-      // expect(callNativeSpy.thirdCall.args[2]).to.not.equal('-1')
-    })
-
-    it('with a exist instanceId', () => {
-      const code = ''
-      const result = framework.createInstance(instanceId, code)
-      expect(result).to.be.an.instanceof(Error)
-    })
-  })
-
-  describe('getRoot', () => {
-    it('with a exist instanceId', () => {
-      const json = framework.getRoot(instanceId)
-      expect(json.ref).eql('_root')
-      clearRefs(json)
-      const expectJSON = {
-        type: 'container',
-        attr: {},
-        style: {},
-        children: [{
-          type: 'text',
-          attr: {
-            value: 'Hello World'
-          },
-          style: {}
-        }]
-      }
-      expect(json).to.be.deep.equal(expectJSON)
-    })
-  })
-
-  describe('callJS', () => {
-    it('fireEvent with no params', () => {
-      framework.callJS()
-    })
-
-    it('fireEvent with a exist instanceId', () => {
-      framework.callJS(instanceId, [{
-        method: 'fireEvent',
-        args: []
-      }])
-    })
-
-    it('callback with a exist instanceId', () => {
-      framework.callJS(instanceId, [{
-        method: 'callback',
-        args: []
-      }])
-    })
-
-    it('with a non-exist instanceId', () => {
-      const result = framework.callJS('123', [{
-        method: 'fireEvent',
-        args: []
-      }])
-      expect(result).to.be.an.instanceof(Error)
-    })
-
-    it('with a non-array tasks', () => {
-      const result = framework.callJS(instanceId, {
-        method: 'fireEvent',
-        args: []
-      })
-      expect(result).to.be.an.instanceof(Error)
-    })
-  })
-
-  describe('refreshInstance', () => {
-    it('modify showText to false', () => {
-      const json = framework.getRoot(instanceId)
-      const textRef = json.children[0].ref
-      framework.refreshInstance(instanceId, { showText: false })
-      expect(callNativeSpy.callCount).to.be.equal(2)
-      expect(callNativeSpy.firstCall.args[0]).to.be.equal(instanceId)
-      expect(callNativeSpy.firstCall.args[1]).to.deep.equal([{
-        module: 'dom',
-        method: 'removeElement',
-        args: [textRef]
-      }])
-      // expect(callNativeSpy.firstCall.args[2]).to.be.equal('-1')
-
-      expect(callNativeSpy.secondCall.args[0]).to.be.equal(instanceId)
-      expect(callNativeSpy.secondCall.args[1]).to.deep.equal([{
-        module: 'dom',
-        method: 'refreshFinish',
-        args: []
-      }])
-      // expect(callNativeSpy.secondCall.args[2]).to.be.equal('-1')
-    })
-
-    it('with a non-exist instanceId', () => {
-      const result = framework.refreshInstance('123', {})
-      expect(result).to.be.an.instanceof(Error)
-    })
-  })
-
-  describe('destroyInstance', () => {
-    it('with no params', () => {
-      framework.destroyInstance()
-    })
-
-    it('with a exist instanceId', () => {
-      const result = framework.destroyInstance(instanceId)
-      expect(result[instanceId]).to.be.undefined
-    })
-
-    it('with a non-exist instanceId', () => {
-      const result = framework.destroyInstance('123')
-      expect(result).to.be.an.instanceof(Error)
-    })
-  })
-
-  describe('registerComponents', () => {
-    it('with old format', () => {
-      framework.registerComponents(['a', 'b', 'c'])
-      expect(defaultConfig.nativeComponentMap).to.contain.keys('a', 'b', 'c')
-    })
-
-    it('with new format', () => {
-      framework.registerComponents([{
-        type: 'd',
-        append: 'tree'
-      }])
-      expect(defaultConfig.nativeComponentMap).to.contain.keys('d')
-      expect(defaultConfig.nativeComponentMap['d']).to.be.deep.equal({
-        type: 'd',
-        append: 'tree'
-      })
-    })
-
-    it('with non-array', () => {
-      framework.registerComponents({
-        type: 'e'
-      })
-      expect(defaultConfig.nativeComponentMap).not.contain.keys('e')
-    })
-
-    it('with methods', () => {
-      const components = [{
-        type: 'x',
-        methods: ['foo', 'bar']
-      }, {
-        type: 'y',
-        methods: []
-      }, {
-        type: 'z',
-        methods: null
-      }]
-      framework.registerComponents(components)
-      expect(defaultConfig.nativeComponentMap).to.contain.keys('x', 'y', 'z')
-    })
-  })
-
-  describe('register modules', () => {
-    it('with object of modules', () => {
-      clearModules()
-      const modules = {
-        a: [{
-          name: 'b',
-          args: ['string']
-        }]
-      }
-
-      framework.registerModules(modules)
-      expect(getModule('b')).an.object
-      clearModules()
-    })
-  })
-
-  describe('register methods', () => {
-    it('with object of methods', () => {
-      const methods = {
-        a: sinon.spy()
-      }
-
-      framework.registerMethods(methods)
-      expect(Vm.prototype.a).a.function
-      delete Vm.prototype.a
-    })
-  })
-})
-
-describe('config', () => {
-  it('config is an object', () => {
-    init({})
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/runtime/runner/helper.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/runtime/runner/helper.js b/html5/test/unit/runtime/runner/helper.js
deleted file mode 100644
index dce81c7..0000000
--- a/html5/test/unit/runtime/runner/helper.js
+++ /dev/null
@@ -1,73 +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 runtime from '../../../../runtime'
-import frameworks from '../../../../frameworks'
-import services from '../../../../services'
-
-const defaultModules = {
-  animation: ['transition'],
-  clipboard: ['getString', 'setString'],
-  dom: ['scrollToElement', 'getComponentRect', 'addRule'],
-  modal: ['alert', 'confirm', 'prompt', 'toast'],
-  navigator: ['push', 'pop'],
-  picker: ['pick', 'pickDate', 'pickTime'],
-  storage: ['setItem', 'getItem', 'removeItem', 'length', 'getAllKeys'],
-  stream: ['fetch'],
-  timer: ['setTimeout', 'setInterval'],
-  WebSocket: ['send', 'close', 'onopen', 'onmessage', 'onclose', 'onerror'],
-  webview: ['goBack', 'goForward', 'reload']
-}
-
-const defaultComponents = [
-  'div', 'text', 'image', 'input', 'a', 'switch', 'scroller', 'list', 'cell',
-  'slider', 'indicator', 'loading', 'refresh', 'textarea', 'web', 'video',
-  'embed', 'richtext'
-]
-
-export function createContext () {
-  const currentConfig = Object.assign({}, runtime.config)
-  currentConfig.frameworks = frameworks
-
-  for (const serviceName in services) {
-    runtime.service.register(serviceName, services[serviceName])
-  }
-
-  const context = runtime.init(currentConfig)
-
-  context.registerModules(defaultModules)
-  context.registerComponents(defaultComponents)
-
-  return context
-}
-
-// execute code and return the generated vdom tree
-export function execute (type, code) {
-  const context = createContext()
-  return new Promise(resolve => {
-    const id = String(Date.now() + Math.random())
-    context.createInstance(id, `
-      // { "framework": "${type}" }
-      ${code}
-    `)
-    setTimeout(() => {
-      resolve(context.getRoot(id))
-    }, 10)
-  })
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/runtime/runner/vanilla.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/runtime/runner/vanilla.js b/html5/test/unit/runtime/runner/vanilla.js
deleted file mode 100644
index 2bb6226..0000000
--- a/html5/test/unit/runtime/runner/vanilla.js
+++ /dev/null
@@ -1,59 +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 { expect } from 'chai'
-import { execute } from './helper'
-
-describe('Vanilla Examples', () => {
-  it('execute vanilla code', (done) => {
-    execute('Vanilla', `
-      const body = document.createElement('div')
-      document.documentElement.appendChild(body)
-    `).then($root => {
-      expect($root.type).to.be.equal('div')
-    }).then(done)
-  })
-
-  it('instert child nodes', (done) => {
-    execute('Vanilla', `
-      const body = document.createElement('div')
-      body.appendChild(document.createElement('text'))
-      body.appendChild(document.createElement('image'))
-      document.documentElement.appendChild(body)
-    `).then($root => {
-      expect($root.type).to.be.equal('div')
-      expect($root.children[0].type).to.be.equal('text')
-      expect($root.children[1].type).to.be.equal('image')
-    }).then(done)
-  })
-
-  it('add text nodes', (done) => {
-    execute('Vanilla', `
-      const body = document.createElement('div')
-      const text = document.createElement('text')
-      text.setAttr('value', 'whatever')
-      body.appendChild(text)
-      document.documentElement.appendChild(body)
-    `).then($root => {
-      expect($root.type).to.be.equal('div')
-      expect($root.children[0].type).to.be.equal('text')
-      expect($root.children[0].attr.value).to.be.equal('whatever')
-    }).then(done)
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/runtime/runner/vue.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/runtime/runner/vue.js b/html5/test/unit/runtime/runner/vue.js
deleted file mode 100644
index bdb0006..0000000
--- a/html5/test/unit/runtime/runner/vue.js
+++ /dev/null
@@ -1,67 +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 { expect } from 'chai'
-import { execute } from './helper'
-import { compile } from 'weex-template-compiler'
-
-function toFunction (content) {
-  return `function () { ${content} }`
-}
-
-function compileAndExecute (template, additional) {
-  const { render, staticRenderFns } = compile(template)
-  return execute('Vue', `
-    new Vue({
-      el: '#root',
-      render: ${toFunction(render)},
-      staticRenderFns: [${staticRenderFns.map(toFunction).join(',')}],
-      ${additional}
-    })
-  `)
-}
-
-describe('Vue Examples', () => {
-  it('execute Vue code', (done) => {
-    execute('Vue', `
-      new Vue({
-        el: 'body',
-        render (h) {
-          return h('div', {}, [h('text', {}, 'Hello World')])
-        }
-      })
-    `).then($root => {
-      expect($root.type).to.be.equal('div')
-      expect($root.children[0].type).to.be.equal('text')
-      expect($root.children[0].attr.value).to.be.equal('Hello World')
-    }).then(done)
-  })
-
-  it('compile and execute', (done) => {
-    compileAndExecute(`
-      <div>
-        <text>Hello World</text>
-      </div>
-    `).then($root => {
-      expect($root.type).to.be.equal('div')
-      expect($root.children[0].type).to.be.equal('text')
-      expect($root.children[0].attr.value).to.be.equal('Hello World')
-    }).then(done)
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/runtime/utils.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/runtime/utils.js b/html5/test/unit/runtime/utils.js
deleted file mode 100644
index 6480705..0000000
--- a/html5/test/unit/runtime/utils.js
+++ /dev/null
@@ -1,41 +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 { expect } from 'chai'
-import * as utils from '../../../runtime/utils'
-
-describe('utils', () => {
-  it('isEmpty', () => {
-    expect(utils.isEmpty()).to.be.true
-    expect(utils.isEmpty(0)).to.be.true
-    expect(utils.isEmpty(null)).to.be.true
-    expect(utils.isEmpty(undefined)).to.be.true
-    expect(utils.isEmpty(false)).to.be.true
-    expect(utils.isEmpty(true)).to.be.true
-    expect(utils.isEmpty(NaN)).to.be.true
-    expect(utils.isEmpty([])).to.be.true
-    expect(utils.isEmpty({})).to.be.true
-    expect(utils.isEmpty(Object.create(null))).to.be.true
-    expect(utils.isEmpty(Object.create({}))).to.be.true
-    expect(utils.isEmpty(Object.create({ x: '' }))).to.be.true
-
-    expect(utils.isEmpty({ abc: '' })).to.be.false
-    expect(utils.isEmpty([0])).to.be.false
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/runtime/vdom/index.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/runtime/vdom/index.js b/html5/test/unit/runtime/vdom/index.js
deleted file mode 100644
index 5270014..0000000
--- a/html5/test/unit/runtime/vdom/index.js
+++ /dev/null
@@ -1,639 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import {
-  Document,
-  Element,
-  Comment,
-  isWeexElement,
-  registerElement,
-  clearWeexElements
-} from '../../../../runtime/vdom'
-
-describe('document constructor', () => {
-  it('create & destroy document', () => {
-    const doc = new Document('foo', 'http://path/to/url')
-    expect(doc).is.an.object
-    expect(doc.id).eql('foo')
-    expect(doc.URL).eql('http://path/to/url')
-    expect(doc.documentElement).is.an.object
-    expect(doc.documentElement.role).equal('documentElement')
-    doc.destroy()
-  })
-})
-
-describe('component methods management', () => {
-  before(() => {
-    registerElement('x', ['foo', 'bar'])
-    registerElement('y', [])
-    registerElement('z')
-  })
-
-  after(() => {
-    clearWeexElements()
-  })
-
-  it('has registered element types', () => {
-    expect(isWeexElement('x')).to.be.true
-  })
-
-  it('will call component method', () => {
-    const spy = sinon.spy()
-    const doc = new Document('test', '', spy)
-    const x = new Element('x')
-    const y = new Element('y')
-    const z = new Element('z')
-    const n = new Element('n')
-    expect(x.foo).is.function
-    expect(x.bar).is.function
-    expect(x.baz).is.undefined
-    expect(y.foo).is.undefined
-    expect(z.foo).is.undefined
-    expect(n.foo).is.undefined
-
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    doc.body.appendChild(x)
-    doc.body.appendChild(y)
-    doc.body.appendChild(z)
-    doc.body.appendChild(n)
-    expect(spy.args.length).eql(5)
-
-    x.foo(1, 2, 3)
-    expect(spy.args.length).eql(6)
-    expect(spy.args[5]).eql([[{ component: 'x', method: 'foo', ref: x.ref, args: [1, 2, 3] }]])
-  })
-})
-
-describe('document methods', () => {
-  let doc
-
-  beforeEach(() => {
-    doc = new Document('foo', null, function () {})
-  })
-
-  afterEach(() => {
-    doc.destroy()
-  })
-
-  it('open & close with a listener', () => {
-    expect(doc.listener.batched).is.false
-    doc.close()
-    expect(doc.listener.batched).is.true
-    doc.open()
-    expect(doc.listener.batched).is.false
-  })
-
-  it('create body', () => {
-    const ele = doc.createBody('container',
-      { attr: { id: 'a' }, style: { fontSize: 16 }})
-    expect(ele.role).equal('body')
-    expect(ele.attr).to.have.a.property('id', 'a')
-    expect(ele.style).to.have.a.property('fontSize', 16)
-    expect(ele).have.a.property('ref')
-    expect(ele).have.a.property('children')
-    expect(ele).have.a.property('pureChildren')
-    expect(ele).have.a.property('insertBefore')
-    expect(ele).have.a.property('setStyle')
-
-    const ref = ele.ref
-    expect(doc.nodeMap[ref]).equal(ele)
-  })
-
-  it('create element', () => {
-    const ele = doc.createElement('container',
-      { attr: { id: 'a' }, style: { fontSize: 16 }})
-    expect(ele.attr).to.have.a.property('id', 'a')
-    expect(ele.style).to.have.a.property('fontSize', 16)
-    expect(ele).have.a.property('ref')
-    expect(ele).have.a.property('children')
-    expect(ele).have.a.property('pureChildren')
-    expect(ele).have.a.property('insertBefore')
-    expect(ele).have.a.property('setStyle')
-
-    expect(doc.nodeMap[ele.ref]).is.undefined
-    doc.documentElement.appendChild(ele)
-    expect(doc.nodeMap[ele.ref]).equal(ele)
-  })
-
-  it('create comment', () => {
-    const comment = doc.createComment('start')
-    expect(comment).have.a.property('ref')
-    expect(comment).not.have.a.property('appendChild')
-    expect(comment).have.a.property('value', 'start')
-    expect(comment.toString()).eql('<!-- start -->')
-
-    const ref = comment.ref
-    expect(doc.nodeMap[ref]).is.undefined
-    doc.documentElement.appendChild(comment)
-    expect(doc.nodeMap[ref]).equal(comment)
-  })
-})
-
-describe('Element in document methods', () => {
-  let doc, el, el2, el3
-
-  beforeEach(() => {
-    doc = new Document('foo', null, function () {})
-    el = new Element('bar', {
-      attr: { a: 11, b: 12 },
-      style: { c: 13, d: 14 },
-      classStyle: { a: 211, c: 213 }
-    })
-    el2 = new Element('baz', {
-      attr: { a: 21, b: 22 },
-      style: { c: 23, d: 24 },
-      classStyle: { a: 221, c: 223 }
-    })
-    el3 = new Element('qux', {
-      attr: { a: 31, b: 32 },
-      style: { c: 33, d: 34 },
-      classStyle: { a: 231, c: 233 }
-    })
-  })
-
-  afterEach(() => {
-    doc.destroy()
-  })
-
-  it('init correctly', () => {
-    expect(el).is.an.object
-    expect(el.type).eql('bar')
-    expect(el.attr).eql({ a: 11, b: 12 })
-    expect(el.style).eql({ c: 13, d: 14 })
-    expect(el.event).eql({})
-    expect(el.children).eql([])
-    expect(el.pureChildren).eql([])
-    expect(doc.nodeMap).is.an.object
-    expect(doc.documentElement).is.an.object
-    expect(Object.keys(doc.nodeMap)).eql([doc.documentElement.ref])
-    doc.documentElement.appendChild(el)
-    doc.documentElement.appendChild(el2)
-    el2.appendChild(el3)
-    expect(Object.keys(doc.nodeMap)).eql([
-      doc.documentElement.ref,
-      el.ref
-    ])
-  })
-
-  it('has correct exports', () => {
-    const ref = el.ref
-    const finalStyle = el.toStyle()
-    expect(finalStyle).eql({ a: 211, c: 13, d: 14 })
-    expect(el.toJSON()).eql({
-      ref: ref, type: 'bar',
-      attr: el.attr, style: finalStyle
-    })
-    expect(el.toString()).eql(
-      '<bar attr={"a":11,"b":12} style={"a":211,"c":13,"d":14}></bar>')
-  })
-
-  it('createBody', () => {
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    expect(doc.body).is.an.object
-    expect(doc.body.role).eql('body')
-    expect(doc.body.type).eql('r')
-    expect(doc.body.docId).eql('foo')
-  })
-
-  it('appendChild', () => {
-    el.appendChild(el2)
-    expect(el.children.length).eql(1)
-    expect(el.children[0]).equal(el2)
-    expect(el2.parentNode.ref).eql(el.ref)
-
-    expect(el.docId).is.not.ok
-    expect(el2.docId).is.not.ok
-
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    doc.body.appendChild(el)
-    expect(doc.body.children.length).eql(1)
-    expect(el.parentNode.ref).eql(doc.body.ref)
-    expect(el.docId).is.ok
-    expect(el2.docId).is.ok
-
-    expect(el3.docId).is.not.ok
-    el.appendChild(el3)
-    expect(el.children.length).eql(2)
-    expect(el.children[0]).equal(el2)
-    expect(el.children[1]).equal(el3)
-    expect(el3.parentNode.ref).eql(el.ref)
-    expect(el3.docId).is.ok
-
-    expect(el2.previousSibling).is.not.ok
-    expect(el2.nextSibling).eql(el3)
-    expect(el3.previousSibling).eql(el2)
-    expect(el3.nextSibling).is.not.ok
-  })
-
-  it('insertBefore', () => {
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    doc.body.appendChild(el)
-    expect(el.parentNode.ref).eql(doc.body.ref)
-
-    el.appendChild(el2)
-    expect(el2.parentNode.ref).eql(el.ref)
-    expect(el.children.length).eql(1)
-    expect(el.children[0]).equal(el2)
-
-    el.insertBefore(el3, el2)
-    expect(el.children.length).eql(2)
-    expect(el.children[0]).equal(el3)
-    expect(el.children[1]).equal(el2)
-    expect(el3.parentNode.ref).eql(el.ref)
-
-    expect(el.docId).eql('foo')
-    expect(el2.docId).eql('foo')
-    expect(el3.docId).eql('foo')
-
-    expect(el3.previousSibling).is.not.ok
-    expect(el3.nextSibling).eql(el2)
-    expect(el2.previousSibling).eql(el3)
-    expect(el2.nextSibling).is.not.ok
-
-    el.insertBefore(el2, el3)
-    expect(el.children.length).eql(2)
-    expect(el.children[0]).equal(el2)
-    expect(el.children[1]).equal(el3)
-
-    expect(el.docId).eql('foo')
-    expect(el2.docId).eql('foo')
-    expect(el3.docId).eql('foo')
-
-    expect(el2.previousSibling).is.not.ok
-    expect(el2.nextSibling).eql(el3)
-    expect(el3.previousSibling).eql(el2)
-    expect(el3.nextSibling).is.not.ok
-  })
-
-  it('insertAfter', () => {
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    doc.body.appendChild(el)
-    expect(el.parentNode.ref).eql(doc.body.ref)
-
-    el.appendChild(el2)
-    el.insertAfter(el3, el2)
-    expect(el.children.length).eql(2)
-    expect(el.children[0]).equal(el2)
-    expect(el.children[1]).equal(el3)
-    expect(el2.parentNode.ref).eql(el.ref)
-    expect(el3.parentNode.ref).eql(el.ref)
-
-    expect(el.docId).eql('foo')
-    expect(el2.docId).eql('foo')
-    expect(el3.docId).eql('foo')
-
-    expect(el2.previousSibling).is.not.ok
-    expect(el2.nextSibling).eql(el3)
-    expect(el3.previousSibling).eql(el2)
-    expect(el3.nextSibling).is.not.ok
-
-    el.insertAfter(el2, el3, true)
-    expect(el.children.length).eql(2)
-    expect(el.children[0]).equal(el3)
-    expect(el.children[1]).equal(el2)
-
-    expect(el3.previousSibling).is.not.ok
-    expect(el3.nextSibling).eql(el2)
-    expect(el2.previousSibling).eql(el3)
-    expect(el2.nextSibling).is.not.ok
-  })
-
-  it('removeChild', () => {
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    doc.body.appendChild(el)
-    doc.body.appendChild(el2)
-    el2.appendChild(el3)
-
-    expect(el.docId).eql('foo')
-    expect(el2.docId).eql('foo')
-    expect(el3.docId).eql('foo')
-    doc.body.removeChild(el)
-    expect(doc.body.children.length).equal(1)
-    expect(doc.body.children[0]).equal(el2)
-    expect(el.docId).is.not.ok
-    expect(el2.docId).eql('foo')
-    expect(el3.docId).eql('foo')
-    doc.body.removeChild(el2)
-    expect(doc.body.children.length).equal(0)
-    expect(el.docId).is.not.ok
-    expect(el2.docId).is.not.ok
-    expect(el3.docId).is.not.ok
-  })
-
-  it('clear', () => {
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    doc.body.appendChild(el)
-    doc.body.appendChild(el2)
-    doc.body.appendChild(el3)
-
-    expect(el.docId).eql('foo')
-    expect(el2.docId).eql('foo')
-    expect(el3.docId).eql('foo')
-
-    doc.body.clear()
-    expect(doc.body.children.length).equal(0)
-    expect(el.docId).is.not.ok
-    expect(el2.docId).is.not.ok
-    expect(el3.docId).is.not.ok
-  })
-
-  it('modify attr, style, event', () => {
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    doc.body.appendChild(el)
-
-    el.setAttr('a', 21)
-    expect(el.toJSON().attr).eql({ a: 21, b: 12 })
-    el.setAttr('a', 22, true)
-    expect(el.toJSON().attr).eql({ a: 22, b: 12 })
-    el.setAttr('a', 23, false)
-    expect(el.toJSON().attr).eql({ a: 23, b: 12 })
-
-    el.setStyle('c', 21)
-    expect(el.toJSON().style).eql({ a: 211, c: 21, d: 14 })
-    el.setStyle('c', 22, true)
-    expect(el.toJSON().style).eql({ a: 211, c: 22, d: 14 })
-    el.setStyle('c', 23, false)
-    expect(el.toJSON().style).eql({ a: 211, c: 23, d: 14 })
-
-    el.setClassStyle({ a: 311, c: 313 })
-    expect(el.toJSON().style).eql({ a: 311, c: 23, d: 14 })
-
-    const handler = function () {}
-    el.addEvent('click', handler)
-    expect(el.toJSON().event).eql(['click'])
-    expect(el.event.click.handler).equal(handler)
-    el.removeEvent('click')
-    expect(el.event.click).is.undefined
-  })
-})
-
-describe('Node', () => {
-  let doc, el, el2, el3, c, c2, c3, spy
-
-  beforeEach(() => {
-    spy = sinon.spy()
-    doc = new Document('foo', '', spy)
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    el = new Element('bar')
-    el2 = new Element('baz')
-    el3 = new Element('qux')
-    c = new Comment('aaa')
-    c2 = new Comment('bbb')
-    c3 = new Comment('ccc')
-  })
-
-  afterEach(() => {
-    doc.destroy()
-  })
-
-  it('prev and next', () => {
-    expect(el.previousSibling).is.not.ok
-    expect(el.nextSibling).is.not.ok
-
-    doc.body.appendChild(el)
-    doc.body.appendChild(el2)
-    doc.body.appendChild(c)
-    doc.body.appendChild(el3)
-    doc.body.appendChild(c2)
-    doc.body.appendChild(c3)
-
-    expect(el.previousSibling).is.not.ok
-    expect(el2.previousSibling).equal(el)
-    expect(c.previousSibling).equal(el2)
-    expect(el3.previousSibling).equal(c)
-    expect(c2.previousSibling).equal(el3)
-    expect(c3.previousSibling).equal(c2)
-
-    expect(el.nextSibling).equal(el2)
-    expect(el2.nextSibling).equal(c)
-    expect(c.nextSibling).equal(el3)
-    expect(el3.nextSibling).equal(c2)
-    expect(c2.nextSibling).equal(c3)
-    expect(c3.nextSibling).is.not.ok
-  })
-
-  it('tree operations with renderer', () => {
-    doc.body.appendChild(el)
-    el.appendChild(el2)
-    el.appendChild(el3)
-
-    expect(spy.args.length).eql(4)
-    el.insertBefore(el3, el2) // [el3, el2]
-    expect(spy.args.length).eql(5)
-    expect(spy.args[4][0]).eql([{
-      module: 'dom', method: 'moveElement',
-      args: [el3.ref, el.ref, 0]
-    }])
-    el.insertAfter(el3, el2) // [el2, el3]
-    expect(spy.args.length).eql(6)
-    expect(spy.args[5][0]).eql([{
-      module: 'dom', method: 'moveElement',
-      args: [el3.ref, el.ref, 2]
-    }])
-    el.removeChild(el2) // [el3]
-    expect(spy.args.length).eql(7)
-    expect(spy.args[6][0]).eql([{
-      module: 'dom', method: 'removeElement',
-      args: [el2.ref]
-    }])
-    el.clear() // []
-    expect(spy.args.length).eql(8)
-    expect(spy.args[7][0]).eql([{
-      module: 'dom', method: 'removeElement',
-      args: [el3.ref]
-    }])
-  })
-})
-
-describe('complicated situations', () => {
-  let doc, el, el2, el3, c, c2, c3, spy
-
-  beforeEach(() => {
-    spy = sinon.spy()
-    doc = new Document('foo', '', spy)
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    el = new Element('bar')
-    el2 = new Element('baz')
-    el3 = new Element('qux')
-    c = new Comment('aaa')
-    c2 = new Comment('bbb')
-    c3 = new Comment('ccc')
-  })
-
-  afterEach(() => {
-    doc.destroy()
-  })
-
-  it('move a node to its original position', () => {
-    doc.body.appendChild(el)
-    doc.body.appendChild(el2)
-    doc.body.appendChild(el3)
-
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, el2, el3])
-    expect(doc.body.pureChildren).eql([el, el2, el3])
-
-    doc.body.insertAfter(el2, el)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, el2, el3])
-    expect(doc.body.pureChildren).eql([el, el2, el3])
-
-    doc.body.insertAfter(el3, el2, true)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, el2, el3])
-    expect(doc.body.pureChildren).eql([el, el2, el3])
-
-    doc.body.insertBefore(el, el2)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, el2, el3])
-    expect(doc.body.pureChildren).eql([el, el2, el3])
-
-    doc.body.insertBefore(el2, el3, true)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, el2, el3])
-    expect(doc.body.pureChildren).eql([el, el2, el3])
-
-    doc.body.insertAfter(c, el)
-    doc.body.insertAfter(c2, el2)
-    doc.body.insertAfter(c3, el3)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, c, el2, c2, el3, c3])
-    expect(doc.body.pureChildren).eql([el, el2, el3])
-
-    doc.body.insertBefore(el, c)
-    doc.body.insertBefore(el, c, true)
-    doc.body.insertBefore(c, el2)
-    doc.body.insertBefore(c, el2, true)
-    doc.body.insertAfter(c, el)
-    doc.body.insertAfter(c, el, true)
-    doc.body.insertAfter(el2, c)
-    doc.body.insertAfter(el2, c, true)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, c, el2, c2, el3, c3])
-    expect(doc.body.pureChildren).eql([el, el2, el3])
-
-    // move to another place that not change the pureChildren
-    doc.body.insertBefore(el, el2)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([c, el, el2, c2, el3, c3])
-    expect(doc.body.pureChildren).eql([el, el2, el3])
-
-    doc.body.insertBefore(el, c)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, c, el2, c2, el3, c3])
-    expect(doc.body.pureChildren).eql([el, el2, el3])
-
-    doc.body.insertAfter(c, el2)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, el2, c, c2, el3, c3])
-    expect(doc.body.pureChildren).eql([el, el2, el3])
-
-    doc.body.insertBefore(c2, c)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, el2, c2, c, el3, c3])
-    expect(doc.body.pureChildren).eql([el, el2, el3])
-  })
-
-  it('insert before a comment', () => {
-    doc.body.appendChild(el)
-    doc.body.appendChild(c)
-    doc.body.appendChild(c2)
-    doc.body.appendChild(el2)
-    expect(spy.args.length).eql(3)
-    expect(doc.body.children).eql([el, c, c2, el2])
-    expect(doc.body.pureChildren).eql([el, el2])
-
-    doc.body.insertBefore(el3, c)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, el3, c, c2, el2])
-    expect(doc.body.pureChildren).eql([el, el3, el2])
-    expect(spy.args[3][0]).eql([{
-      module: 'dom', method: 'addElement',
-      args: [doc.body.ref, el3.toJSON(), 1] }])
-  })
-
-  it('insert before a comment which has no more element after', () => {
-    doc.body.appendChild(el)
-    doc.body.appendChild(el2)
-    doc.body.appendChild(c)
-    doc.body.appendChild(c2)
-    expect(spy.args.length).eql(3)
-    expect(doc.body.children).eql([el, el2, c, c2])
-    expect(doc.body.pureChildren).eql([el, el2])
-
-    doc.body.insertBefore(el3, c)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, el2, el3, c, c2])
-    expect(doc.body.pureChildren).eql([el, el2, el3])
-    expect(spy.args[3][0]).eql([{
-      module: 'dom', method: 'addElement',
-      args: [doc.body.ref, el3.toJSON(), 2] }])
-  })
-
-  it('insert after a comment', () => {
-    doc.body.appendChild(el)
-    doc.body.appendChild(c)
-    doc.body.appendChild(c2)
-    doc.body.appendChild(el2)
-    expect(spy.args.length).eql(3)
-    expect(doc.body.children).eql([el, c, c2, el2])
-    expect(doc.body.pureChildren).eql([el, el2])
-
-    doc.body.insertAfter(el3, c2)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([el, c, c2, el3, el2])
-    expect(doc.body.pureChildren).eql([el, el3, el2])
-    expect(spy.args[3][0]).eql([{
-      module: 'dom', method: 'addElement',
-      args: [doc.body.ref, el3.toJSON(), 1] }])
-  })
-
-  it('insert after a comment which has no more element before', () => {
-    doc.body.appendChild(c)
-    doc.body.appendChild(c2)
-    doc.body.appendChild(el)
-    doc.body.appendChild(el2)
-    expect(spy.args.length).eql(3)
-    expect(doc.body.children).eql([c, c2, el, el2])
-    expect(doc.body.pureChildren).eql([el, el2])
-
-    doc.body.insertAfter(el3, c2)
-    expect(spy.args.length).eql(4)
-    expect(doc.body.children).eql([c, c2, el3, el, el2])
-    expect(doc.body.pureChildren).eql([el3, el, el2])
-    expect(spy.args[3][0]).eql([{
-      module: 'dom', method: 'addElement',
-      args: [doc.body.ref, el3.toJSON(), 0] }])
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/runtime/vdom/listener.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/runtime/vdom/listener.js b/html5/test/unit/runtime/vdom/listener.js
deleted file mode 100644
index 1eab8d5..0000000
--- a/html5/test/unit/runtime/vdom/listener.js
+++ /dev/null
@@ -1,381 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import { Document } from '../../../../runtime/vdom'
-import Listener from '../../../../runtime/bridge/Listener'
-
-describe('dom listener basic', () => {
-  it('works with no id', () => {
-    const doc = new Document(null, null, null)
-    doc.destroy()
-  })
-
-  it('works with no handler', () => {
-    const oriCallNative = global.callNative
-    const oriCallAddElement = global.callAddElement
-    const oriDocumentHandler = Document.handler
-
-    Document.handler = null
-    global.callNative = function () { return -1 }
-    global.callAddElement = function () { return -1 }
-
-    const doc = new Document('foo1', null, null)
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    const el = doc.createElement('a')
-    doc.body.appendChild(el)
-    doc.destroy()
-
-    global.callNative = oriCallNative
-    global.callAddElement = oriCallAddElement
-    Document.handler = oriDocumentHandler
-  })
-
-  it('works with an handler', () => {
-    const doc = new Document('foo2', null, function () {})
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    doc.destroy()
-  })
-
-  it('works with an handler', () => {
-    const doc = new Document('foo2', null, function () {})
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-    doc.destroy()
-  })
-})
-
-describe('dom listener details', () => {
-  let doc, spy
-
-  beforeEach(() => {
-    spy = sinon.spy()
-    doc = new Document('foo', '', spy, Listener)
-  })
-
-  afterEach(() => {
-    doc.destroy()
-  })
-
-  it('create document again', (done) => {
-    const documentElement = doc.createDocumentElement()
-
-    expect(documentElement).eql(doc.documentElement)
-    done()
-  })
-
-  it('create body', (done) => {
-    const body = doc.createBody('r', {
-      attr: { a: 1 }, style: { b: 2 }
-    })
-
-    doc.documentElement.appendChild(body)
-
-    expect(spy.args.length).eql(1)
-    expect(spy.args[0]).eql([[{
-      module: 'dom', method: 'createBody',
-      args: [{ type: 'r', ref: '_root', attr: { a: 1 }, style: { b: 2 }}]
-    }], '-1'])
-    done()
-  })
-
-  it('document appendChild', (done) => {
-    const el = doc.createElement('father')
-    const el2 = doc.createElement('child1')
-    const el3 = doc.createElement('child2')
-
-    el.appendChild(el2)
-    el.appendChild(el3)
-
-    doc.documentElement.appendChild(el)
-
-    expect(doc.documentElement.children.length).eql(1)
-    expect(doc.documentElement.children[0]).eql(el)
-
-    done()
-  })
-
-  it('document insertBefore', (done) => {
-    const body = doc.createBody('r', {
-      attr: { a: 1 }, style: { b: 2 }
-    })
-
-    doc.documentElement.insertBefore(body)
-
-    expect(spy.args.length).eql(1)
-    expect(spy.args[0]).eql([[{
-      module: 'dom', method: 'createBody',
-      args: [{ type: 'r', ref: '_root', attr: { a: 1 }, style: { b: 2 }}]
-    }], '-1'])
-    done()
-  })
-
-  it('document insertBefore with comment', (done) => {
-    const body = doc.createBody('r', {
-      attr: { a: 1 }, style: { b: 2 }
-    })
-    const el = doc.createComment('asd')
-
-    doc.documentElement.appendChild(el)
-    doc.documentElement.insertBefore(body, el)
-
-    expect(doc.documentElement.children.length).eql(2)
-    expect(doc.documentElement.children[0]).eql(body)
-    done()
-  })
-
-  it('document fireEvent with no element', (done) => {
-    doc.fireEvent(null)
-    done()
-  })
-
-  it('document fireEvent with domChanges', (done) => {
-    const el = doc.createElement('el', {
-      attr: { a: 1 }, style: { b: 2 }
-    })
-
-    doc.documentElement.appendChild(el)
-
-    doc.fireEvent(el, null, null, {})
-
-    expect(el.attr).eql({ a: 1 })
-    expect(el.style).eql({ b: 2 })
-
-    doc.fireEvent(el, null, null, {
-      attrs: { a: 1 }, style: { b: 2 }
-    })
-
-    expect(el.attr).eql({ a: 1 })
-    expect(el.style).eql({ b: 2 })
-
-    doc.fireEvent(el, null, null, {
-      attrs: { a: 2 }, style: { b: 3 }
-    })
-
-    expect(el.attr).eql({ a: 2 })
-    expect(el.style).eql({ b: 3 })
-    done()
-  })
-
-  it('toString has pureChildren', (done) => {
-    const el = doc.createElement('el')
-    const el1 = doc.createElement('el1')
-    el.appendChild(el1)
-    expect(el.toString()).eql('<el attr={} style={}><el1 attr={} style={}></el1></el>')
-
-    done()
-  })
-
-  it('removeChild', (done) => {
-    const el = doc.createElement('el')
-    const el1 = doc.createElement('el1')
-
-    el.removeChild(el1)
-    el.removeChild(el1, true)
-
-    done()
-  })
-
-  it('removeEvent', (done) => {
-    const el = doc.createElement('el')
-    el.removeEvent(null)
-    done()
-  })
-
-  it('add element', (done) => {
-    const body = doc.createBody('r')
-
-    doc.documentElement.appendChild(body)
-
-    expect(spy.args[0]).eql([[{
-      module: 'dom', method: 'createBody',
-      args: [body.toJSON()]
-    }], '-1'])
-
-    const el = doc.createElement('a')
-    el.setAttr('x', 1)
-    el.addEvent('click', () => {})
-    doc.body.appendChild(el)
-
-    expect(spy.args[1]).eql([[{
-      module: 'dom', method: 'addElement',
-      args: ['_root', el.toJSON(), -1]
-    }], '-1'])
-
-    const el2 = doc.createElement('b')
-    doc.body.insertBefore(el2, el) // [el2, el]
-
-    const el3 = doc.createElement('c')
-    doc.body.insertAfter(el3, el) // [el2, el, el3]
-
-    expect(spy.args.length).eql(4)
-    expect(spy.args[2]).eql([[{
-      module: 'dom', method: 'addElement',
-      args: ['_root', el2.toJSON(), 0]
-    }], '-1'])
-    expect(spy.args[3]).eql([[{
-      module: 'dom', method: 'addElement',
-      args: ['_root', el3.toJSON(), 2]
-    }], '-1'])
-
-    done()
-  })
-
-  it('bind listener for element props', () => {
-    const el = doc.createElement('bar')
-    doc.createBody('r')
-    doc.documentElement.appendChild(doc.body)
-
-    expect(spy.args.length).eql(1)
-    expect(spy.args[0][0]).eql([{
-      module: 'dom', method: 'createBody',
-      args: [doc.body.toJSON()]
-    }])
-
-    el.setAttr('a', 1)
-    el.setStyle('a', 2)
-    el.setClassStyle({ a: 3, b: 4 })
-    el.addEvent('click', () => {})
-    el.addEvent('appear', () => {})
-    el.removeEvent('appear')
-    doc.body.appendChild(el)
-
-    expect(spy.args.length).eql(2)
-    expect(spy.args[1][0]).eql([{
-      module: 'dom', method: 'addElement',
-      args: ['_root', el.toJSON(), -1]
-    }])
-
-    el.setAttr('a', 11, true)
-    expect(spy.args.length).eql(2)
-    el.setAttr('a', 12)
-    expect(spy.args.length).eql(3)
-    expect(spy.args[2][0]).eql([{
-      module: 'dom', method: 'updateAttrs',
-      args: [el.ref, { a: 12 }]
-    }])
-    el.setAttr('a', 12, false)
-    expect(spy.args.length).eql(4)
-    expect(spy.args[3][0]).eql([{
-      module: 'dom', method: 'updateAttrs',
-      args: [el.ref, { a: 12 }]
-    }])
-
-    el.setStyle('a', 13, true)
-    expect(spy.args.length).eql(4)
-    el.setStyle('a', 14)
-    expect(spy.args.length).eql(5)
-    expect(spy.args[4][0]).eql([{
-      module: 'dom', method: 'updateStyle',
-      args: [el.ref, { a: 14 }]
-    }])
-    el.setStyle('a', 14, false)
-    expect(spy.args.length).eql(6)
-    expect(spy.args[5][0]).eql([{
-      module: 'dom', method: 'updateStyle',
-      args: [el.ref, { a: 14 }]
-    }])
-
-    el.setClassStyle({ a: 13, b: 14 })
-    expect(spy.args[6][0]).eql([{
-      module: 'dom', method: 'updateStyle',
-      args: [el.ref, { a: 14, b: 14 }]
-    }])
-    expect(spy.args.length).eql(7)
-    el.addEvent('click', () => {})
-    expect(spy.args.length).eql(7)
-    el.addEvent('appear', () => {})
-    expect(spy.args.length).eql(8)
-    expect(spy.args[7][0]).eql([{
-      module: 'dom', method: 'addEvent',
-      args: [el.ref, 'appear']
-    }])
-
-    el.removeEvent('click')
-    expect(spy.args.length).eql(9)
-    expect(spy.args[8][0]).eql([{
-      module: 'dom', method: 'removeEvent',
-      args: [el.ref, 'click']
-    }])
-    el.removeEvent('appear')
-    expect(spy.args.length).eql(10)
-    expect(spy.args[9][0]).eql([{
-      module: 'dom', method: 'removeEvent',
-      args: [el.ref, 'appear']
-    }])
-
-    el.setAttr('a', 1)
-    el.setStyle('a', 2)
-    el.setClassStyle({ a: 3, b: 4 })
-    el.addEvent('click', () => {})
-    el.addEvent('appear', () => {})
-    el.removeEvent('appear')
-    expect(spy.args.length).eql(16)
-    expect(spy.args.slice(10).map(c => c[0][0])).eql([
-      { module: 'dom', method: 'updateAttrs', args: [el.ref, { a: 1 }] },
-      { module: 'dom', method: 'updateStyle', args: [el.ref, { a: 2 }] },
-      { module: 'dom', method: 'updateStyle', args: [el.ref, { a: 2, b: 4 }] },
-      { module: 'dom', method: 'addEvent', args: [el.ref, 'click'] },
-      { module: 'dom', method: 'addEvent', args: [el.ref, 'appear'] },
-      { module: 'dom', method: 'removeEvent', args: [el.ref, 'appear'] }
-    ])
-  })
-})
-
-describe('listener', () => {
-  let spy, listener
-
-  beforeEach(() => {
-    spy = sinon.spy()
-    listener = new Listener('1', spy)
-  })
-
-  it('removeElement many', () => {
-    listener.removeElement(['1', '2', '3', '4'])
-
-    expect(spy.args[0][0]).eql([
-      { module: 'dom', method: 'removeElement', args: ['1'] },
-      { module: 'dom', method: 'removeElement', args: ['2'] },
-      { module: 'dom', method: 'removeElement', args: ['3'] },
-      { module: 'dom', method: 'removeElement', args: ['4'] }
-    ])
-
-    listener = null
-  })
-
-  it('no-handler', () => {
-    listener = new Listener('1')
-    listener.handler(
-      { module: 'dom', method: 'removeElement', args: ['1'] },
-      spy
-    )
-    expect(spy).callCount(1)
-  })
-
-  afterEach(() => {
-    spy = null
-    listener = null
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/runtime/vdom/node.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/runtime/vdom/node.js b/html5/test/unit/runtime/vdom/node.js
deleted file mode 100644
index b05d277..0000000
--- a/html5/test/unit/runtime/vdom/node.js
+++ /dev/null
@@ -1,39 +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 chai from 'chai'
-const { expect } = chai
-
-import {
-  Node
-} from '../../../../runtime/vdom'
-
-describe('Node', () => {
-  it('create node', () => {
-    const node = new Node()
-    expect(node).is.an.object
-    expect(node.nodeId).to.be.a('string')
-    expect(node.ref).eql(node.nodeId)
-    expect(node.children).eql([])
-    expect(node.pureChildren).eql([])
-    expect(node.parentNode).to.be.null
-    expect(node.nextSibling).to.be.null
-    expect(node.previousSibling).to.be.null
-    node.destroy()
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/shared/BroadcastChannel.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/shared/BroadcastChannel.js b/html5/test/unit/shared/BroadcastChannel.js
deleted file mode 100644
index 880ed6b..0000000
--- a/html5/test/unit/shared/BroadcastChannel.js
+++ /dev/null
@@ -1,231 +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 { expect } from 'chai'
-import sinon from 'sinon'
-
-import service from '../../../services/broadcast-channel/index'
-import { MessageEvent } from '../../../services/broadcast-channel/message-event'
-const { BroadcastChannel } = service.create('foo').instance
-
-describe('BroadcastChannel', () => {
-  it('is a function', () => {
-    expect(BroadcastChannel).is.an('function')
-  })
-
-  it('has standard APIs', () => {
-    const JamesBond = new BroadcastChannel('007')
-    expect(JamesBond.name).is.an('string')
-    expect(JamesBond.onmessage).is.an('null')
-    expect(JamesBond.postMessage).is.an('function')
-    expect(JamesBond.close).is.an('function')
-
-    expect(JamesBond).to.have.ownProperty('name')
-    expect(JamesBond).to.have.ownProperty('onmessage')
-    expect(JamesBond).not.to.have.ownProperty('postMessage')
-    expect(JamesBond).not.to.have.ownProperty('close')
-    JamesBond.close()
-  })
-
-  it('name attribute is readonly', () => {
-    const Wolverine = new BroadcastChannel('Logan')
-    expect(Wolverine.name).to.equal('Logan')
-    Wolverine.name = 'Wolverine'
-    expect(Wolverine.name).to.equal('Logan')
-    Wolverine.close()
-  })
-
-  describe('basci usage', () => {
-    const Hulk = new BroadcastChannel('Avengers')
-    const Stack = new BroadcastChannel('Avengers')
-    const Steven = new BroadcastChannel('Avengers')
-    const Logan = new BroadcastChannel('Mutants')
-    const Erik = new BroadcastChannel('Mutants')
-
-    beforeEach(() => {
-      Hulk.onmessage = sinon.spy()
-      Stack.onmessage = sinon.spy()
-      Steven.onmessage = sinon.spy()
-      Logan.onmessage = sinon.spy()
-    })
-
-    afterEach(() => {
-      Hulk.onmessage = null
-      Stack.onmessage = null
-      Steven.onmessage = null
-      Logan.onmessage = null
-      Erik.onmessage = null
-    })
-
-    it('trigger onmessage', () => {
-      Hulk.postMessage('Hulk Smash !!!')
-      expect(Hulk.onmessage.callCount).to.be.equal(0)
-      expect(Logan.onmessage.callCount).to.be.equal(0)
-      expect(Stack.onmessage.callCount).to.be.equal(1)
-      expect(Steven.onmessage.callCount).to.be.equal(1)
-    })
-
-    it('don\'t trigger onmessage itself', () => {
-      Logan.postMessage('Any one here ?')
-      expect(Hulk.onmessage.callCount).to.be.equal(0)
-      expect(Logan.onmessage.callCount).to.be.equal(0)
-      expect(Stack.onmessage.callCount).to.be.equal(0)
-      expect(Steven.onmessage.callCount).to.be.equal(0)
-    })
-
-    it('send multi messages', () => {
-      Hulk.postMessage('Hulk Smash !!!')
-      Logan.postMessage('I will fight you !')
-      Stack.postMessage('whatever')
-      Hulk.postMessage('whatever')
-      Stack.postMessage('whatever')
-      Steven.postMessage('whatever')
-      Stack.postMessage('whatever')
-
-      expect(Hulk.onmessage.callCount).to.be.equal(4)
-      expect(Logan.onmessage.callCount).to.be.equal(0)
-      expect(Stack.onmessage.callCount).to.be.equal(3)
-      expect(Steven.onmessage.callCount).to.be.equal(5)
-    })
-
-    it('send string message', () => {
-      Stack.postMessage('I am Iron-Man.')
-
-      expect(Hulk.onmessage.callCount).to.be.equal(1)
-      expect(Steven.onmessage.callCount).to.be.equal(1)
-
-      const event = Hulk.onmessage.firstCall.args[0]
-      expect(event).is.an('object')
-      expect(event.data).is.a('string')
-      expect(event.data).to.be.equal('I am Iron-Man.')
-    })
-
-    it('send object message', () => {
-      const message = {
-        type: 'SOKOVIA ACCORDS',
-        approvedCountry: 117,
-        content: 'The Avengers shall no longer be a private organization.'
-      }
-
-      Stack.postMessage(message)
-
-      const event = Steven.onmessage.firstCall.args[0]
-      expect(event).is.an('object')
-      expect(event.data).is.a('object')
-      expect(event.data).to.deep.equal(message)
-    })
-
-    it('close channel', () => {
-      Hulk.close()
-
-      Steven.postMessage('come to fight !')
-      expect(Hulk.onmessage.callCount).to.be.equal(0)
-      expect(Stack.onmessage.callCount).to.be.equal(1)
-      expect(Steven.onmessage.callCount).to.be.equal(0)
-    })
-
-    it('send message after close', () => {
-      Hulk.close()
-
-      expect(() => { Hulk.postMessage('I am leaving.') }).to.throw(Error)
-
-      expect(Hulk.onmessage.callCount).to.be.equal(0)
-      expect(Logan.onmessage.callCount).to.be.equal(0)
-      expect(Stack.onmessage.callCount).to.be.equal(0)
-      expect(Steven.onmessage.callCount).to.be.equal(0)
-    })
-
-    it('MessageEvent dafault parameters', () => {
-      const event = new MessageEvent()
-
-      expect(event).is.an('object')
-      expect(event.data).to.be.null
-      expect(event.type).to.be.equal('message')
-      expect(event.origin).to.be.equal('')
-      expect(event.target).to.be.null
-      expect(event.source).to.be.null
-      expect(event.timeStamp).to.be.a('number')
-      expect(event.ports).to.be.an('array')
-    })
-
-    it('MessageEvent constructor', () => {
-      const source = { type: 'empty' }
-      const event = new MessageEvent('custom', {
-        source,
-        data: 'Nothing',
-        origin: 'http://127.0.0.1',
-        ports: ['8080']
-      })
-
-      expect(event).is.an('object')
-      expect(event.data).to.be.equal('Nothing')
-      expect(event.type).to.be.equal('custom')
-      expect(event.origin).to.be.equal('http://127.0.0.1')
-      expect(event.target).to.be.null
-      expect(event.source).to.deep.equal(source)
-      expect(event.timeStamp).to.be.a('number')
-      expect(event.ports).to.deep.equal(['8080'])
-    })
-
-    it('use MessageEvent', () => {
-      Steven.postMessage('Be Together.')
-
-      const event = Stack.onmessage.firstCall.args[0]
-      expect(event).is.an('object')
-      expect(event.data).to.be.equal('Be Together.')
-      expect(event.type).to.be.equal('message')
-      expect(event.origin).to.be.equal('')
-      expect(event.target).to.be.null
-      expect(event.timeStamp).to.be.a('number')
-    })
-
-    it('invalid usage', () => {
-      const stranger = {
-        name: 'stranger',
-        close: Erik.close,
-        postMessage: Erik.postMessage
-      }
-
-      stranger.postMessage('hello world.')
-
-      expect(Hulk.onmessage.callCount).to.be.equal(0)
-      expect(Logan.onmessage.callCount).to.be.equal(0)
-      expect(Stack.onmessage.callCount).to.be.equal(0)
-      expect(Steven.onmessage.callCount).to.be.equal(0)
-
-      stranger.close()
-    })
-
-    it('close all', () => {
-      Hulk.close()
-      Stack.close()
-      Steven.close()
-      Logan.close()
-      Erik.close()
-
-      // close again
-      expect(() => {
-        Hulk.close()
-        Stack.close()
-        Steven.close()
-        Logan.close()
-        Erik.close()
-      }).to.not.throw
-    })
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/shared/arrayFrom.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/shared/arrayFrom.js b/html5/test/unit/shared/arrayFrom.js
deleted file mode 100644
index 99e9495..0000000
--- a/html5/test/unit/shared/arrayFrom.js
+++ /dev/null
@@ -1,90 +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 chai from 'chai'
-const { expect } = chai
-
-// remove original Array.from
-// Array.from = null
-
-import '../../../shared/arrayFrom'
-
-/* eslint-disable */
-describe('Array.from', () => {
-  it('is a function', () => {
-    expect(Array.from).is.an('function')
-  })
-
-  it('requires an array-like object', () => {
-    expect(() => Array.from()).to.throw(TypeError)
-    expect(() => Array.from(null)).to.throw(TypeError)
-	})
-
-  it('throws with invalid lengths', () => {
-    expect(() => Array.from({ 'length': Infinity })).to.throw(RangeError)
-    expect(() => Array.from({ 'length': Math.pow(2, 32) })).to.throw(RangeError)
-	})
-
-  it('swallows negative lengths', () => {
-    expect(Array.from({ length: -1 }).length).to.equal(0)
-    expect(Array.from({ length: -0 }).length).to.equal(0)
-    expect(Array.from({ length: -Infinity }).length).to.equal(0)
-	})
-
-  it('works with primitives', () => {
-    expect(Array.from(false)).to.deep.equal([])
-    expect(Array.from(true)).to.deep.equal([])
-    expect(Array.from(0)).to.deep.equal([])
-    expect(Array.from(-0)).to.deep.equal([])
-    expect(Array.from(1)).to.deep.equal([])
-    expect(Array.from(-1)).to.deep.equal([])
-    expect(Array.from(Infinity)).to.deep.equal([])
-    expect(Array.from(-Infinity)).to.deep.equal([])
-	})
-
-  it('works with primitive strings', () => {
-    expect(Array.from('')).to.deep.equal([])
-    expect(Array.from('abc')).to.deep.equal(['a', 'b', 'c'])
-    expect(Array.from('a\u5F20\u7FF0')).to.deep.equal(['a', '\u5F20', '\u7FF0'])
-    expect(Array.from('x\uD834\uDF06Y')).to.deep.equal(['x', '\uD834\uDF06', 'Y'])
-	})
-
-  it('works with objects', () => {
-    expect(Array.from({})).to.deep.equal([])
-    expect(Array.from({ name: 'abc' })).to.deep.equal([])
-  })
-
-  it('works with arrays', () => {
-    expect(Array.from([])).to.deep.equal([])
-    expect(Array.from(['a', 2, false, {}])).to.deep.equal(['a', 2, false, {}])
-  })
-
-  it('throws with an invalid mapping function', () => {
-    expect(() => Array.from([], null)).to.throw(TypeError)
-    expect(() => Array.from([], false)).to.throw(TypeError)
-    expect(() => Array.from([], 'true')).to.throw(TypeError)
-    expect(() => Array.from([], 42)).to.throw(TypeError)
-    expect(() => Array.from([], {})).to.throw(TypeError)
-    expect(() => Array.from([], [])).to.throw(TypeError)
-	})
-
-  it('works with mapping function', () => {
-    expect(Array.from([1, 2, 3], n => n * n)).to.deep.equal([1, 4, 9])
-    expect(Array.from('abc', x => x.toUpperCase())).to.deep.equal(['A', 'B', 'C'])
-	})
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/shared/console.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/shared/console.js b/html5/test/unit/shared/console.js
deleted file mode 100644
index 60eb281..0000000
--- a/html5/test/unit/shared/console.js
+++ /dev/null
@@ -1,224 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import { setNativeConsole, resetNativeConsole } from '../../../shared/console'
-
-const oriEnv = global.WXEnvironment
-const oriConsole = global.console
-
-describe.skip('polyfill for console', () => {
-  const scope = {}
-  const nativeLogSpy = sinon.spy()
-
-  before(() => {
-    delete global.console
-    global.nativeLog = function (...args) {
-      nativeLogSpy(...args)
-    }
-    global.WXEnvironment = {
-      platform: 'iOS',
-      logLevel: 'log'
-    }
-    setNativeConsole()
-    scope.console = global.console
-    // global.console = oriConsole // no output for these test case
-  })
-
-  after(() => {
-    global.WXEnvironment = oriEnv
-    resetNativeConsole()
-    expect(global.console).eql(oriConsole)
-  })
-
-  beforeEach(() => {
-    nativeLogSpy.reset()
-  })
-
-  it('export apis', () => {
-    expect(setNativeConsole).is.a('function')
-    expect(resetNativeConsole).is.a('function')
-  })
-
-  it('nativeLog', () => {
-    expect(global.nativeLog).is.a('function')
-    global.nativeLog('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(1)
-  })
-
-  it('console apis', () => {
-    expect(scope.console).is.a('object')
-    expect(scope.console.debug).is.a('function')
-    expect(scope.console.log).is.a('function')
-    expect(scope.console.info).is.a('function')
-    expect(scope.console.warn).is.a('function')
-    expect(scope.console.error).is.a('function')
-  })
-
-  it('no debug message', () => {
-    scope.console.debug('a debug message')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-  })
-
-  it('a log message', () => {
-    scope.console.log('a log message')
-    expect(nativeLogSpy.callCount).to.be.equal(1)
-    expect(nativeLogSpy.firstCall.args).to.deep.equal(
-      ['a log message', '__LOG']
-    )
-  })
-
-  it('multi log message', () => {
-    scope.console.log('A', 'B', 'C')
-    scope.console.log('D', 'E')
-    expect(nativeLogSpy.callCount).to.be.equal(2)
-    expect(nativeLogSpy.firstCall.args).to.deep.equal(
-      ['A', 'B', 'C', '__LOG']
-    )
-    expect(nativeLogSpy.lastCall.args).to.deep.equal(
-      ['D', 'E', '__LOG']
-    )
-  })
-
-  it('an info message', () => {
-    scope.console.info('a info message')
-    expect(nativeLogSpy.callCount).to.be.equal(1)
-    expect(nativeLogSpy.firstCall.args).to.deep.equal(
-      ['a info message', '__INFO']
-    )
-  })
-
-  it('a warn message', () => {
-    scope.console.warn('a warn message')
-    expect(nativeLogSpy.callCount).to.be.equal(1)
-    expect(nativeLogSpy.firstCall.args).eql(
-      ['a warn message', '__WARN']
-    )
-  })
-
-  it('a error message', () => {
-    scope.console.error('a error message')
-    expect(nativeLogSpy.callCount).to.be.equal(1)
-    expect(nativeLogSpy.firstCall.args).eql(
-      ['a error message', '__ERROR']
-    )
-  })
-
-  it('debug logLevel', () => {
-    global.WXEnvironment.logLevel = 'debug'
-    scope.console.debug('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(1)
-    scope.console.log('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(2)
-    scope.console.info('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(3)
-    scope.console.warn('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(4)
-    scope.console.error('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(5)
-  })
-
-  it('log logLevel', () => {
-    global.WXEnvironment.logLevel = 'log'
-    scope.console.debug('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.log('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(1)
-    scope.console.info('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(2)
-    scope.console.warn('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(3)
-    scope.console.error('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(4)
-  })
-
-  it('info logLevel', () => {
-    global.WXEnvironment.logLevel = 'info'
-    scope.console.debug('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.log('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.info('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(1)
-    scope.console.warn('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(2)
-    scope.console.error('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(3)
-  })
-
-  it('warn logLevel', () => {
-    global.WXEnvironment.logLevel = 'warn'
-    scope.console.debug('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.log('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.info('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.warn('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(1)
-    scope.console.error('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(2)
-  })
-
-  it('error logLevel', () => {
-    global.WXEnvironment.logLevel = 'error'
-    scope.console.debug('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.log('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.info('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.warn('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.error('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(1)
-  })
-
-  it('off logLevel', () => {
-    global.WXEnvironment.logLevel = 'off'
-    scope.console.debug('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.log('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.info('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.warn('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.error('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-  })
-
-  it('unknown logLevel', () => {
-    global.WXEnvironment.logLevel = 'log'
-    scope.console.debug('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(0)
-    scope.console.log('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(1)
-    scope.console.info('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(2)
-    scope.console.warn('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(3)
-    scope.console.error('whatever')
-    expect(nativeLogSpy.callCount).to.be.equal(4)
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/shared/index.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/shared/index.js b/html5/test/unit/shared/index.js
deleted file mode 100644
index 337bdf1..0000000
--- a/html5/test/unit/shared/index.js
+++ /dev/null
@@ -1,73 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import * as shared from '../../../shared'
-
-describe('a polyfill of', () => {
-  it('Promise', () => {
-    expect(typeof Promise).to.be.equal('function')
-    expect(() => {
-      new Promise(sinon.spy()).then(sinon.spy())
-      Promise.all()
-      Promise.race()
-      Promise.resolve()
-      Promise.reject()
-    }).to.not.throw
-  })
-
-  it('Object.assign', () => {
-    expect(typeof Object.assign).to.be.equal('function')
-  })
-
-  it('setTimeout', () => {
-    expect(typeof setTimeout).to.be.equal('function')
-  })
-
-  it('console.log', () => {
-    expect(typeof console.log).to.be.equal('function')
-  })
-})
-
-describe('freeze the prototype of build-in objects', function () {
-  before(() => {
-    shared.freezePrototype()
-  })
-
-  it('Object & Object.prototype', () => {
-    expect(Object).to.be.frozen
-    // expect(Object.prototype).to.be.frozen
-  })
-  it('Array & Array.prototype', () => {
-    expect(Array).to.be.frozen
-    expect(Array.prototype).to.be.frozen
-  })
-
-  it('String.prototype', () => { expect(String.prototype).to.be.frozen })
-  it('Number.prototype', () => { expect(Number.prototype).to.be.frozen })
-  it('Boolean.prototype', () => { expect(Boolean.prototype).to.be.frozen })
-  it('Error.prototype', () => { expect(Error.prototype).to.be.frozen })
-  it('Date.prototype', () => { expect(Date.prototype).to.be.frozen })
-  it('Math.prototype', () => { expect(Math.prototype).to.be.frozen })
-  it('RegExp.prototype', () => { expect(RegExp.prototype).to.be.frozen })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/shared/objectAssign.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/shared/objectAssign.js b/html5/test/unit/shared/objectAssign.js
deleted file mode 100644
index 7c76a4a..0000000
--- a/html5/test/unit/shared/objectAssign.js
+++ /dev/null
@@ -1,50 +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 chai from 'chai'
-const { expect } = chai
-
-import '../../../shared/objectAssign'
-
-describe('object.assign', () => {
-  it('is a function', () => {
-    expect(Object.assign).is.an('function')
-  })
-
-  it('source to taget', () => {
-    const target = {
-      a: 'a',
-      b: 'b'
-    }
-    const src1 = {
-      b: 'bb',
-      c: 'c'
-    }
-    const src2 = {
-      d: 'd',
-      a: 'aa'
-    }
-    Object.assign(target, src1, src2)
-    expect(target).to.deep.equal({
-      a: 'aa',
-      b: 'bb',
-      c: 'c',
-      d: 'd'
-    })
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/shared/objectSetPrototypeOf.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/shared/objectSetPrototypeOf.js b/html5/test/unit/shared/objectSetPrototypeOf.js
deleted file mode 100644
index 58056bc..0000000
--- a/html5/test/unit/shared/objectSetPrototypeOf.js
+++ /dev/null
@@ -1,42 +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 chai from 'chai'
-const { expect } = chai
-
-import '../../../shared/objectSetPrototypeOf'
-
-/* eslint-disable */
-describe('Object.setPrototypeOf', () => {
-  it('is a function', () => {
-    expect(Object.setPrototypeOf).is.an('function')
-  })
-
-  it('be able to set prototype', () => {
-    const JonSnow = { name: 'Jon Snow' }
-    Object.setPrototypeOf(JonSnow, {
-      know: function() { return 'nothing' }
-    })
-
-    expect(Object.hasOwnProperty.call(JonSnow, 'name')).to.be.true
-    expect(Object.hasOwnProperty.call(JonSnow, 'know')).to.be.false
-
-    expect(JonSnow.know()).to.equal('nothing')
-
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/vanilla/index.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/vanilla/index.js b/html5/test/unit/vanilla/index.js
deleted file mode 100644
index 6389c4c..0000000
--- a/html5/test/unit/vanilla/index.js
+++ /dev/null
@@ -1,159 +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 { expect } from 'chai'
-
-import vanilla from '../../../frameworks/vanilla'
-import runtime from '../../../runtime'
-
-// clear ref property in vdom
-function clearRefs (json) {
-  delete json.ref
-  if (json.children) {
-    json.children.forEach(clearRefs)
-  }
-}
-
-describe('vanilla test', () => {
-  it('standard APIs', () => {
-    expect(vanilla.init).to.be.a('function')
-    expect(vanilla.createInstance).to.be.a('function')
-    expect(vanilla.destroyInstance).to.be.a('function')
-    expect(vanilla.getRoot).to.be.a('function')
-    expect(vanilla.receiveTasks).to.be.a('function')
-  })
-
-  it('create & destroy instance', () => {
-    const id = 'basic-demo'
-    vanilla.init(runtime.config)
-    vanilla.createInstance(id, `
-      var body = document.createBody()
-      var div = document.createElement('div')
-      body.appendChild(div)
-    `, {})
-    vanilla.destroyInstance(id)
-  })
-
-  it('getRoot', () => {
-    const id = 'sample-demo'
-    const code = `
-      var body = document.createBody()
-      var div = document.createElement('div')
-      div.setStyle('color', 'red')
-      var span = document.createElement('span')
-      span.setAttr('value', 'text node')
-      div.appendChild(span)
-      body.appendChild(div)
-    `
-    vanilla.init(runtime.config)
-    vanilla.createInstance(id, code, {})
-
-    const vdom = vanilla.getRoot(id)
-    clearRefs(vdom)
-    expect(vdom).to.deep.equal({
-      // ref: '_root',
-      type: 'div',
-      attr: {},
-      style: {},
-      children: [
-        {
-          // ref: '8',
-          type: 'div',
-          attr: {},
-          style: {
-            color: 'red'
-          },
-          children: [
-            {
-              // ref: '9',
-              type: 'span',
-              attr: {
-                value: 'text node'
-              },
-              style: {}
-            }
-          ]
-        }
-      ]
-    })
-
-    vanilla.destroyInstance(id)
-  })
-
-  describe('receiveTasks', () => {
-    const id = 'task-demo'
-
-    before(() => {
-      vanilla.init(runtime.config)
-      vanilla.createInstance(id, `
-        var body = document.createBody()
-        body.addEvent('click', function() {
-          this.setStyle('width', '200')
-        })
-        document.addCallback(function(message) {
-          body.setAttr('from', message)
-        })
-        `, {})
-    })
-
-    after(() => {
-      vanilla.destroyInstance(id)
-    })
-
-    it('initial state', () => {
-      // initial style and attr in empty
-      expect(vanilla.getRoot(id).style).to.deep.equal({})
-      expect(vanilla.getRoot(id).attr).to.deep.equal({})
-
-      // empty task
-      vanilla.receiveTasks(id, [])
-    })
-
-    it('invalid tasks', () => {
-      vanilla.receiveTasks('invalid id', [])
-      vanilla.receiveTasks(id, {})
-      vanilla.receiveTasks(id, [{
-        method: 'whatever',
-        args: []
-      }])
-    })
-
-    it('fireEvent', () => {
-      vanilla.receiveTasks(id, [{
-        method: 'fireEvent',
-        args: ['_root', 'click']
-      }])
-      expect(vanilla.getRoot(id).style).to.deep.equal({ width: '200' })
-    })
-
-    it('callback', () => {
-      vanilla.receiveTasks(id, [{
-        method: 'callback',
-        args: ['1', 'callback']
-      }])
-      expect(vanilla.getRoot(id).attr).to.deep.equal({ from: 'callback' })
-
-      // last callback
-      vanilla.receiveTasks(id, [{
-        method: 'callback',
-        args: ['1', 'last message', true]
-      }])
-      expect(vanilla.getRoot(id).attr).to.deep.equal({ from: 'last message' })
-    })
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/api/WeexInstance.js
----------------------------------------------------------------------
diff --git a/runtime/api/WeexInstance.js b/runtime/api/WeexInstance.js
new file mode 100644
index 0000000..3bc25e6
--- /dev/null
+++ b/runtime/api/WeexInstance.js
@@ -0,0 +1,138 @@
+/*
+ * 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 from '../vdom/Document'
+import { isRegisteredModule, getModuleDescription } from './module'
+import { isRegisteredComponent } from './component'
+import { getTaskCenter } from '../vdom/operation'
+
+const moduleProxies = {}
+
+function setId (weex, id) {
+  Object.defineProperty(weex, '[[CurrentInstanceId]]', { value: id })
+}
+
+function getId (weex) {
+  return weex['[[CurrentInstanceId]]']
+}
+
+function moduleGetter (id, module, method) {
+  const taskCenter = getTaskCenter(id)
+  if (!taskCenter || typeof taskCenter.send !== 'function') {
+    console.error(`[JS Framework] Failed to find taskCenter (${id}).`)
+    return null
+  }
+  return (...args) => taskCenter.send('module', { module, method }, args)
+}
+
+function moduleSetter (id, module, method, fn) {
+  const taskCenter = getTaskCenter(id)
+  if (!taskCenter || typeof taskCenter.send !== 'function') {
+    console.error(`[JS Framework] Failed to find taskCenter (${id}).`)
+    return null
+  }
+  if (typeof fn !== 'function') {
+    console.error(`[JS Framework] ${module}.${method} must be assigned as a function.`)
+    return null
+  }
+  return fn => taskCenter.send('module', { module, method }, [fn])
+}
+
+export default class WeexInstance {
+  constructor (id, config) {
+    setId(this, String(id))
+    this.config = config || {}
+    this.document = new Document(id, this.config.bundleUrl)
+    this.requireModule = this.requireModule.bind(this)
+    this.isRegisteredModule = isRegisteredModule
+    this.isRegisteredComponent = isRegisteredComponent
+  }
+
+  requireModule (moduleName) {
+    const id = getId(this)
+    if (!(id && this.document && this.document.taskCenter)) {
+      console.error(`[JS Framework] Failed to requireModule("${moduleName}"), `
+        + `instance (${id}) doesn't exist anymore.`)
+      return
+    }
+
+    // warn for unknown module
+    if (!isRegisteredModule(moduleName)) {
+      console.warn(`[JS Framework] using unregistered weex module "${moduleName}"`)
+      return
+    }
+
+    // create new module proxy
+    const proxyName = `${moduleName}#${id}`
+    if (!moduleProxies[proxyName]) {
+      // create registered module apis
+      const moduleDefine = getModuleDescription(moduleName)
+      const moduleApis = {}
+      for (const methodName in moduleDefine) {
+        Object.defineProperty(moduleApis, methodName, {
+          enumerable: true,
+          configurable: true,
+          get: () => moduleGetter(id, moduleName, methodName),
+          set: fn => moduleSetter(id, moduleName, methodName, fn)
+        })
+      }
+
+      // create module Proxy
+      if (typeof Proxy === 'function') {
+        moduleProxies[proxyName] = new Proxy(moduleApis, {
+          get (target, methodName) {
+            if (methodName in target) {
+              return target[methodName]
+            }
+            console.warn(`[JS Framework] using unregistered method "${moduleName}.${methodName}"`)
+            return moduleGetter(id, moduleName, methodName)
+          }
+        })
+      }
+      else {
+        moduleProxies[proxyName] = moduleApis
+      }
+    }
+
+    return moduleProxies[proxyName]
+  }
+
+  supports (condition) {
+    if (typeof condition !== 'string') return null
+
+    const res = condition.match(/^@(\w+)\/(\w+)(\.(\w+))?$/i)
+    if (res) {
+      const type = res[1]
+      const name = res[2]
+      const method = res[4]
+      switch (type) {
+        case 'module': return isRegisteredModule(name, method)
+        case 'component': return isRegisteredComponent(name)
+      }
+    }
+
+    return null
+  }
+
+  // registerStyleSheet (styles) {
+  //   if (this.document) {
+  //     this.document.registerStyleSheet(styles)
+  //   }
+  // }
+}



[20/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
* [jsfm] Adjust the file structure of js framework

1. Rename `html5/` to `runtime/` and move api, bridge, vdom out:

  rename: html5/frameworks       -> runtime/frameworks
  rename: html5/runtime/api      -> runtime/api
  rename: html5/runtime/bridge   -> runtime/bridge
  rename: html5/runtime/vdom     -> runtime/vdom
  rename: html5/runtime/utils.js -> runtime/shared/utils.js
  rename: html5/services         -> runtime/services
  rename: html5/shared           -> runtime/shared

2. Delete `html5/render/`, move it to `entries`:

  delete: html5/render/
  rename: html5/runtime/index.js       -> runtime/entries/runtime.js
  rename: html5/render/native/index.js -> runtime/entries/index.js
  rename: html5/render/native/setup.js -> runtime/entries/setup.js

3. Move `html5/test` into `test/` folder:

  rename: html5/test -> test/js-framework


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

Branch: refs/heads/master
Commit: 51dd277e5cd579c7fda524a0a0972e2972d04ab8
Parents: dc42844
Author: Hanks <zh...@gmail.com>
Authored: Fri Dec 29 20:37:26 2017 +0800
Committer: Hanks <zh...@gmail.com>
Committed: Fri Dec 29 20:58:12 2017 +0800

----------------------------------------------------------------------
 html5/frameworks/index.js                       |   29 -
 html5/frameworks/legacy/api/methods.js          |  194 --
 html5/frameworks/legacy/api/modules.js          |   96 -
 html5/frameworks/legacy/app/bundle/bootstrap.js |   90 -
 html5/frameworks/legacy/app/bundle/define.js    |  115 -
 html5/frameworks/legacy/app/bundle/index.js     |   34 -
 html5/frameworks/legacy/app/ctrl/index.js       |   40 -
 html5/frameworks/legacy/app/ctrl/init.js        |  215 --
 html5/frameworks/legacy/app/ctrl/misc.js        |  206 --
 html5/frameworks/legacy/app/differ.js           |   91 -
 html5/frameworks/legacy/app/downgrade.js        |  172 --
 html5/frameworks/legacy/app/index.js            |   55 -
 html5/frameworks/legacy/app/instance.js         |   47 -
 html5/frameworks/legacy/app/register.js         |  131 --
 html5/frameworks/legacy/app/viewport.js         |   56 -
 html5/frameworks/legacy/config.js               |   35 -
 html5/frameworks/legacy/core/LICENSE            |   21 -
 html5/frameworks/legacy/core/array.js           |  100 -
 html5/frameworks/legacy/core/dep.js             |   80 -
 html5/frameworks/legacy/core/object.js          |   91 -
 html5/frameworks/legacy/core/observer.js        |  323 ---
 html5/frameworks/legacy/core/state.js           |   92 -
 html5/frameworks/legacy/core/watcher.js         |  254 ---
 html5/frameworks/legacy/index.js                |   40 -
 html5/frameworks/legacy/static/bridge.js        |   56 -
 html5/frameworks/legacy/static/create.js        |   51 -
 html5/frameworks/legacy/static/life.js          |   91 -
 html5/frameworks/legacy/static/map.js           |   19 -
 html5/frameworks/legacy/static/misc.js          |   40 -
 html5/frameworks/legacy/static/register.js      |   75 -
 html5/frameworks/legacy/util/LICENSE            |   21 -
 html5/frameworks/legacy/util/index.js           |  156 --
 html5/frameworks/legacy/util/shared.js          |  162 --
 html5/frameworks/legacy/vm/compiler.js          |  636 ------
 html5/frameworks/legacy/vm/directive.js         |  377 ----
 html5/frameworks/legacy/vm/dom-helper.js        |  258 ---
 html5/frameworks/legacy/vm/events.js            |  179 --
 html5/frameworks/legacy/vm/index.js             |  131 --
 html5/frameworks/vanilla/index.js               |  110 -
 html5/render/native/index.js                    |   22 -
 html5/render/native/setup.js                    |   58 -
 html5/render/vue/README.md                      |    3 -
 html5/runtime/api/WeexInstance.js               |  138 --
 html5/runtime/api/component.js                  |   51 -
 html5/runtime/api/config.js                     |   37 -
 html5/runtime/api/init.js                       |  304 ---
 html5/runtime/api/module.js                     |   56 -
 html5/runtime/api/service.js                    |   77 -
 html5/runtime/bridge/CallbackManager.js         |   89 -
 html5/runtime/bridge/Handler.js                 |   91 -
 html5/runtime/bridge/Listener.js                |  220 --
 html5/runtime/bridge/TaskCenter.js              |  145 --
 html5/runtime/bridge/debug.js                   |   65 -
 html5/runtime/bridge/normalize.js               |   91 -
 html5/runtime/bridge/receiver.js                |   71 -
 html5/runtime/index.js                          |   55 -
 html5/runtime/utils.js                          |   71 -
 html5/runtime/vdom/Comment.js                   |   43 -
 html5/runtime/vdom/Document.js                  |  189 --
 html5/runtime/vdom/Element.js                   |  524 -----
 html5/runtime/vdom/Node.js                      |   47 -
 html5/runtime/vdom/WeexElement.js               |   84 -
 html5/runtime/vdom/index.js                     |   37 -
 html5/runtime/vdom/operation.js                 |  272 ---
 html5/services/amd/index.js                     |  105 -
 html5/services/broadcast-channel/index.js       |  125 --
 .../services/broadcast-channel/message-event.js |   40 -
 html5/services/index.js                         |   23 -
 html5/shared/arrayFrom.js                       |  105 -
 html5/shared/console.js                         |  137 --
 html5/shared/freeze.js                          |   90 -
 html5/shared/index.js                           |   32 -
 html5/shared/objectAssign.js                    |   19 -
 html5/shared/objectSetPrototypeOf.js            |   74 -
 html5/shared/promise.js                         |   28 -
 html5/shared/setTimeout.js                      |   63 -
 html5/test/case/.eslintrc                       |    9 -
 html5/test/case/basic/append-root.output.js     |   36 -
 html5/test/case/basic/append-root.source.js     |   75 -
 html5/test/case/basic/append.output.js          |   79 -
 html5/test/case/basic/append.source.js          |  123 --
 html5/test/case/basic/backward1.output.js       |   57 -
 html5/test/case/basic/backward1.source.js       |  113 -
 html5/test/case/basic/backward2.output.js       |   57 -
 html5/test/case/basic/backward2.source.js       |  113 -
 html5/test/case/basic/components.output.js      |   20 -
 html5/test/case/basic/components.source.js      |  101 -
 .../case/basic/computed-in-repeat.output.js     |   19 -
 .../case/basic/computed-in-repeat.source.js     |   82 -
 html5/test/case/basic/created.output.js         |   11 -
 html5/test/case/basic/created.source.js         |   56 -
 html5/test/case/basic/dynamic-id.output.js      |   44 -
 html5/test/case/basic/dynamic-id.source.js      |   94 -
 .../test/case/basic/dynamic-property.output.js  |   24 -
 .../test/case/basic/dynamic-property.source.js  |   79 -
 html5/test/case/basic/dynamic-type.output.js    |   10 -
 html5/test/case/basic/dynamic-type.source.js    |   45 -
 html5/test/case/basic/foo.output.js             |    3 -
 html5/test/case/basic/foo.source.js             |   44 -
 html5/test/case/basic/foo2.output.js            |   10 -
 html5/test/case/basic/foo2.source.js            |   52 -
 html5/test/case/basic/foo3.output.js            |    9 -
 html5/test/case/basic/foo3.source.js            |   41 -
 html5/test/case/basic/foo4.output.js            |   12 -
 html5/test/case/basic/foo4.source.js            |   44 -
 html5/test/case/basic/foo5.output.js            |   14 -
 html5/test/case/basic/foo5.source.js            |   50 -
 html5/test/case/basic/foo6.output.js            |   28 -
 html5/test/case/basic/foo6.source.js            |   71 -
 html5/test/case/basic/foo7.output.js            |   26 -
 html5/test/case/basic/foo7.source.js            |   76 -
 html5/test/case/basic/foo8.output.js            |   60 -
 html5/test/case/basic/foo8.source.js            |   91 -
 html5/test/case/basic/foo9.output.js            |   57 -
 html5/test/case/basic/foo9.source.js            |  113 -
 .../case/basic/global-weex-object.output.js     |    6 -
 .../case/basic/global-weex-object.source.js     |   37 -
 html5/test/case/basic/id.output.js              |   32 -
 html5/test/case/basic/id.source.js              |   80 -
 html5/test/case/basic/if-repeat.output.js       |   13 -
 html5/test/case/basic/if-repeat.source.js       |   60 -
 html5/test/case/basic/if-root.output.js         |    1 -
 html5/test/case/basic/if-root.source.js         |   44 -
 html5/test/case/basic/if.output.js              |   13 -
 html5/test/case/basic/if.source.js              |   57 -
 html5/test/case/basic/promise.output.js         |    3 -
 html5/test/case/basic/promise.source.js         |   47 -
 html5/test/case/basic/ready.output.js           |   11 -
 html5/test/case/basic/ready.source.js           |   56 -
 html5/test/case/basic/repeat-array-kv.output.js |   22 -
 html5/test/case/basic/repeat-array-kv.source.js |   63 -
 .../case/basic/repeat-array-no-kv.output.js     |   22 -
 .../case/basic/repeat-array-no-kv.source.js     |   61 -
 .../case/basic/repeat-array-non-obj.output.js   |   22 -
 .../case/basic/repeat-array-non-obj.source.js   |   62 -
 html5/test/case/basic/repeat-array-v.output.js  |   22 -
 html5/test/case/basic/repeat-array-v.source.js  |   62 -
 html5/test/case/basic/repeat-index.output.js    |   22 -
 html5/test/case/basic/repeat-index.source.js    |   59 -
 html5/test/case/basic/repeat-root.output.js     |    1 -
 html5/test/case/basic/repeat-root.source.js     |   47 -
 html5/test/case/basic/repeat-watch.output.js    |   46 -
 html5/test/case/basic/repeat-watch.source.js    |   75 -
 html5/test/case/basic/require.output.js         |    9 -
 html5/test/case/basic/require.source.js         |   52 -
 html5/test/case/basic/reset-style.output.js     |   15 -
 html5/test/case/basic/reset-style.source.js     |   58 -
 html5/test/case/basic/static1.output.js         |   12 -
 html5/test/case/basic/static1.source.js         |   51 -
 html5/test/case/basic/static2.output.js         |   30 -
 html5/test/case/basic/static2.source.js         |   62 -
 html5/test/case/basic/static3.output.js         |   33 -
 html5/test/case/basic/static3.source.js         |   62 -
 html5/test/case/basic/static4.output.js         |   35 -
 html5/test/case/basic/static4.source.js         |   93 -
 html5/test/case/basic/subvm.output.js           |   20 -
 html5/test/case/basic/subvm.source.js           |   98 -
 html5/test/case/basic/transformer1.output.js    |    3 -
 html5/test/case/basic/transformer1.source.js    |   46 -
 .../case/complex/append-root-event.output.js    |   13 -
 .../case/complex/append-root-event.source.js    |   63 -
 html5/test/case/complex/click.output.js         |   26 -
 html5/test/case/complex/click.source.js         |   76 -
 .../complex/component-append-tree.output.js     |   22 -
 .../complex/component-append-tree.source.js     |   87 -
 html5/test/case/complex/computed.output.js      |   15 -
 html5/test/case/complex/computed.source.js      |   72 -
 html5/test/case/complex/if-refresh.output.js    |    3 -
 html5/test/case/complex/if-refresh.source.js    |   57 -
 .../case/complex/if-repeat-refresh.output.js    |   22 -
 .../case/complex/if-repeat-refresh.source.js    |   60 -
 html5/test/case/complex/inline-click.output.js  |   14 -
 html5/test/case/complex/inline-click.source.js  |   54 -
 html5/test/case/complex/input-binding.output.js |   12 -
 html5/test/case/complex/input-binding.source.js |   78 -
 html5/test/case/complex/refresh2.output.js      |   20 -
 html5/test/case/complex/refresh2.source.js      |  133 --
 .../test/case/complex/repeat-track-by.output.js |   24 -
 .../test/case/complex/repeat-track-by.source.js |   64 -
 html5/test/case/complex/transformer2.output.js  |    3 -
 html5/test/case/complex/transformer2.source.js  |   46 -
 html5/test/case/complex/transformer3.output.js  |    3 -
 html5/test/case/complex/transformer3.source.js  |   46 -
 .../case/multi/clear-dep-target-error.source.js |   45 -
 .../case/multi/clear-dep-target-fine.output.js  |   11 -
 .../case/multi/clear-dep-target-fine.source.js  |   48 -
 html5/test/case/multi/clear-moduleA.output.js   |   12 -
 html5/test/case/multi/clear-moduleA.source.js   |   61 -
 html5/test/case/multi/clear-moduleB.output.js   |   11 -
 html5/test/case/multi/clear-moduleB.source.js   |   50 -
 html5/test/case/prepare.js                      |   83 -
 html5/test/case/signals/modal.output.js         |    9 -
 html5/test/case/signals/modal.source.js         |   60 -
 html5/test/case/signals/signals-long.source.js  | 1223 -----------
 html5/test/case/signals/signals.source.js       |   84 -
 html5/test/case/signals/timer.output.js         |    9 -
 html5/test/case/signals/timer.source.js         |   54 -
 html5/test/case/tester.js                       |  528 -----
 .../test/case/throws/global-variable1.source.js |   35 -
 .../test/case/throws/global-variable2.source.js |   33 -
 .../test/case/throws/global-variable3.source.js |   40 -
 html5/test/unit/default/api/methods.js          |  190 --
 html5/test/unit/default/api/modules.js          |   43 -
 html5/test/unit/default/app/bundle.js           |  409 ----
 html5/test/unit/default/app/ctrl.js             |  230 --
 html5/test/unit/default/app/downgrade.js        |  265 ---
 html5/test/unit/default/app/index.js            |  147 --
 html5/test/unit/default/app/register.js         |  200 --
 html5/test/unit/default/app/viewport.js         |   79 -
 html5/test/unit/default/util/index.js           |  185 --
 html5/test/unit/default/util/shared.js          |  143 --
 html5/test/unit/default/vm/directive.js         |  228 --
 html5/test/unit/default/vm/dom-helper.js        |  405 ----
 html5/test/unit/default/vm/events.js            |  416 ----
 html5/test/unit/default/vm/vm.js                | 1975 ------------------
 html5/test/unit/runtime/legacy-framework.js     |  348 ---
 html5/test/unit/runtime/runner/helper.js        |   73 -
 html5/test/unit/runtime/runner/vanilla.js       |   59 -
 html5/test/unit/runtime/runner/vue.js           |   67 -
 html5/test/unit/runtime/utils.js                |   41 -
 html5/test/unit/runtime/vdom/index.js           |  639 ------
 html5/test/unit/runtime/vdom/listener.js        |  381 ----
 html5/test/unit/runtime/vdom/node.js            |   39 -
 html5/test/unit/shared/BroadcastChannel.js      |  231 --
 html5/test/unit/shared/arrayFrom.js             |   90 -
 html5/test/unit/shared/console.js               |  224 --
 html5/test/unit/shared/index.js                 |   73 -
 html5/test/unit/shared/objectAssign.js          |   50 -
 html5/test/unit/shared/objectSetPrototypeOf.js  |   42 -
 html5/test/unit/vanilla/index.js                |  159 --
 runtime/api/WeexInstance.js                     |  138 ++
 runtime/api/component.js                        |   51 +
 runtime/api/config.js                           |   37 +
 runtime/api/index.js                            |   40 +
 runtime/api/init.js                             |  304 +++
 runtime/api/module.js                           |   56 +
 runtime/api/service.js                          |   77 +
 runtime/bridge/CallbackManager.js               |   89 +
 runtime/bridge/Handler.js                       |   91 +
 runtime/bridge/Listener.js                      |  220 ++
 runtime/bridge/TaskCenter.js                    |  145 ++
 runtime/bridge/debug.js                         |   65 +
 runtime/bridge/normalize.js                     |   91 +
 runtime/bridge/receiver.js                      |   71 +
 runtime/entries/index.js                        |   23 +
 runtime/entries/rax.js                          |   23 +
 runtime/entries/setup.js                        |   60 +
 runtime/entries/vue.js                          |   23 +
 runtime/frameworks/index.js                     |   29 +
 runtime/frameworks/legacy/api/methods.js        |  194 ++
 runtime/frameworks/legacy/api/modules.js        |   96 +
 .../frameworks/legacy/app/bundle/bootstrap.js   |   90 +
 runtime/frameworks/legacy/app/bundle/define.js  |  115 +
 runtime/frameworks/legacy/app/bundle/index.js   |   34 +
 runtime/frameworks/legacy/app/ctrl/index.js     |   40 +
 runtime/frameworks/legacy/app/ctrl/init.js      |  215 ++
 runtime/frameworks/legacy/app/ctrl/misc.js      |  206 ++
 runtime/frameworks/legacy/app/differ.js         |   91 +
 runtime/frameworks/legacy/app/downgrade.js      |  172 ++
 runtime/frameworks/legacy/app/index.js          |   55 +
 runtime/frameworks/legacy/app/instance.js       |   47 +
 runtime/frameworks/legacy/app/register.js       |  131 ++
 runtime/frameworks/legacy/app/viewport.js       |   56 +
 runtime/frameworks/legacy/config.js             |   35 +
 runtime/frameworks/legacy/core/LICENSE          |   21 +
 runtime/frameworks/legacy/core/array.js         |  100 +
 runtime/frameworks/legacy/core/dep.js           |   80 +
 runtime/frameworks/legacy/core/object.js        |   91 +
 runtime/frameworks/legacy/core/observer.js      |  323 +++
 runtime/frameworks/legacy/core/state.js         |   92 +
 runtime/frameworks/legacy/core/watcher.js       |  254 +++
 runtime/frameworks/legacy/index.js              |   40 +
 runtime/frameworks/legacy/static/bridge.js      |   56 +
 runtime/frameworks/legacy/static/create.js      |   51 +
 runtime/frameworks/legacy/static/life.js        |   91 +
 runtime/frameworks/legacy/static/map.js         |   19 +
 runtime/frameworks/legacy/static/misc.js        |   40 +
 runtime/frameworks/legacy/static/register.js    |   75 +
 runtime/frameworks/legacy/util/LICENSE          |   21 +
 runtime/frameworks/legacy/util/index.js         |  156 ++
 runtime/frameworks/legacy/util/shared.js        |  162 ++
 runtime/frameworks/legacy/vm/compiler.js        |  636 ++++++
 runtime/frameworks/legacy/vm/directive.js       |  377 ++++
 runtime/frameworks/legacy/vm/dom-helper.js      |  258 +++
 runtime/frameworks/legacy/vm/events.js          |  179 ++
 runtime/frameworks/legacy/vm/index.js           |  131 ++
 runtime/frameworks/vanilla/index.js             |  110 +
 runtime/services/amd/index.js                   |  105 +
 runtime/services/broadcast-channel/index.js     |  125 ++
 .../services/broadcast-channel/message-event.js |   40 +
 runtime/services/index.js                       |   23 +
 runtime/shared/env/console.js                   |  137 ++
 runtime/shared/env/freeze.js                    |   90 +
 runtime/shared/env/setTimeout.js                |   63 +
 runtime/shared/index.js                         |   33 +
 runtime/shared/polyfill/arrayFrom.js            |  105 +
 runtime/shared/polyfill/objectAssign.js         |   19 +
 runtime/shared/polyfill/objectSetPrototypeOf.js |   74 +
 runtime/shared/polyfill/promise.js              |   28 +
 runtime/shared/utils.js                         |   71 +
 runtime/vdom/Comment.js                         |   43 +
 runtime/vdom/Document.js                        |  189 ++
 runtime/vdom/Element.js                         |  524 +++++
 runtime/vdom/Node.js                            |   47 +
 runtime/vdom/WeexElement.js                     |   84 +
 runtime/vdom/index.js                           |   37 +
 runtime/vdom/operation.js                       |  272 +++
 test/js-framework/case/.eslintrc                |    9 +
 .../case/basic/append-root.output.js            |   36 +
 .../case/basic/append-root.source.js            |   75 +
 test/js-framework/case/basic/append.output.js   |   79 +
 test/js-framework/case/basic/append.source.js   |  123 ++
 .../js-framework/case/basic/backward1.output.js |   57 +
 .../js-framework/case/basic/backward1.source.js |  113 +
 .../js-framework/case/basic/backward2.output.js |   57 +
 .../js-framework/case/basic/backward2.source.js |  113 +
 .../case/basic/components.output.js             |   20 +
 .../case/basic/components.source.js             |  101 +
 .../case/basic/computed-in-repeat.output.js     |   19 +
 .../case/basic/computed-in-repeat.source.js     |   82 +
 test/js-framework/case/basic/created.output.js  |   11 +
 test/js-framework/case/basic/created.source.js  |   56 +
 .../case/basic/dynamic-id.output.js             |   44 +
 .../case/basic/dynamic-id.source.js             |   94 +
 .../case/basic/dynamic-property.output.js       |   24 +
 .../case/basic/dynamic-property.source.js       |   79 +
 .../case/basic/dynamic-type.output.js           |   10 +
 .../case/basic/dynamic-type.source.js           |   45 +
 test/js-framework/case/basic/foo.output.js      |    3 +
 test/js-framework/case/basic/foo.source.js      |   44 +
 test/js-framework/case/basic/foo2.output.js     |   10 +
 test/js-framework/case/basic/foo2.source.js     |   52 +
 test/js-framework/case/basic/foo3.output.js     |    9 +
 test/js-framework/case/basic/foo3.source.js     |   41 +
 test/js-framework/case/basic/foo4.output.js     |   12 +
 test/js-framework/case/basic/foo4.source.js     |   44 +
 test/js-framework/case/basic/foo5.output.js     |   14 +
 test/js-framework/case/basic/foo5.source.js     |   50 +
 test/js-framework/case/basic/foo6.output.js     |   28 +
 test/js-framework/case/basic/foo6.source.js     |   71 +
 test/js-framework/case/basic/foo7.output.js     |   26 +
 test/js-framework/case/basic/foo7.source.js     |   76 +
 test/js-framework/case/basic/foo8.output.js     |   60 +
 test/js-framework/case/basic/foo8.source.js     |   91 +
 test/js-framework/case/basic/foo9.output.js     |   57 +
 test/js-framework/case/basic/foo9.source.js     |  113 +
 .../case/basic/global-weex-object.output.js     |    6 +
 .../case/basic/global-weex-object.source.js     |   37 +
 test/js-framework/case/basic/id.output.js       |   32 +
 test/js-framework/case/basic/id.source.js       |   80 +
 .../js-framework/case/basic/if-repeat.output.js |   13 +
 .../js-framework/case/basic/if-repeat.source.js |   60 +
 test/js-framework/case/basic/if-root.output.js  |    1 +
 test/js-framework/case/basic/if-root.source.js  |   44 +
 test/js-framework/case/basic/if.output.js       |   13 +
 test/js-framework/case/basic/if.source.js       |   57 +
 test/js-framework/case/basic/promise.output.js  |    3 +
 test/js-framework/case/basic/promise.source.js  |   47 +
 test/js-framework/case/basic/ready.output.js    |   11 +
 test/js-framework/case/basic/ready.source.js    |   56 +
 .../case/basic/repeat-array-kv.output.js        |   22 +
 .../case/basic/repeat-array-kv.source.js        |   63 +
 .../case/basic/repeat-array-no-kv.output.js     |   22 +
 .../case/basic/repeat-array-no-kv.source.js     |   61 +
 .../case/basic/repeat-array-non-obj.output.js   |   22 +
 .../case/basic/repeat-array-non-obj.source.js   |   62 +
 .../case/basic/repeat-array-v.output.js         |   22 +
 .../case/basic/repeat-array-v.source.js         |   62 +
 .../case/basic/repeat-index.output.js           |   22 +
 .../case/basic/repeat-index.source.js           |   59 +
 .../case/basic/repeat-root.output.js            |    1 +
 .../case/basic/repeat-root.source.js            |   47 +
 .../case/basic/repeat-watch.output.js           |   46 +
 .../case/basic/repeat-watch.source.js           |   75 +
 test/js-framework/case/basic/require.output.js  |    9 +
 test/js-framework/case/basic/require.source.js  |   52 +
 .../case/basic/reset-style.output.js            |   15 +
 .../case/basic/reset-style.source.js            |   58 +
 test/js-framework/case/basic/static1.output.js  |   12 +
 test/js-framework/case/basic/static1.source.js  |   51 +
 test/js-framework/case/basic/static2.output.js  |   30 +
 test/js-framework/case/basic/static2.source.js  |   62 +
 test/js-framework/case/basic/static3.output.js  |   33 +
 test/js-framework/case/basic/static3.source.js  |   62 +
 test/js-framework/case/basic/static4.output.js  |   35 +
 test/js-framework/case/basic/static4.source.js  |   93 +
 test/js-framework/case/basic/subvm.output.js    |   20 +
 test/js-framework/case/basic/subvm.source.js    |   98 +
 .../case/basic/transformer1.output.js           |    3 +
 .../case/basic/transformer1.source.js           |   46 +
 .../case/complex/append-root-event.output.js    |   13 +
 .../case/complex/append-root-event.source.js    |   63 +
 test/js-framework/case/complex/click.output.js  |   26 +
 test/js-framework/case/complex/click.source.js  |   76 +
 .../complex/component-append-tree.output.js     |   22 +
 .../complex/component-append-tree.source.js     |   87 +
 .../case/complex/computed.output.js             |   15 +
 .../case/complex/computed.source.js             |   72 +
 .../case/complex/if-refresh.output.js           |    3 +
 .../case/complex/if-refresh.source.js           |   57 +
 .../case/complex/if-repeat-refresh.output.js    |   22 +
 .../case/complex/if-repeat-refresh.source.js    |   60 +
 .../case/complex/inline-click.output.js         |   14 +
 .../case/complex/inline-click.source.js         |   54 +
 .../case/complex/input-binding.output.js        |   12 +
 .../case/complex/input-binding.source.js        |   78 +
 .../case/complex/refresh2.output.js             |   20 +
 .../case/complex/refresh2.source.js             |  133 ++
 .../case/complex/repeat-track-by.output.js      |   24 +
 .../case/complex/repeat-track-by.source.js      |   64 +
 .../case/complex/transformer2.output.js         |    3 +
 .../case/complex/transformer2.source.js         |   46 +
 .../case/complex/transformer3.output.js         |    3 +
 .../case/complex/transformer3.source.js         |   46 +
 .../case/multi/clear-dep-target-error.source.js |   45 +
 .../case/multi/clear-dep-target-fine.output.js  |   11 +
 .../case/multi/clear-dep-target-fine.source.js  |   48 +
 .../case/multi/clear-moduleA.output.js          |   12 +
 .../case/multi/clear-moduleA.source.js          |   61 +
 .../case/multi/clear-moduleB.output.js          |   11 +
 .../case/multi/clear-moduleB.source.js          |   50 +
 test/js-framework/case/prepare.js               |   83 +
 test/js-framework/case/signals/modal.output.js  |    9 +
 test/js-framework/case/signals/modal.source.js  |   60 +
 .../case/signals/signals-long.source.js         | 1223 +++++++++++
 .../js-framework/case/signals/signals.source.js |   84 +
 test/js-framework/case/signals/timer.output.js  |    9 +
 test/js-framework/case/signals/timer.source.js  |   54 +
 test/js-framework/case/tester.js                |  528 +++++
 .../case/throws/global-variable1.source.js      |   35 +
 .../case/throws/global-variable2.source.js      |   33 +
 .../case/throws/global-variable3.source.js      |   40 +
 test/js-framework/unit/default/api/methods.js   |  190 ++
 test/js-framework/unit/default/api/modules.js   |   43 +
 test/js-framework/unit/default/app/bundle.js    |  409 ++++
 test/js-framework/unit/default/app/ctrl.js      |  230 ++
 test/js-framework/unit/default/app/downgrade.js |  265 +++
 test/js-framework/unit/default/app/index.js     |  147 ++
 test/js-framework/unit/default/app/register.js  |  200 ++
 test/js-framework/unit/default/app/viewport.js  |   79 +
 test/js-framework/unit/default/util/index.js    |  185 ++
 test/js-framework/unit/default/util/shared.js   |  143 ++
 test/js-framework/unit/default/vm/directive.js  |  228 ++
 test/js-framework/unit/default/vm/dom-helper.js |  405 ++++
 test/js-framework/unit/default/vm/events.js     |  416 ++++
 test/js-framework/unit/default/vm/vm.js         | 1975 ++++++++++++++++++
 .../unit/runtime/legacy-framework.js            |  348 +++
 test/js-framework/unit/runtime/runner/helper.js |   73 +
 .../js-framework/unit/runtime/runner/vanilla.js |   59 +
 test/js-framework/unit/runtime/runner/vue.js    |   67 +
 test/js-framework/unit/runtime/utils.js         |   41 +
 test/js-framework/unit/runtime/vdom/index.js    |  639 ++++++
 test/js-framework/unit/runtime/vdom/listener.js |  381 ++++
 test/js-framework/unit/runtime/vdom/node.js     |   39 +
 .../unit/shared/BroadcastChannel.js             |  231 ++
 test/js-framework/unit/shared/arrayFrom.js      |   90 +
 test/js-framework/unit/shared/console.js        |  227 ++
 test/js-framework/unit/shared/index.js          |   73 +
 test/js-framework/unit/shared/objectAssign.js   |   50 +
 .../unit/shared/objectSetPrototypeOf.js         |   42 +
 test/js-framework/unit/vanilla/index.js         |  159 ++
 461 files changed, 23298 insertions(+), 23263 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/index.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/index.js b/html5/frameworks/index.js
deleted file mode 100644
index 8880f75..0000000
--- a/html5/frameworks/index.js
+++ /dev/null
@@ -1,29 +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 * as Vanilla from './vanilla/index'
-import * as Vue from 'weex-vue-framework'
-import * as Weex from './legacy/index'
-import Rax from 'weex-rax-framework'
-
-export default {
-  Vanilla,
-  Vue,
-  Rax,
-  Weex
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/api/methods.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/api/methods.js b/html5/frameworks/legacy/api/methods.js
deleted file mode 100644
index 19f1cdc..0000000
--- a/html5/frameworks/legacy/api/methods.js
+++ /dev/null
@@ -1,194 +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 The api for invoking with "$" prefix
- */
-
-/**
- * @deprecated use $vm instead
- * find the vm by id
- * Note: there is only one id in whole component
- * @param  {string} id
- * @return {Vm}
- */
-export function $ (id) {
-  console.warn('[JS Framework] Vm#$ is deprecated, please use Vm#$vm instead')
-  const info = this._ids[id]
-  if (info) {
-    return info.vm
-  }
-}
-
-/**
- * find the element by id
- * Note: there is only one id in whole component
- * @param  {string} id
- * @return {Element}
- */
-export function $el (id) {
-  const info = this._ids[id]
-  if (info) {
-    return info.el
-  }
-}
-
-/**
- * find the vm of the custom component by id
- * Note: there is only one id in whole component
- * @param  {string} id
- * @return {Vm}
- */
-export function $vm (id) {
-  const info = this._ids[id]
-  if (info) {
-    return info.vm
-  }
-}
-
-/**
- * Fire when differ rendering finished
- *
- * @param  {Function} fn
- */
-export function $renderThen (fn) {
-  const app = this._app
-  const differ = app.differ
-  return differ.then(() => {
-    fn()
-  })
-}
-
-/**
- * scroll an element specified by id into view,
- * moreover specify a number of offset optionally
- * @param  {string} id
- * @param  {number} offset
- */
-export function $scrollTo (id, offset) {
-  console.warn('[JS Framework] Vm#$scrollTo is deprecated, ' +
-          'please use "require(\'@weex-module/dom\')' +
-          '.scrollTo(el, options)" instead')
-  const el = this.$el(id)
-  if (el) {
-    const dom = this._app.requireModule('dom')
-    dom.scrollToElement(el.ref, { offset: offset })
-  }
-}
-
-/**
- * perform transition animation on an element specified by id
- * @param  {string}   id
- * @param  {object}   options
- * @param  {object}   options.styles
- * @param  {object}   options.duration(ms)
- * @param  {object}   [options.timingFunction]
- * @param  {object}   [options.delay=0(ms)]
- * @param  {Function} callback
- */
-export function $transition (id, options, callback) {
-  const el = this.$el(id)
-  if (el && options && options.styles) {
-    const animation = this._app.requireModule('animation')
-    animation.transition(el.ref, options, (...args) => {
-      this._setStyle(el, options.styles)
-      callback && callback(...args)
-    })
-  }
-}
-
-/**
- * get some config
- * @return {object} some config for app instance
- * @property {string} bundleUrl
- * @property {boolean} debug
- * @property {object} env
- * @property {string} env.weexVersion(ex. 1.0.0)
- * @property {string} env.appName(ex. TB/TM)
- * @property {string} env.appVersion(ex. 5.0.0)
- * @property {string} env.platform(ex. iOS/Android)
- * @property {string} env.osVersion(ex. 7.0.0)
- * @property {string} env.deviceModel **native only**
- * @property {number} env.[deviceWidth=750]
- * @property {number} env.deviceHeight
- */
-export function $getConfig (callback) {
-  const config = this._app.options
-  if (typeof callback === 'function') {
-    console.warn('[JS Framework] the callback of Vm#$getConfig(callback) is deprecated, ' +
-      'this api now can directly RETURN config info.')
-    callback(config)
-  }
-  return config
-}
-
-/**
- * @deprecated
- * request network via http protocol
- * @param  {object}   params
- * @param  {Function} callback
- */
-export function $sendHttp (params, callback) {
-  console.warn('[JS Framework] Vm#$sendHttp is deprecated, ' +
-          'please use "require(\'@weex-module/stream\')' +
-          '.sendHttp(params, callback)" instead')
-  const stream = this._app.requireModule('stream')
-  stream.sendHttp(params, callback)
-}
-
-/**
- * @deprecated
- * open a url
- * @param  {string} url
- */
-export function $openURL (url) {
-  console.warn('[JS Framework] Vm#$openURL is deprecated, ' +
-          'please use "require(\'@weex-module/event\')' +
-          '.openURL(url)" instead')
-  const event = this._app.requireModule('event')
-  event.openURL(url)
-}
-
-/**
- * @deprecated
- * set a title for page
- * @param  {string} title
- */
-export function $setTitle (title) {
-  console.warn('[JS Framework] Vm#$setTitle is deprecated, ' +
-          'please use "require(\'@weex-module/pageInfo\')' +
-          '.setTitle(title)" instead')
-  const pageInfo = this._app.requireModule('pageInfo')
-  pageInfo.setTitle(title)
-}
-
-/**
- * @deprecated use "require('@weex-module/moduleName') instead"
- * invoke a native method by specifing the name of module and method
- * @param  {string} moduleName
- * @param  {string} methodName
- * @param  {...*} the rest arguments
- */
-export function $call (moduleName, methodName, ...args) {
-  console.warn('[JS Framework] Vm#$call is deprecated, ' +
-    'please use "require(\'@weex-module/moduleName\')" instead')
-  const module = this._app.requireModule(moduleName)
-  if (module && module[methodName]) {
-    module[methodName](...args)
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/api/modules.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/api/modules.js b/html5/frameworks/legacy/api/modules.js
deleted file mode 100644
index 60123b0..0000000
--- a/html5/frameworks/legacy/api/modules.js
+++ /dev/null
@@ -1,96 +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.
- */
-/**
- * @deprecated after native register these modules
- */
-
-/**
- * @module dom
- * @type {Array}
- */
-export const dom = [
-  /**
-   * @method scrollToElement
-   * @param {string} ref
-   * @param {object} options
-   * @param {number} options.offset
-   */
-  {
-    name: 'scrollToElement',
-    args: ['string', 'object']
-  }
-]
-
-/**
- * @module stream
- */
-export const stream = [
-  /**
-   * @method sendHttp
-   * @param  {object} params
-   * @param  {string} params.url
-   * @param  {string} [params.method]
-   * @param  {object} [params.header]
-   * @param  {object} params.body
-   * @param  {function} callback
-   */
-  {
-    name: 'sendHttp',
-    args: ['object', 'function']
-  }
-]
-
-/**
- * @module event
- */
-export const event = [
-  /**
-   * @method openURL
-   * @param {string} url
-   */
-  {
-    name: 'openURL',
-    args: ['string']
-  }
-]
-
-/**
- * @module pageInfo
- */
-export const pageInfo = [
-  /**
-   * @method setTitle
-   * @param {string} text
-   */
-  {
-    name: 'setTitle',
-    args: ['string']
-  }
-]
-
-/**
- * @module animation
- */
-export const animation = [
-
-  {
-    name: 'transition',
-    args: ['string', 'object', 'function']
-  }
-]

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/app/bundle/bootstrap.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/app/bundle/bootstrap.js b/html5/frameworks/legacy/app/bundle/bootstrap.js
deleted file mode 100644
index 8821873..0000000
--- a/html5/frameworks/legacy/app/bundle/bootstrap.js
+++ /dev/null
@@ -1,90 +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 semver from 'semver'
-import Vm from '../../vm/index'
-import * as downgrade from '../downgrade'
-import { setViewport } from '../viewport'
-import {
-  requireCustomComponent
-} from '../register'
-import {
-  isPlainObject,
-  isWeexComponent,
-  isNpmModule,
-  removeWeexPrefix,
-  removeJSSurfix
-} from '../../util/index'
-
-/**
- * bootstrap app from a certain custom component with config & data
- */
-export function bootstrap (app, name, config, data) {
-  console.debug(`[JS Framework] bootstrap for ${name}`)
-
-  // 1. validate custom component name first
-  let cleanName
-  if (isWeexComponent(name)) {
-    cleanName = removeWeexPrefix(name)
-  }
-  else if (isNpmModule(name)) {
-    cleanName = removeJSSurfix(name)
-    // check if define by old 'define' method
-    /* istanbul ignore if */
-    if (!requireCustomComponent(app, cleanName)) {
-      return new Error(`It's not a component: ${name}`)
-    }
-  }
-  else {
-    return new Error(`Wrong component name: ${name}`)
-  }
-
-  // 2. validate configuration
-  config = isPlainObject(config) ? config : {}
-  // 2.1 transformer version check
-  if (typeof config.transformerVersion === 'string' &&
-    typeof global.transformerVersion === 'string' &&
-    !semver.satisfies(config.transformerVersion,
-      global.transformerVersion)) {
-    return new Error(`JS Bundle version: ${config.transformerVersion} ` +
-      `not compatible with ${global.transformerVersion}`)
-  }
-  // 2.2 downgrade version check
-  const downgradeResult = downgrade.check(config.downgrade)
-  /* istanbul ignore if */
-  if (downgradeResult.isDowngrade) {
-    app.callTasks([{
-      module: 'instanceWrap',
-      method: 'error',
-      args: [
-        downgradeResult.errorType,
-        downgradeResult.code,
-        downgradeResult.errorMessage
-      ]
-    }])
-    return new Error(`Downgrade[${downgradeResult.code}]: ${downgradeResult.errorMessage}`)
-  }
-
-  // set viewport
-  if (config.viewport) {
-    setViewport(app, config.viewport)
-  }
-
-  // 3. create a new Vm with custom component name and data
-  app.vm = new Vm(cleanName, null, { _app: app }, null, data)
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/app/bundle/define.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/app/bundle/define.js b/html5/frameworks/legacy/app/bundle/define.js
deleted file mode 100644
index aec30f7..0000000
--- a/html5/frameworks/legacy/app/bundle/define.js
+++ /dev/null
@@ -1,115 +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 {
-  isWeexComponent,
-  isWeexModule,
-  isNormalModule,
-  isNpmModule,
-  removeWeexPrefix,
-  removeJSSurfix
-} from '../../util/index'
-import {
-  registerCustomComponent,
-  requireCustomComponent,
-  initModules
-} from '../register'
-
-/**
- * define(name, factory) for primary usage
- * or
- * define(name, deps, factory) for compatibility
- * Notice: DO NOT use function define() {},
- * it will cause error after builded by webpack
- */
-export const defineFn = function (app, name, ...args) {
-  console.debug(`[JS Framework] define a component ${name}`)
-
-  // adapt args:
-  // 1. name, deps[], factory()
-  // 2. name, factory()
-  // 3. name, definition{}
-  let factory, definition
-  if (args.length > 1) {
-    definition = args[1]
-  }
-  else {
-    definition = args[0]
-  }
-  if (typeof definition === 'function') {
-    factory = definition
-    definition = null
-  }
-
-  // resolve definition from factory
-  if (factory) {
-    const r = (name) => {
-      if (isWeexComponent(name)) {
-        const cleanName = removeWeexPrefix(name)
-        return requireCustomComponent(app, cleanName)
-      }
-      if (isWeexModule(name)) {
-        const cleanName = removeWeexPrefix(name)
-        return app.requireModule(cleanName)
-      }
-      if (isNormalModule(name) || isNpmModule(name)) {
-        const cleanName = removeJSSurfix(name)
-        return app.commonModules[cleanName]
-      }
-    }
-    const m = { exports: {}}
-    factory(r, m.exports, m)
-    definition = m.exports
-  }
-
-  // apply definition
-  if (isWeexComponent(name)) {
-    const cleanName = removeWeexPrefix(name)
-    registerCustomComponent(app, cleanName, definition)
-  }
-  else if (isWeexModule(name)) {
-    const cleanName = removeWeexPrefix(name)
-    initModules({ [cleanName]: definition })
-  }
-  else if (isNormalModule(name)) {
-    const cleanName = removeJSSurfix(name)
-    app.commonModules[cleanName] = definition
-  }
-  else if (isNpmModule(name)) {
-    const cleanName = removeJSSurfix(name)
-    if (definition.template ||
-        definition.style ||
-        definition.methods) {
-      // downgrade to old define method (define('componentName', factory))
-      // the exports contain one key of template, style or methods
-      // but it has risk!!!
-      registerCustomComponent(app, cleanName, definition)
-    }
-    else {
-      app.commonModules[cleanName] = definition
-    }
-  }
-}
-
-/**
- * @deprecated
- */
-export function register (app, type, options) {
-  console.warn('[JS Framework] Register is deprecated, please install lastest transformer.')
-  registerCustomComponent(app, type, options)
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/app/bundle/index.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/app/bundle/index.js b/html5/frameworks/legacy/app/bundle/index.js
deleted file mode 100644
index 40126cf..0000000
--- a/html5/frameworks/legacy/app/bundle/index.js
+++ /dev/null
@@ -1,34 +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
- * api that invoked by js bundle code
- *
- * - define(name, factory): define a new composed component type
- * - bootstrap(type, config, data): require a certain type &
- *         render with (optional) data
- *
- * deprecated:
- * - register(type, options): register a new composed component type
- * - render(type, data): render by a certain type with (optional) data
- * - require(type)(data): require a type then render with data
- */
-
-export { bootstrap } from './bootstrap'
-export { defineFn, register } from './define'

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/app/ctrl/index.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/app/ctrl/index.js b/html5/frameworks/legacy/app/ctrl/index.js
deleted file mode 100644
index 99fb3a7..0000000
--- a/html5/frameworks/legacy/app/ctrl/index.js
+++ /dev/null
@@ -1,40 +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
- * instance controls from native
- *
- * - init bundle
- * - fire event
- * - callback
- * - destroy
- *
- * corresponded with the API of instance manager (framework.js)
- */
-export { init } from './init'
-
-export {
-  refresh,
-  destroy,
-  getRootElement,
-  fireEvent,
-  callback,
-  updateActions,
-  callTasks
-} from './misc'

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/app/ctrl/init.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/app/ctrl/init.js b/html5/frameworks/legacy/app/ctrl/init.js
deleted file mode 100644
index 401d614..0000000
--- a/html5/frameworks/legacy/app/ctrl/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.
- */
-/**
- * @fileOverview
- * instance controls from native
- *
- * - init bundle
- *
- * corresponded with the API of instance manager (framework.js)
- */
-
-import Vm from '../../vm/index'
-import { removeWeexPrefix } from '../../util/index'
-import {
-  defineFn,
-  bootstrap,
-  register
-} from '../bundle/index'
-import { updateActions } from './misc'
-
-/**
- * Init an app by run code witgh data
- * @param  {object} app
- * @param  {string} code
- * @param  {object} data
- */
-export function init (app, code, data, services) {
-  console.debug('[JS Framework] Intialize an instance with:\n', data)
-  let result
-
-  // prepare app env methods
-  const bundleDefine = (...args) => defineFn(app, ...args)
-  const bundleBootstrap = (name, config, _data) => {
-    result = bootstrap(app, name, config, _data || data)
-    updateActions(app)
-    app.doc.listener.createFinish()
-    console.debug(`[JS Framework] After intialized an instance(${app.id})`)
-  }
-  const bundleVm = Vm
-  /* istanbul ignore next */
-  const bundleRegister = (...args) => register(app, ...args)
-  /* istanbul ignore next */
-  const bundleRender = (name, _data) => {
-    result = bootstrap(app, name, {}, _data)
-  }
-  /* istanbul ignore next */
-  const bundleRequire = name => _data => {
-    result = bootstrap(app, name, {}, _data)
-  }
-  const bundleDocument = app.doc
-  /* istanbul ignore next */
-  const bundleRequireModule = name => app.requireModule(removeWeexPrefix(name))
-
-  const weexGlobalObject = {
-    config: app.options,
-    define: bundleDefine,
-    bootstrap: bundleBootstrap,
-    requireModule: bundleRequireModule,
-    document: bundleDocument,
-    Vm: bundleVm
-  }
-
-  Object.freeze(weexGlobalObject)
-
-  // prepare code
-  let functionBody
-  /* istanbul ignore if */
-  if (typeof code === 'function') {
-    // `function () {...}` -> `{...}`
-    // not very strict
-    functionBody = code.toString().substr(12)
-  }
-  /* istanbul ignore next */
-  else if (code) {
-    functionBody = code.toString()
-  }
-  // wrap IFFE and use strict mode
-  functionBody = `(function(global){\n\n"use strict";\n\n ${functionBody} \n\n})(Object.create(this))`
-
-  // run code and get result
-  const { WXEnvironment } = global
-  const timerAPIs = {}
-
-  /* istanbul ignore if */
-  if (WXEnvironment && WXEnvironment.platform !== 'Web') {
-    // timer APIs polyfill in native
-    const timer = app.requireModule('timer')
-    Object.assign(timerAPIs, {
-      setTimeout: (...args) => {
-        const handler = function () {
-          args[0](...args.slice(2))
-        }
-        timer.setTimeout(handler, args[1])
-        return app.doc.taskCenter.callbackManager.lastCallbackId.toString()
-      },
-      setInterval: (...args) => {
-        const handler = function () {
-          args[0](...args.slice(2))
-        }
-        timer.setInterval(handler, args[1])
-        return app.doc.taskCenter.callbackManager.lastCallbackId.toString()
-      },
-      clearTimeout: (n) => {
-        timer.clearTimeout(n)
-      },
-      clearInterval: (n) => {
-        timer.clearInterval(n)
-      }
-    })
-  }
-  // run code and get result
-  const globalObjects = Object.assign({
-    define: bundleDefine,
-    require: bundleRequire,
-    bootstrap: bundleBootstrap,
-    register: bundleRegister,
-    render: bundleRender,
-    __weex_define__: bundleDefine, // alias for define
-    __weex_bootstrap__: bundleBootstrap, // alias for bootstrap
-    __weex_document__: bundleDocument,
-    __weex_require__: bundleRequireModule,
-    __weex_viewmodel__: bundleVm,
-    weex: weexGlobalObject
-  }, timerAPIs, services)
-  if (!callFunctionNative(globalObjects, functionBody)) {
-    // If failed to compile functionBody on native side,
-    // fallback to callFunction.
-    callFunction(globalObjects, functionBody)
-  }
-
-  return result
-}
-
-/**
- * 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)
-}
-
-/**
- * Call a new function generated on the V8 native side.
- * @param  {object} globalObjects
- * @param  {string} body
- * @return {boolean} return true if no error occurred.
- */
-function callFunctionNative (globalObjects, body) {
-  if (typeof compileAndRunBundle !== 'function') {
-    return false
-  }
-
-  let fn = void 0
-  let isNativeCompileOk = false
-  let script = '(function ('
-  const globalKeys = []
-  const globalValues = []
-  for (const key in globalObjects) {
-    globalKeys.push(key)
-    globalValues.push(globalObjects[key])
-  }
-  for (let i = 0; i < globalKeys.length - 1; ++i) {
-    script += globalKeys[i]
-    script += ','
-  }
-  script += globalKeys[globalKeys.length - 1]
-  script += ') {'
-  script += body
-  script += '} )'
-
-  try {
-    const weex = globalObjects.weex || {}
-    const config = weex.config || {}
-    fn = compileAndRunBundle(script,
-                             config.bundleUrl,
-                             config.bundleDigest,
-                             config.codeCachePath)
-    if (fn && typeof fn === 'function') {
-      fn(...globalValues)
-      isNativeCompileOk = true
-    }
-  }
-  catch (e) {
-    console.error(e)
-  }
-
-  return isNativeCompileOk
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/app/ctrl/misc.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/app/ctrl/misc.js b/html5/frameworks/legacy/app/ctrl/misc.js
deleted file mode 100644
index 13ab09e..0000000
--- a/html5/frameworks/legacy/app/ctrl/misc.js
+++ /dev/null
@@ -1,206 +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
- * instance controls from native
- *
- * - fire event
- * - callback
- * - refresh
- * - destroy
- *
- * corresponded with the API of instance manager (framework.js)
- */
-import { extend, typof } from '../../util/index'
-
-/**
- * Refresh an app with data to its root component options.
- * @param  {object} app
- * @param  {any}    data
- */
-export function refresh (app, data) {
-  console.debug(`[JS Framework] Refresh with`, data,
-            `in instance[${app.id}]`)
-  const vm = app.vm
-  if (vm && data) {
-    if (typeof vm.refreshData === 'function') {
-      vm.refreshData(data)
-    }
-    else {
-      extend(vm, data)
-    }
-    app.differ.flush()
-    app.doc.taskCenter.send('dom', { action: 'refreshFinish' }, [])
-    return
-  }
-  return new Error(`invalid data "${data}"`)
-}
-
-/**
- * Destroy an app.
- * @param  {object} app
- */
-export function destroy (app) {
-  console.debug(`[JS Framework] Destory an instance(${app.id})`)
-
-  if (app.vm) {
-    destroyVm(app.vm)
-  }
-
-  app.id = ''
-  app.options = null
-  app.blocks = null
-  app.vm = null
-  app.doc.taskCenter.destroyCallback()
-  app.doc.destroy()
-  app.doc = null
-  app.customComponentMap = null
-  app.commonModules = null
-}
-
-/**
- * Destroy an Vm.
- * @param {object} vm
- */
-export function destroyVm (vm) {
-  delete vm._app
-  delete vm._computed
-  delete vm._css
-  delete vm._data
-  delete vm._ids
-  delete vm._methods
-  delete vm._options
-  delete vm._parent
-  delete vm._parentEl
-  delete vm._rootEl
-
-  // remove all watchers
-  if (vm._watchers) {
-    let watcherCount = vm._watchers.length
-    while (watcherCount--) {
-      vm._watchers[watcherCount].teardown()
-    }
-    delete vm._watchers
-  }
-
-  // destroy child vms recursively
-  if (vm._childrenVms) {
-    let vmCount = vm._childrenVms.length
-    while (vmCount--) {
-      destroyVm(vm._childrenVms[vmCount])
-    }
-    delete vm._childrenVms
-  }
-
-  console.debug(`[JS Framework] "destroyed" lifecycle in Vm(${vm._type})`)
-  vm.$emit('hook:destroyed')
-
-  delete vm._type
-  delete vm._vmEvents
-}
-
-/**
- * Get a JSON object to describe the document body.
- * @param  {object} app
- * @return {object}
- */
-export function getRootElement (app) {
-  const doc = app.doc || {}
-  const body = doc.body || {}
-  return body.toJSON ? body.toJSON() : {}
-}
-
-/**
- * Fire an event from renderer. The event has type, an event object and an
- * element ref. If the event comes with some virtual-DOM changes, it should
- * have one more parameter to describe the changes.
- * @param  {object} app
- * @param  {string} ref
- * @param  {type}   type
- * @param  {object} e
- * @param  {object} domChanges
- */
-export function fireEvent (app, ref, type, e, domChanges) {
-  console.debug(`[JS Framework] Fire a "${type}" event on an element(${ref}) in instance(${app.id})`)
-  if (Array.isArray(ref)) {
-    ref.some((ref) => {
-      return fireEvent(app, ref, type, e) !== false
-    })
-    return
-  }
-  const el = app.doc.getRef(ref)
-  if (el) {
-    const result = app.doc.fireEvent(el, type, e, domChanges)
-    app.differ.flush()
-    app.doc.taskCenter.send('dom', { action: 'updateFinish' }, [])
-    return result
-  }
-  return new Error(`invalid element reference "${ref}"`)
-}
-
-/**
- * Make a callback for a certain app.
- * @param  {object}   app
- * @param  {number}   callbackId
- * @param  {any}      data
- * @param  {boolean}  ifKeepAlive
- */
-export function callback (app, callbackId, data, ifKeepAlive) {
-  console.debug(`[JS Framework] Invoke a callback(${callbackId}) with`, data,
-            `in instance(${app.id})`)
-  const result = app.doc.taskCenter.callback(callbackId, data, ifKeepAlive)
-  updateActions(app)
-  app.doc.taskCenter.send('dom', { action: 'updateFinish' }, [])
-  return result
-}
-
-/**
- * Collect all virtual-DOM mutations together and send them to renderer.
- * @param  {object} app
- */
-export function updateActions (app) {
-  app.differ.flush()
-}
-
-/**
- * Call all tasks from an app to renderer (native).
- * @param  {object} app
- * @param  {array}  tasks
- */
-export function callTasks (app, tasks) {
-  let result
-
-  /* istanbul ignore next */
-  if (typof(tasks) !== 'array') {
-    tasks = [tasks]
-  }
-
-  tasks.forEach(task => {
-    result = app.doc.taskCenter.send(
-      'module',
-      {
-        module: task.module,
-        method: task.method
-      },
-      task.args
-    )
-  })
-
-  return result
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/app/differ.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/app/differ.js b/html5/frameworks/legacy/app/differ.js
deleted file mode 100644
index 61e25ef..0000000
--- a/html5/frameworks/legacy/app/differ.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.
- */
-export default class Differ {
-  constructor (id) {
-    this.id = id
-    this.map = []
-    this.hooks = []
-  }
-  isEmpty () {
-    return this.map.length === 0
-  }
-  append (type, depth = 0, ref, handler) {
-    if (!this.hasTimer) {
-      this.hasTimer = true
-      setTimeout(() => {
-        this.hasTimer = false
-        this.flush(true)
-      }, 0)
-    }
-    const map = this.map
-    if (!map[depth]) {
-      map[depth] = {}
-    }
-    const group = map[depth]
-    if (!group[type]) {
-      group[type] = {}
-    }
-    if (type === 'element') {
-      if (!group[type][ref]) {
-        group[type][ref] = []
-      }
-      group[type][ref].push(handler)
-    }
-    else {
-      group[type][ref] = handler
-    }
-  }
-  flush (isTimeout) {
-    const map = this.map.slice()
-    this.map.length = 0
-    map.forEach((group) => {
-      callTypeMap(group, 'repeat')
-      callTypeMap(group, 'shown')
-      callTypeList(group, 'element')
-    })
-
-    const hooks = this.hooks.slice()
-    this.hooks.length = 0
-    hooks.forEach((fn) => {
-      fn()
-    })
-
-    if (!this.isEmpty()) {
-      this.flush()
-    }
-  }
-  then (fn) {
-    this.hooks.push(fn)
-  }
-}
-
-function callTypeMap (group, type) {
-  const map = group[type]
-  for (const ref in map) {
-    map[ref]()
-  }
-}
-
-function callTypeList (group, type) {
-  const map = group[type]
-  for (const ref in map) {
-    const list = map[ref]
-    list.forEach((handler) => { handler() })
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/app/downgrade.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/app/downgrade.js b/html5/frameworks/legacy/app/downgrade.js
deleted file mode 100644
index 327d70a..0000000
--- a/html5/frameworks/legacy/app/downgrade.js
+++ /dev/null
@@ -1,172 +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 semver from 'semver'
-import { isPlainObject, typof } from '../util/index'
-
-/**
- * Normalize a version string.
- * @param  {String} Version. ie: 1, 1.0, 1.0.0
- * @return {String} Version
- */
-export function normalizeVersion (v) {
-  const isValid = semver.valid(v)
-  if (isValid) {
-    return v
-  }
-
-  v = typeof (v) === 'string' ? v : ''
-  const split = v.split('.')
-  let i = 0
-  const result = []
-
-  while (i < 3) {
-    const s = typeof (split[i]) === 'string' && split[i] ? split[i] : '0'
-    result.push(s)
-    i++
-  }
-
-  return result.join('.')
-}
-
-/**
- * Get informations from different error key. Like:
- * - code
- * - errorMessage
- * - errorType
- * - isDowngrade
- * @param  {string} key
- * @param  {string} val
- * @param  {string} criteria
- * @return {object}
- */
-export function getError (key, val, criteria) {
-  const result = {
-    isDowngrade: true,
-    errorType: 1,
-    code: 1000
-  }
-  const getMsg = function (key, val, criteria) {
-    return 'Downgrade[' + key + '] :: deviceInfo '
-      + val + ' matched criteria ' + criteria
-  }
-  const _key = key.toLowerCase()
-
-  result.errorMessage = getMsg(key, val, criteria)
-
-  if (_key.indexOf('osversion') >= 0) {
-    result.code = 1001
-  }
-  else if (_key.indexOf('appversion') >= 0) {
-    result.code = 1002
-  }
-  else if (_key.indexOf('weexversion') >= 0) {
-    result.code = 1003
-  }
-  else if (_key.indexOf('devicemodel') >= 0) {
-    result.code = 1004
-  }
-
-  return result
-}
-
-/**
- * WEEX framework input(deviceInfo)
- * {
- *   platform: 'iOS' or 'android'
- *   osVersion: '1.0.0' or '1.0' or '1'
- *   appVersion: '1.0.0' or '1.0' or '1'
- *   weexVersion: '1.0.0' or '1.0' or '1'
- *   dDeviceModel: 'MODEL_NAME'
- * }
- *
- * downgrade config(config)
- * {
- *   ios: {
- *     osVersion: '>1.0.0' or '>=1.0.0' or '<1.0.0' or '<=1.0.0' or '1.0.0'
- *     appVersion: '>1.0.0' or '>=1.0.0' or '<1.0.0' or '<=1.0.0' or '1.0.0'
- *     weexVersion: '>1.0.0' or '>=1.0.0' or '<1.0.0' or '<=1.0.0' or '1.0.0'
- *     deviceModel: ['modelA', 'modelB', ...]
- *   },
- *   android: {
- *     osVersion: '>1.0.0' or '>=1.0.0' or '<1.0.0' or '<=1.0.0' or '1.0.0'
- *     appVersion: '>1.0.0' or '>=1.0.0' or '<1.0.0' or '<=1.0.0' or '1.0.0'
- *     weexVersion: '>1.0.0' or '>=1.0.0' or '<1.0.0' or '<=1.0.0' or '1.0.0'
- *     deviceModel: ['modelA', 'modelB', ...]
- *   }
- * }
- *
- *
- * @param  {object} deviceInfo Weex SDK framework input
- * @param  {object} config     user input
- * @return {Object}            { isDowngrade: true/false, errorMessage... }
- */
-export function check (config, deviceInfo) {
-  deviceInfo = deviceInfo || global.WXEnvironment
-  deviceInfo = isPlainObject(deviceInfo) ? deviceInfo : {}
-
-  let result = {
-    isDowngrade: false // defautl is pass
-  }
-
-  if (typof(config) === 'function') {
-    let customDowngrade = config.call(this, deviceInfo, {
-      semver: semver,
-      normalizeVersion
-    })
-
-    customDowngrade = !!customDowngrade
-
-    result = customDowngrade ? getError('custom', '', 'custom params') : result
-  }
-  else {
-    config = isPlainObject(config) ? config : {}
-
-    const platform = deviceInfo.platform || 'unknow'
-    const dPlatform = platform.toLowerCase()
-    const cObj = config[dPlatform] || {}
-
-    for (const i in deviceInfo) {
-      const key = i
-      const keyLower = key.toLowerCase()
-      const val = deviceInfo[i]
-      const isVersion = keyLower.indexOf('version') >= 0
-      const isDeviceModel = keyLower.indexOf('devicemodel') >= 0
-      const criteria = cObj[i]
-
-      if (criteria && isVersion) {
-        const c = normalizeVersion(criteria)
-        const d = normalizeVersion(deviceInfo[i])
-
-        if (semver.satisfies(d, c)) {
-          result = getError(key, val, criteria)
-          break
-        }
-      }
-      else if (isDeviceModel) {
-        const _criteria = typof(criteria) === 'array' ? criteria : [criteria]
-        if (_criteria.indexOf(val) >= 0) {
-          result = getError(key, val, criteria)
-          break
-        }
-      }
-    }
-  }
-
-  return result
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/app/index.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/app/index.js b/html5/frameworks/legacy/app/index.js
deleted file mode 100644
index 5ae389d..0000000
--- a/html5/frameworks/legacy/app/index.js
+++ /dev/null
@@ -1,55 +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
- * Weex instance constructor & definition
- */
-
-import { requireModule } from './register'
-import { updateActions, callTasks } from './ctrl/index'
-import App from './instance'
-
-/**
- * @deprecated
- */
-App.prototype.requireModule = function (name) {
-  return requireModule(this, name)
-}
-
-/**
- * @deprecated
- */
-App.prototype.updateActions = function () {
-  return updateActions(this)
-}
-
-/**
- * @deprecated
- */
-App.prototype.callTasks = function (tasks) {
-  return callTasks(this, tasks)
-}
-
-/**
- * Prevent modification of App and App.prototype
- */
-Object.freeze(App)
-Object.freeze(App.prototype)
-
-export default App

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/app/instance.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/app/instance.js b/html5/frameworks/legacy/app/instance.js
deleted file mode 100644
index 55bb4c5..0000000
--- a/html5/frameworks/legacy/app/instance.js
+++ /dev/null
@@ -1,47 +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
- * Weex App constructor & definition
- */
-
-import Differ from './differ'
-import renderer from '../config'
-
-/**
- * App constructor for Weex framework.
- * @param {string} id
- * @param {object} options
- */
-export default function App (id, options) {
-  this.id = id
-  this.options = options || {}
-  this.vm = null
-  this.customComponentMap = {}
-  this.commonModules = {}
-
-  // document
-  this.doc = new renderer.Document(
-    id,
-    this.options.bundleUrl,
-    null,
-    renderer.Listener
-  )
-  this.differ = new Differ(id)
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/app/register.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/app/register.js b/html5/frameworks/legacy/app/register.js
deleted file mode 100644
index bd0302e..0000000
--- a/html5/frameworks/legacy/app/register.js
+++ /dev/null
@@ -1,131 +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.
- */
-let nativeModules = {}
-
-// for testing
-
-/**
- * for testing
- */
-export function getModule (moduleName) {
-  return nativeModules[moduleName]
-}
-
-/**
- * for testing
- */
-export function clearModules () {
-  nativeModules = {}
-}
-
-// for framework
-
-/**
- * init modules for an app instance
- * the second param determines whether to replace an existed method
- */
-export function initModules (modules, ifReplace) {
-  for (const moduleName in modules) {
-    // init `modules[moduleName][]`
-    let methods = nativeModules[moduleName]
-    if (!methods) {
-      methods = {}
-      nativeModules[moduleName] = methods
-    }
-
-    // push each non-existed new method
-    modules[moduleName].forEach(function (method) {
-      if (typeof method === 'string') {
-        method = {
-          name: method
-        }
-      }
-
-      if (!methods[method.name] || ifReplace) {
-        methods[method.name] = method
-      }
-    })
-  }
-}
-
-/**
- * init app methods
- */
-export function initMethods (Vm, apis) {
-  const p = Vm.prototype
-
-  for (const apiName in apis) {
-    if (!p.hasOwnProperty(apiName)) {
-      p[apiName] = apis[apiName]
-    }
-  }
-}
-
-/**
- * get a module of methods for an app instance
- */
-export function requireModule (app, name) {
-  const methods = nativeModules[name]
-  const target = {}
-  for (const methodName in methods) {
-    Object.defineProperty(target, methodName, {
-      configurable: true,
-      enumerable: true,
-      get: function moduleGetter () {
-        return (...args) => app.callTasks({
-          module: name,
-          method: methodName,
-          args: args
-        })
-      },
-      set: function moduleSetter (value) {
-        if (typeof value === 'function') {
-          return app.callTasks({
-            module: name,
-            method: methodName,
-            args: [value]
-          })
-        }
-      }
-    })
-  }
-  return target
-}
-
-/**
- * get a custom component options
- */
-export function requireCustomComponent (app, name) {
-  const { customComponentMap } = app
-  return customComponentMap[name]
-}
-
-/**
- * register a custom component options
- */
-export function registerCustomComponent (app, name, def) {
-  const { customComponentMap } = app
-
-  if (customComponentMap[name]) {
-    console.error(`[JS Framework] define a component(${name}) that already exists`)
-    return
-  }
-
-  customComponentMap[name] = def
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/app/viewport.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/app/viewport.js b/html5/frameworks/legacy/app/viewport.js
deleted file mode 100644
index 60f8eff..0000000
--- a/html5/frameworks/legacy/app/viewport.js
+++ /dev/null
@@ -1,56 +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.
- */
-export function setViewport (app, configs = {}) {
-  /* istanbul ignore if */
-  if (process.env.NODE_ENV === 'development') {
-    console.debug(`[JS Framework] Set viewport (width: ${configs.width}) for app#${app.id}.`)
-    validateViewport(configs)
-  }
-
-  // Send viewport configs to native
-  if (app && app.callTasks) {
-    return app.callTasks([{
-      module: 'meta',
-      method: 'setViewport',
-      args: [configs]
-    }])
-  }
-
-  /* istanbul ignore next */
-  else if (process.env.NODE_ENV === 'development') {
-    console.warn(`[JS Framework] Can't find "callTasks" method on current app.`)
-  }
-}
-
-/**
- * Validate the viewport config.
- * @param {Object} configs
- */
-export function validateViewport (configs = {}) {
-  const { width } = configs
-  if (width) {
-    if (typeof width !== 'number' && width !== 'device-width') {
-      console.warn(`[JS Framework] Not support to use ${width} as viewport width.`)
-      return false
-    }
-    return true
-  }
-  console.warn('[JS Framework] the viewport config should contain the "width" property.')
-  return false
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/config.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/config.js b/html5/frameworks/legacy/config.js
deleted file mode 100644
index ebf64d2..0000000
--- a/html5/frameworks/legacy/config.js
+++ /dev/null
@@ -1,35 +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.
- */
-// @todo: It should be registered by native from `registerComponents()`.
-
-export default {
-  nativeComponentMap: {
-    text: true,
-    image: true,
-    container: true,
-    slider: {
-      type: 'slider',
-      append: 'tree'
-    },
-    cell: {
-      type: 'cell',
-      append: 'tree'
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/core/LICENSE
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/core/LICENSE b/html5/frameworks/legacy/core/LICENSE
deleted file mode 100644
index 4645bc4..0000000
--- a/html5/frameworks/legacy/core/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2013-2015 Yuxi Evan You
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/core/array.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/core/array.js b/html5/frameworks/legacy/core/array.js
deleted file mode 100644
index 856792f..0000000
--- a/html5/frameworks/legacy/core/array.js
+++ /dev/null
@@ -1,100 +0,0 @@
-/* eslint-disable */
-
-
-import { def } from '../util/index'
-
-const arrayProto = Array.prototype
-export const arrayMethods = Object.create(arrayProto)
-
-/**
- * Intercept mutating methods and emit events
- */
-
-;[
-  'push',
-  'pop',
-  'shift',
-  'unshift',
-  'splice',
-  'sort',
-  'reverse'
-]
-.forEach(function (method) {
-  // cache original method
-  const original = arrayProto[method]
-  def(arrayMethods, method, function mutator () {
-    // avoid leaking arguments:
-    // http://jsperf.com/closure-with-arguments
-    let i = arguments.length
-    const args = new Array(i)
-    while (i--) {
-      args[i] = arguments[i]
-    }
-    const result = original.apply(this, args)
-    const ob = this.__ob__
-    let inserted
-    switch (method) {
-      case 'push':
-        inserted = args
-        break
-      case 'unshift':
-        inserted = args
-        break
-      case 'splice':
-        inserted = args.slice(2)
-        break
-    }
-    if (inserted) ob.observeArray(inserted)
-    // notify change
-    ob.dep.notify()
-    return result
-  })
-})
-
-/**
- * Swap the element at the given index with a new value
- * and emits corresponding event.
- *
- * @param {Number} index
- * @param {*} val
- * @return {*} - replaced element
- */
-
-def(
-  arrayProto,
-  '$set',
-  function $set (index, val) {
-    console.warn(`[JS Framework] "Array.prototype.$set" is not a standard API,`
-      + ` it will be removed in the next version.`)
-    if (index >= this.length) {
-      this.length = index + 1
-    }
-    return this.splice(index, 1, val)[0]
-  }
-)
-
-/**
- * Convenience method to remove the element at given index.
- *
- * @param {Number} index
- * @param {*} val
- */
-
-def(
-  arrayProto,
-  '$remove',
-  function $remove (index) {
-    console.warn(`[JS Framework] "Array.prototype.$remove" is not a standard API,`
-      + ` it will be removed in the next version.`)
-    /* istanbul ignore if */
-    if (!this.length) return
-    /* istanbul ignore else */
-    if (typeof index !== 'number') {
-      index = this.indexOf(index)
-    }
-    /* istanbul ignore else */
-    if (index > -1) {
-      this.splice(index, 1)
-    }
-  }
-)

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/core/dep.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/core/dep.js b/html5/frameworks/legacy/core/dep.js
deleted file mode 100644
index 6c29bf6..0000000
--- a/html5/frameworks/legacy/core/dep.js
+++ /dev/null
@@ -1,80 +0,0 @@
-/* eslint-disable */
-
-
-import { remove } from '../util/index'
-
-let uid = 0
-
-/**
- * A dep is an observable that can have multiple
- * directives subscribing to it.
- *
- * @constructor
- */
-
-export default function Dep () {
-  this.id = uid++
-  this.subs = []
-}
-
-// the current target watcher being evaluated.
-// this is globally unique because there could be only one
-// watcher being evaluated at any time.
-Dep.target = null
-let targetStack = []
-
-export function pushTarget (_target) {
-  if (Dep.target) targetStack.push(Dep.target)
-  Dep.target = _target
-}
-
-export function popTarget () {
-  Dep.target = targetStack.pop()
-}
-
-export function resetTarget () {
-  Dep.target = null
-  targetStack = []
-}
-
-/**
- * Add a directive subscriber.
- *
- * @param {Directive} sub
- */
-
-Dep.prototype.addSub = function (sub) {
-  this.subs.push(sub)
-}
-
-/**
- * Remove a directive subscriber.
- *
- * @param {Directive} sub
- */
-
-Dep.prototype.removeSub = function (sub) {
-  remove(this.subs, sub)
-}
-
-/**
- * Add self as a dependency to the target watcher.
- */
-
-Dep.prototype.depend = function () {
-  if (Dep.target) {
-    Dep.target.addDep(this)
-  }
-}
-
-/**
- * Notify all subscribers of a new value.
- */
-
-Dep.prototype.notify = function () {
-  // stablize the subscriber list first
-  const subs = this.subs.slice()
-  for (let i = 0, l = subs.length; i < l; i++) {
-    subs[i].update()
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/core/object.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/core/object.js b/html5/frameworks/legacy/core/object.js
deleted file mode 100644
index 86f83e3..0000000
--- a/html5/frameworks/legacy/core/object.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/* eslint-disable */
-
-
-import * as _ from '../util/index'
-var objProto = Object.prototype
-
-/**
- * Add a new property to an observed object
- * and emits corresponding event
- *
- * @param {String} key
- * @param {*} val
- * @public
- */
-
-_.define(
-  objProto,
-  '$add',
-  function $add (key, val) {
-    console.warn(`[JS Framework] "Object.prototype.$add" is not a standard API,`
-      + ` it will be removed in the next version.`)
-    if (this.hasOwnProperty(key)) return
-    var ob = this.__ob__
-    if (!ob || _.isReserved(key)) {
-      this[key] = val
-      return
-    }
-    ob.convert(key, val)
-    ob.notify()
-    if (ob.vms) {
-      var i = ob.vms.length
-      while (i--) {
-        var vm = ob.vms[i]
-        vm._proxy(key)
-        // vm._digest() // todo
-      }
-    }
-  }
-)
-
-/**
- * Set a property on an observed object, calling add to
- * ensure the property is observed.
- *
- * @param {String} key
- * @param {*} val
- * @public
- */
-
-_.define(
-  objProto,
-  '$set',
-  function $set (key, val) {
-    console.warn(`[JS Framework] "Object.prototype.$set" is not a standard API,`
-      + ` it will be removed in the next version.`)
-    this.$add(key, val)
-    this[key] = val
-  }
-)
-
-/**
- * Deletes a property from an observed object
- * and emits corresponding event
- *
- * @param {String} key
- * @public
- */
-
-_.define(
-  objProto,
-  '$delete',
-  function $delete (key) {
-    console.warn(`[JS Framework] "Object.prototype.$delete" is not a standard API,`
-      + ` it will be removed in the next version.`)
-    if (!this.hasOwnProperty(key)) return
-    delete this[key]
-    var ob = this.__ob__
-    if (!ob || _.isReserved(key)) {
-      return
-    }
-    ob.notify()
-    if (ob.vms) {
-      var i = ob.vms.length
-      while (i--) {
-        var vm = ob.vms[i]
-        vm._unproxy(key)
-        // vm._digest() // todo
-      }
-    }
-  }
-)



[04/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/signals/signals-long.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/signals/signals-long.source.js b/test/js-framework/case/signals/signals-long.source.js
new file mode 100644
index 0000000..06b541f
--- /dev/null
+++ b/test/js-framework/case/signals/signals-long.source.js
@@ -0,0 +1,1223 @@
+/*
+ * 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.
+ */
+new Promise(function () {})
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+;define('systemConfig', function (require, exports, module) {
+    module.exports = {"isAliIntranet":false,"serverTime":"2016-07-04 17:00:00"};
+    module.exports.serverTime = new Date(module.exports.serverTime).getTime();
+    module.exports.clientTime = new Date().getTime();
+});
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+;define('zebraConfig', function (require, exports, module) {
+    module.exports = {"spma":"0","spmb":"0","name":"zebra-act-item-240x240","title":"zebra-act-item-240x240","pageStatus":"online","modules":[{"name":"zebra-act-item-240x240","rmcId":"zebra-act-item-240x240","zebraId":"zebra-act-item-240x240","moduleId":"zebra-act-item-240x240","meta":{"size":"10px"}}],"closeCustom":true};
+});
+
+;define('zebraDoms', function (require, exports, module) {
+    var els = [
+      {"isZebraModule":true,"zebraId":"zebra-act-item-240x240","name":"zebra-act-item-240x240","data":{"$config":{"$dynamic":true,"mobileNoLogo":false},"item":[{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemIm
 g":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png",
 "itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/",
 "itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpX
 XQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":
 567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","
 brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.ali
 cdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.al
 icdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXX
 XJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.
 com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com
 /tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子
 短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短
 裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","bra
 ndLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"item
 MPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicd
 n.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXX
 XGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg"
 :"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","i
 temDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","i
 temTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQ
 U6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":56
 7,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","br
 andLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicd
 n.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alic
 dn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJ
 XFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.co
 m/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/t
 ps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子�
 �裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短�
 �连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brand
 Logo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMP
 rice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.
 com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXG
 XFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"
 http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","ite
 mDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","ite
 mTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6
 CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,
 "itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","bran
 dLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.
 com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn
 .com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXF
 XXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/
 tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps
 /i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短�
 ��连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙�
 ��衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLo
 go":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPri
 ce":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.co
 m/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXF
 XXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"ht
 tp://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemD
 esc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemT
 itle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CG
 XXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"i
 temActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandL
 ogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.co
 m/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.c
 om/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXX
 mqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tp
 s/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i
 1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙
 连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连
 衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo
 ":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice
 ":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/
 tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXX
 G9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http
 ://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDes
 c":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTit
 le":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXX
 X-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"ite
 mActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLog
 o":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/
 tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com
 /tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmq
 jTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/
 i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/
 TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙�
 �衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连�
 �裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":
 "http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":
 12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tp
 s/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9
 gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http:/
 /gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc"
 :"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle
 ":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-
 348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemA
 ctPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo"
 :"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tp
 s/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/t
 ps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.p

<TRUNCATED>


[18/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/render/native/setup.js
----------------------------------------------------------------------
diff --git a/html5/render/native/setup.js b/html5/render/native/setup.js
deleted file mode 100644
index d7404cb..0000000
--- a/html5/render/native/setup.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 { subversion } from '../../../package.json'
-import runtime from '../../runtime'
-import services from '../../services/index'
-
-/**
- * Setup frameworks with runtime.
- * You can package more frameworks by
- *  passing them as arguments.
- */
-export default function (frameworks) {
-  const { init, config } = runtime
-  config.frameworks = frameworks
-  const { native, transformer } = subversion
-
-  for (const serviceName in services) {
-    runtime.service.register(serviceName, services[serviceName])
-  }
-
-  runtime.freezePrototype()
-  runtime.setNativeConsole()
-
-  // register framework meta info
-  global.frameworkVersion = native
-  global.transformerVersion = transformer
-
-  // init frameworks
-  const globalMethods = init(config)
-
-  // set global methods
-  for (const methodName in globalMethods) {
-    global[methodName] = (...args) => {
-      const ret = globalMethods[methodName](...args)
-      if (ret instanceof Error) {
-        console.error(ret.toString())
-      }
-      return ret
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/render/vue/README.md
----------------------------------------------------------------------
diff --git a/html5/render/vue/README.md b/html5/render/vue/README.md
deleted file mode 100644
index a8bf32e..0000000
--- a/html5/render/vue/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# weex-vue-render
-
-This project has been moved to [weexteam group](https://github.com/weexteam/weex-vue-render)

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/api/WeexInstance.js
----------------------------------------------------------------------
diff --git a/html5/runtime/api/WeexInstance.js b/html5/runtime/api/WeexInstance.js
deleted file mode 100644
index 3bc25e6..0000000
--- a/html5/runtime/api/WeexInstance.js
+++ /dev/null
@@ -1,138 +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 from '../vdom/Document'
-import { isRegisteredModule, getModuleDescription } from './module'
-import { isRegisteredComponent } from './component'
-import { getTaskCenter } from '../vdom/operation'
-
-const moduleProxies = {}
-
-function setId (weex, id) {
-  Object.defineProperty(weex, '[[CurrentInstanceId]]', { value: id })
-}
-
-function getId (weex) {
-  return weex['[[CurrentInstanceId]]']
-}
-
-function moduleGetter (id, module, method) {
-  const taskCenter = getTaskCenter(id)
-  if (!taskCenter || typeof taskCenter.send !== 'function') {
-    console.error(`[JS Framework] Failed to find taskCenter (${id}).`)
-    return null
-  }
-  return (...args) => taskCenter.send('module', { module, method }, args)
-}
-
-function moduleSetter (id, module, method, fn) {
-  const taskCenter = getTaskCenter(id)
-  if (!taskCenter || typeof taskCenter.send !== 'function') {
-    console.error(`[JS Framework] Failed to find taskCenter (${id}).`)
-    return null
-  }
-  if (typeof fn !== 'function') {
-    console.error(`[JS Framework] ${module}.${method} must be assigned as a function.`)
-    return null
-  }
-  return fn => taskCenter.send('module', { module, method }, [fn])
-}
-
-export default class WeexInstance {
-  constructor (id, config) {
-    setId(this, String(id))
-    this.config = config || {}
-    this.document = new Document(id, this.config.bundleUrl)
-    this.requireModule = this.requireModule.bind(this)
-    this.isRegisteredModule = isRegisteredModule
-    this.isRegisteredComponent = isRegisteredComponent
-  }
-
-  requireModule (moduleName) {
-    const id = getId(this)
-    if (!(id && this.document && this.document.taskCenter)) {
-      console.error(`[JS Framework] Failed to requireModule("${moduleName}"), `
-        + `instance (${id}) doesn't exist anymore.`)
-      return
-    }
-
-    // warn for unknown module
-    if (!isRegisteredModule(moduleName)) {
-      console.warn(`[JS Framework] using unregistered weex module "${moduleName}"`)
-      return
-    }
-
-    // create new module proxy
-    const proxyName = `${moduleName}#${id}`
-    if (!moduleProxies[proxyName]) {
-      // create registered module apis
-      const moduleDefine = getModuleDescription(moduleName)
-      const moduleApis = {}
-      for (const methodName in moduleDefine) {
-        Object.defineProperty(moduleApis, methodName, {
-          enumerable: true,
-          configurable: true,
-          get: () => moduleGetter(id, moduleName, methodName),
-          set: fn => moduleSetter(id, moduleName, methodName, fn)
-        })
-      }
-
-      // create module Proxy
-      if (typeof Proxy === 'function') {
-        moduleProxies[proxyName] = new Proxy(moduleApis, {
-          get (target, methodName) {
-            if (methodName in target) {
-              return target[methodName]
-            }
-            console.warn(`[JS Framework] using unregistered method "${moduleName}.${methodName}"`)
-            return moduleGetter(id, moduleName, methodName)
-          }
-        })
-      }
-      else {
-        moduleProxies[proxyName] = moduleApis
-      }
-    }
-
-    return moduleProxies[proxyName]
-  }
-
-  supports (condition) {
-    if (typeof condition !== 'string') return null
-
-    const res = condition.match(/^@(\w+)\/(\w+)(\.(\w+))?$/i)
-    if (res) {
-      const type = res[1]
-      const name = res[2]
-      const method = res[4]
-      switch (type) {
-        case 'module': return isRegisteredModule(name, method)
-        case 'component': return isRegisteredComponent(name)
-      }
-    }
-
-    return null
-  }
-
-  // registerStyleSheet (styles) {
-  //   if (this.document) {
-  //     this.document.registerStyleSheet(styles)
-  //   }
-  // }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/api/component.js
----------------------------------------------------------------------
diff --git a/html5/runtime/api/component.js b/html5/runtime/api/component.js
deleted file mode 100644
index 8a56961..0000000
--- a/html5/runtime/api/component.js
+++ /dev/null
@@ -1,51 +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 { registerElement } from '../vdom/WeexElement'
-
-const weexComponents = {}
-
-/**
- * Register native components information.
- * @param {array} newComponents
- */
-export function registerComponents (newComponents) {
-  if (Array.isArray(newComponents)) {
-    newComponents.forEach(component => {
-      if (!component) {
-        return
-      }
-      if (typeof component === 'string') {
-        weexComponents[component] = true
-      }
-      else if (typeof component === 'object' && typeof component.type === 'string') {
-        weexComponents[component.type] = component
-        registerElement(component.type, component.methods)
-      }
-    })
-  }
-}
-
-/**
- * Check whether the component has been registered.
- * @param {String} component name
- */
-export function isRegisteredComponent (name) {
-  return !!weexComponents[name]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/api/config.js
----------------------------------------------------------------------
diff --git a/html5/runtime/api/config.js b/html5/runtime/api/config.js
deleted file mode 100644
index d212f3d..0000000
--- a/html5/runtime/api/config.js
+++ /dev/null
@@ -1,37 +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 '../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/51dd277e/html5/runtime/api/init.js
----------------------------------------------------------------------
diff --git a/html5/runtime/api/init.js b/html5/runtime/api/init.js
deleted file mode 100644
index 187504a..0000000
--- a/html5/runtime/api/init.js
+++ /dev/null
@@ -1,304 +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 '../bridge/TaskCenter'
-import { receiveTasks } from '../bridge/receiver'
-import { registerModules } from './module'
-import { registerComponents } from './component'
-import { services, register, unregister } from './service'
-import { track } from '../bridge/debug'
-import WeexInstance from './WeexInstance'
-import { getDoc } from '../vdom/operation'
-
-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 getBundleType (code) {
-  const result = versionRegExp.exec(code)
-  if (result) {
-    try {
-      const info = JSON.parse(result[1])
-      return info.framework
-    }
-    catch (e) {}
-  }
-
-  // default bundle type
-  return 'Weex'
-}
-
-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 instanceTypeMap = {}
-function getFrameworkType (id) {
-  return instanceTypeMap[id]
-}
-
-function createInstanceContext (id, options = {}, data) {
-  const weex = new WeexInstance(id, options)
-  Object.freeze(weex)
-
-  const bundleType = options.bundleType || 'Vue'
-  instanceTypeMap[id] = bundleType
-  const framework = runtimeConfig.frameworks[bundleType]
-  if (!framework) {
-    return new Error(`[JS Framework] Invalid bundle type "${bundleType}".`)
-  }
-  track(id, 'bundleType', bundleType)
-
-  // prepare js service
-  const services = createServices(id, { weex, bundleType }, runtimeConfig)
-  Object.freeze(services)
-
-  // prepare runtime context
-  const runtimeContext = Object.create(null)
-  Object.assign(runtimeContext, services, {
-    weex,
-    services // Temporary compatible with some legacy APIs in Rax
-  })
-  Object.freeze(runtimeContext)
-
-  // prepare instance context
-  const instanceContext = Object.create(runtimeContext)
-  if (typeof framework.createInstanceContext === 'function') {
-    Object.assign(instanceContext, framework.createInstanceContext(id, runtimeContext, data))
-  }
-  Object.freeze(instanceContext)
-  return instanceContext
-}
-
-/**
- * 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) {
-  if (instanceTypeMap[id]) {
-    return new Error(`The instance id "${id}" has already been used!`)
-  }
-
-  // Init instance info.
-  const bundleType = getBundleType(code)
-  instanceTypeMap[id] = bundleType
-
-  // Init instance config.
-  config = JSON.parse(JSON.stringify(config || {}))
-  config.env = JSON.parse(JSON.stringify(global.WXEnvironment || {}))
-  config.bundleType = bundleType
-
-  const framework = runtimeConfig.frameworks[bundleType]
-  if (!framework) {
-    return new Error(`[JS Framework] Invalid bundle type "${bundleType}".`)
-  }
-  if (bundleType === 'Weex') {
-    console.error(`[JS Framework] COMPATIBILITY WARNING: `
-      + `Weex DSL 1.0 (.we) framework is no longer supported! `
-      + `It will be removed in the next version of WeexSDK, `
-      + `your page would be crash if you still using the ".we" framework. `
-      + `Please upgrade it to Vue.js or Rax.`)
-  }
-
-  const instanceContext = createInstanceContext(id, config, data)
-  if (typeof framework.createInstance === 'function') {
-    // Temporary compatible with some legacy APIs in Rax,
-    // some Rax page is using the legacy ".we" framework.
-    if (bundleType === 'Rax' || bundleType === 'Weex') {
-      const raxInstanceContext = Object.assign({
-        config,
-        created: Date.now(),
-        framework: bundleType
-      }, instanceContext)
-      return framework.createInstance(id, code, config, data, raxInstanceContext)
-    }
-    return framework.createInstance(id, code, config, data, instanceContext)
-  }
-  // console.error(`[JS Framework] Can't find available "createInstance" method in ${bundleType}!`)
-  runInContext(code, instanceContext)
-}
-
-/**
- * Run js code in a specific context.
- * @param {string} code
- * @param {object} context
- */
-function runInContext (code, context) {
-  const keys = []
-  const args = []
-  for (const key in context) {
-    keys.push(key)
-    args.push(context[key])
-  }
-
-  const bundle = `
-    (function (global) {
-      ${code}
-    })(Object.create(this))
-  `
-
-  return (new Function(...keys, bundle))(...args)
-}
-
-/**
- * Get the JSON object of the root element.
- * @param {string} instanceId
- */
-function getRoot (instanceId) {
-  const document = getDoc(instanceId)
-  try {
-    if (document && document.body) {
-      return document.body.toJSON()
-    }
-  }
-  catch (e) {
-    console.error(`[JS Framework] Failed to get the virtual dom tree.`)
-    return
-  }
-}
-
-const methods = {
-  createInstance,
-  createInstanceContext,
-  getRoot,
-  getDocument: getDoc,
-  registerService: register,
-  unregisterService: unregister,
-  callJS (id, tasks) {
-    const framework = frameworks[getFrameworkType(id)]
-    if (framework && typeof framework.receiveTasks === 'function') {
-      return framework.receiveTasks(id, tasks)
-    }
-    return receiveTasks(id, tasks)
-  }
-}
-
-/**
- * Register methods which will be called for each instance.
- * @param {string} methodName
- */
-function genInstance (methodName) {
-  methods[methodName] = function (...args) {
-    const id = args[0]
-    const type = getFrameworkType(id)
-    if (type && frameworks[type]) {
-      const result = frameworks[type][methodName](...args)
-      const info = { framework: type }
-
-      // 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 instanceTypeMap[id]
-      }
-
-      return result
-    }
-    return new Error(`[JS Framework] Using invalid instance id `
-      + `"${id}" when calling ${methodName}.`)
-  }
-}
-
-/**
- * Register methods which init each frameworks.
- * @param {string} methodName
- * @param {function} sharedMethod
- */
-function adaptMethod (methodName, sharedMethod) {
-  methods[methodName] = function (...args) {
-    if (typeof sharedMethod === 'function') {
-      sharedMethod(...args)
-    }
-
-    // TODO: deprecated
-    for (const name in runtimeConfig.frameworks) {
-      const framework = runtimeConfig.frameworks[name]
-      if (framework && framework[methodName]) {
-        framework[methodName](...args)
-      }
-    }
-  }
-}
-
-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]
-    if (typeof framework.init === 'function') {
-      try {
-        framework.init(config)
-      }
-      catch (e) {}
-    }
-  }
-
-  adaptMethod('registerComponents', registerComponents)
-  adaptMethod('registerModules', registerModules)
-  adaptMethod('registerMethods')
-
-  ; ['destroyInstance', 'refreshInstance'].forEach(genInstance)
-
-  return methods
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/api/module.js
----------------------------------------------------------------------
diff --git a/html5/runtime/api/module.js b/html5/runtime/api/module.js
deleted file mode 100644
index df26b92..0000000
--- a/html5/runtime/api/module.js
+++ /dev/null
@@ -1,56 +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.
- */
-
-const weexModules = {}
-
-/**
- * Register native modules information.
- * @param {object} newModules
- */
-export function registerModules (newModules) {
-  for (const name in newModules) {
-    if (!weexModules[name]) {
-      weexModules[name] = {}
-    }
-    newModules[name].forEach(method => {
-      if (typeof method === 'string') {
-        weexModules[name][method] = true
-      }
-      else {
-        weexModules[name][method.name] = method.args
-      }
-    })
-  }
-}
-
-/**
- * Check whether the module or the method has been registered.
- * @param {String} module name
- * @param {String} method name (optional)
- */
-export function isRegisteredModule (name, method) {
-  if (typeof method === 'string') {
-    return !!(weexModules[name] && weexModules[name][method])
-  }
-  return !!weexModules[name]
-}
-
-export function getModuleDescription (name) {
-  return weexModules[name]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/api/service.js
----------------------------------------------------------------------
diff --git a/html5/runtime/api/service.js b/html5/runtime/api/service.js
deleted file mode 100644
index 6f2e72e..0000000
--- a/html5/runtime/api/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/51dd277e/html5/runtime/bridge/CallbackManager.js
----------------------------------------------------------------------
diff --git a/html5/runtime/bridge/CallbackManager.js b/html5/runtime/bridge/CallbackManager.js
deleted file mode 100644
index 70bd729..0000000
--- a/html5/runtime/bridge/CallbackManager.js
+++ /dev/null
@@ -1,89 +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'
-
-function getHookKey (componentId, type, hookName) {
-  return `${type}@${hookName}#${componentId}`
-}
-
-/**
- * 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 = String(instanceId)
-    this.lastCallbackId = 0
-    this.callbacks = {}
-    this.hooks = {}
-  }
-  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
-  }
-  registerHook (componentId, type, hookName, hookFunction) {
-    // TODO: validate arguments
-    const key = getHookKey(componentId, type, hookName)
-    if (this.hooks[key]) {
-      console.warn(`[JS Framework] Override an existing component hook "${key}".`)
-    }
-    this.hooks[key] = hookFunction
-  }
-  triggerHook (componentId, type, hookName, options = {}) {
-    // TODO: validate arguments
-    const key = getHookKey(componentId, type, hookName)
-    const hookFunction = this.hooks[key]
-    if (typeof hookFunction !== 'function') {
-      console.error(`[JS Framework] Invalid hook function type (${typeof hookFunction}) on "${key}".`)
-      return null
-    }
-    let result = null
-    try {
-      result = hookFunction.apply(null, options.args || [])
-    }
-    catch (e) {
-      console.error(`[JS Framework] Failed to execute the hook function on "${key}".`)
-    }
-    return result
-  }
-  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 = {}
-    this.hooks = {}
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/bridge/Handler.js
----------------------------------------------------------------------
diff --git a/html5/runtime/bridge/Handler.js b/html5/runtime/bridge/Handler.js
deleted file mode 100644
index 71bf4c5..0000000
--- a/html5/runtime/bridge/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/51dd277e/html5/runtime/bridge/Listener.js
----------------------------------------------------------------------
diff --git a/html5/runtime/bridge/Listener.js b/html5/runtime/bridge/Listener.js
deleted file mode 100644
index 2bf3893..0000000
--- a/html5/runtime/bridge/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/51dd277e/html5/runtime/bridge/TaskCenter.js
----------------------------------------------------------------------
diff --git a/html5/runtime/bridge/TaskCenter.js b/html5/runtime/bridge/TaskCenter.js
deleted file mode 100644
index defee6f..0000000
--- a/html5/runtime/bridge/TaskCenter.js
+++ /dev/null
@@ -1,145 +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 './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: String(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)
-  }
-
-  registerHook (...args) {
-    return this.callbackManager.registerHook(...args)
-  }
-
-  triggerHook (...args) {
-    return this.callbackManager.triggerHook(...args)
-  }
-
-  updateData (componentId, newData, callback) {
-    this.send('module', {
-      module: 'dom',
-      method: 'updateComponentData'
-    }, [componentId, newData, callback])
-  }
-
-  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 && v instanceof Element) {
-      return v.ref
-    }
-    if (v && 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/51dd277e/html5/runtime/bridge/debug.js
----------------------------------------------------------------------
diff --git a/html5/runtime/bridge/debug.js b/html5/runtime/bridge/debug.js
deleted file mode 100644
index 6e24c6a..0000000
--- a/html5/runtime/bridge/debug.js
+++ /dev/null
@@ -1,65 +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 { getTaskCenter } from '../vdom/operation'
-import { isRegisteredModule } from '../api/module'
-
-export function track (id, type, value) {
-  const taskCenter = getTaskCenter(id)
-  if (!taskCenter || typeof taskCenter.send !== 'function') {
-    console.error(`[JS Framework] Failed to create tracker!`)
-    return
-  }
-  if (!type || !value) {
-    console.warn(`[JS Framework] Invalid track type (${type}) or value (${value})`)
-    return
-  }
-  const label = `jsfm.${type}.${value}`
-  try {
-    if (isRegisteredModule('userTrack', 'addPerfPoint')) {
-      const message = Object.create(null)
-      message[label] = '4'
-      taskCenter.send('module', {
-        module: 'userTrack',
-        method: 'addPerfPoint'
-      }, [message])
-    }
-  }
-  catch (err) {
-    console.error(`[JS Framework] Failed to trace "${label}"!`)
-  }
-}
-
-export function error (...messages) {
-  if (typeof console.error === 'function') {
-    console.error(`[JS Framework] `, ...messages)
-  }
-}
-
-export function handleException (err) {
-  if (typeof console.error === 'function') {
-    try {
-      console.error(err.toString())
-    }
-    catch (e) {}
-  }
-  if (process.env.NODE_ENV !== 'production') {
-    throw err
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/bridge/normalize.js
----------------------------------------------------------------------
diff --git a/html5/runtime/bridge/normalize.js b/html5/runtime/bridge/normalize.js
deleted file mode 100644
index d7f2450..0000000
--- a/html5/runtime/bridge/normalize.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.
- */
-
-import { typof, bufferToBase64, base64ToBuffer } from '../utils'
-
-/**
- * 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/51dd277e/html5/runtime/bridge/receiver.js
----------------------------------------------------------------------
diff --git a/html5/runtime/bridge/receiver.js b/html5/runtime/bridge/receiver.js
deleted file mode 100644
index e6dea06..0000000
--- a/html5/runtime/bridge/receiver.js
+++ /dev/null
@@ -1,71 +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 { getDoc } from '../vdom/operation'
-
-function fireEvent (document, nodeId, type, event, domChanges, params) {
-  const el = document.getRef(nodeId)
-  if (el) {
-    return document.fireEvent(el, type, event, domChanges, params)
-  }
-  return new Error(`invalid element reference "${nodeId}"`)
-}
-
-function callback (document, callbackId, data, ifKeepAlive) {
-  return document.taskCenter.callback(callbackId, data, ifKeepAlive)
-}
-
-function componentHook (document, componentId, type, hook, options) {
-  if (!document || !document.taskCenter) {
-    console.error(`[JS Framework] Can't find "document" or "taskCenter".`)
-    return null
-  }
-  let result = null
-  try {
-    result = document.taskCenter.triggerHook(componentId, type, hook, options)
-  }
-  catch (e) {
-    console.error(`[JS Framework] Failed to trigger the "${type}@${hook}" hook on ${componentId}.`)
-  }
-  return result
-}
-
-/**
- * Accept calls from native (event or callback).
- *
- * @param  {string} id
- * @param  {array} tasks list with `method` and `args`
- */
-export function receiveTasks (id, tasks) {
-  const document = getDoc(id)
-  if (!document) {
-    return new Error(`[JS Framework] Failed to receiveTasks, `
-      + `instance (${id}) is not available.`)
-  }
-  if (Array.isArray(tasks)) {
-    return tasks.map(task => {
-      switch (task.method) {
-        case 'callback': return callback(document, ...task.args)
-        case 'fireEventSync':
-        case 'fireEvent': return fireEvent(document, ...task.args)
-        case 'componentHook': return componentHook(document, ...task.args)
-      }
-    })
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/index.js
----------------------------------------------------------------------
diff --git a/html5/runtime/index.js b/html5/runtime/index.js
deleted file mode 100644
index 90c425e..0000000
--- a/html5/runtime/index.js
+++ /dev/null
@@ -1,55 +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
- * Register framework(s) in JS runtime. Weex supply two layers for 3rd-party
- * framework(s): one is the instance management layer, another is the
- * virtual-DOM layer.
- */
-
-import * as shared from '../shared'
-
-import init from './api/init'
-import config from './api/config'
-import { register, unregister, has } from './api/service'
-
-/* istanbul ignore next */
-function freezePrototype () {
-  shared.freezePrototype()
-
-  // Object.freeze(config.Element)
-  Object.freeze(config.Comment)
-  Object.freeze(config.Listener)
-  Object.freeze(config.Document.prototype)
-  // Object.freeze(config.Element.prototype)
-  Object.freeze(config.Comment.prototype)
-  Object.freeze(config.Listener.prototype)
-}
-
-export default {
-  setNativeConsole: shared.setNativeConsole,
-  resetNativeConsole: shared.resetNativeConsole,
-  setNativeTimer: shared.setNativeTimer,
-  resetNativeTimer: shared.resetNativeTimer,
-  service: { register, unregister, has },
-  freezePrototype,
-  init,
-  config
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/utils.js
----------------------------------------------------------------------
diff --git a/html5/runtime/utils.js b/html5/runtime/utils.js
deleted file mode 100644
index c2ab10f..0000000
--- a/html5/runtime/utils.js
+++ /dev/null
@@ -1,71 +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.
- */
-
-/**
- * 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)
-}
-
-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
-}
-
-/**
- * Detect if the param is falsy or empty
- * @param {any} any
- */
-export function isEmpty (any) {
-  if (!any || typeof any !== 'object') {
-    return true
-  }
-
-  for (const key in any) {
-    if (Object.prototype.hasOwnProperty.call(any, key)) {
-      return false
-    }
-  }
-  return true
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/vdom/Comment.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/Comment.js b/html5/runtime/vdom/Comment.js
deleted file mode 100644
index d7ab115..0000000
--- a/html5/runtime/vdom/Comment.js
+++ /dev/null
@@ -1,43 +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 Node from './Node'
-import { uniqueId } from '../utils'
-
-export default class Comment extends Node {
-  constructor (value) {
-    super()
-
-    this.nodeType = 8
-    this.nodeId = uniqueId()
-    this.ref = this.nodeId
-    this.type = 'comment'
-    this.value = value
-    this.children = []
-    this.pureChildren = []
-  }
-
-  /**
-  * Convert to HTML comment string.
-  * @return {stirng} html
-  */
-  toString () {
-    return '<!-- ' + this.value + ' -->'
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/vdom/Document.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/Document.js b/html5/runtime/vdom/Document.js
deleted file mode 100644
index c64fba5..0000000
--- a/html5/runtime/vdom/Document.js
+++ /dev/null
@@ -1,189 +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 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'
-
-/**
- * Update all changes for an element.
- * @param {object} element
- * @param {object} changes
- */
-function updateElement (el, changes) {
-  const attrs = changes.attrs || {}
-  for (const name in attrs) {
-    el.setAttr(name, attrs[name], true)
-  }
-  const style = changes.style || {}
-  for (const name in style) {
-    el.setStyle(name, style[name], true)
-  }
-}
-
-export default class Document {
-  constructor (id, url, handler) {
-    id = id ? id.toString() : ''
-    this.id = id
-    this.URL = url
-
-    addDoc(id, this)
-    this.nodeMap = {}
-    const L = Document.Listener || Listener
-    this.listener = new L(id, handler || createHandler(id, Document.handler)) // deprecated
-    this.taskCenter = new TaskCenter(id, handler ? (id, ...args) => handler(...args) : Document.handler)
-    this.createDocumentElement()
-  }
-
-  /**
-  * Get the node from nodeMap.
-  * @param {string} reference id
-  * @return {object} node
-  */
-  getRef (ref) {
-    return this.nodeMap[ref]
-  }
-
-  /**
-  * Turn on batched updates.
-  */
-  open () {
-    this.listener.batched = false
-  }
-
-  /**
-  * Turn off batched updates.
-  */
-  close () {
-    this.listener.batched = true
-  }
-
-  /**
-  * Create the document element.
-  * @return {object} documentElement
-  */
-  createDocumentElement () {
-    if (!this.documentElement) {
-      const el = new Element('document')
-      el.docId = this.id
-      el.ownerDocument = this
-      el.role = 'documentElement'
-      el.depth = 0
-      el.ref = '_documentElement'
-      this.nodeMap._documentElement = el
-      this.documentElement = el
-
-      Object.defineProperty(el, 'appendChild', {
-        configurable: true,
-        enumerable: true,
-        writable: true,
-        value: (node) => {
-          appendBody(this, node)
-        }
-      })
-
-      Object.defineProperty(el, 'insertBefore', {
-        configurable: true,
-        enumerable: true,
-        writable: true,
-        value: (node, before) => {
-          appendBody(this, node, before)
-        }
-      })
-    }
-
-    return this.documentElement
-  }
-
-  /**
-  * Create the body element.
-  * @param {string} type
-  * @param {objct} props
-  * @return {object} body element
-  */
-  createBody (type, props) {
-    if (!this.body) {
-      const el = new Element(type, props)
-      setBody(this, el)
-    }
-
-    return this.body
-  }
-
-  /**
-  * Create an element.
-  * @param {string} tagName
-  * @param {objct} props
-  * @return {object} element
-  */
-  createElement (tagName, props) {
-    return new Element(tagName, props)
-  }
-
-  /**
-  * Create an comment.
-  * @param {string} text
-  * @return {object} comment
-  */
-  createComment (text) {
-    return new Comment(text)
-  }
-
-  /**
-  * Fire an event on specified element manually.
-  * @param {object} element
-  * @param {string} event type
-  * @param {object} event object
-  * @param {object} dom changes
-  * @param {object} options
-  * @return {} anything returned by handler function
-  */
-  fireEvent (el, type, event, domChanges, options) {
-    if (!el) {
-      return
-    }
-    event = event || {}
-    event.type = event.type || type
-    event.target = el
-    event.currentTarget = el
-    event.timestamp = Date.now()
-    if (domChanges) {
-      updateElement(el, domChanges)
-    }
-    const isBubble = this.getRef('_root').attr['bubble'] === 'true'
-    return el.fireEvent(type, event, isBubble, options)
-  }
-
-  /**
-  * Destroy current document, and remove itself form docMap.
-  */
-  destroy () {
-    this.taskCenter.destroyCallback()
-    delete this.listener
-    delete this.nodeMap
-    delete this.taskCenter
-    removeDoc(this.id)
-  }
-}
-
-// default task handler
-Document.handler = null

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/vdom/Element.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/Element.js b/html5/runtime/vdom/Element.js
deleted file mode 100644
index 5ba05ba..0000000
--- a/html5/runtime/vdom/Element.js
+++ /dev/null
@@ -1,524 +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 Node from './Node'
-import {
-  getDoc,
-  getTaskCenter,
-  linkParent,
-  nextElement,
-  previousElement,
-  insertIndex,
-  moveIndex,
-  removeIndex
-} from './operation'
-import { uniqueId, isEmpty } from '../utils'
-import { getWeexElement, setElement } from './WeexElement'
-
-const DEFAULT_TAG_NAME = 'div'
-const BUBBLE_EVENTS = [
-  'click', 'longpress', 'touchstart', 'touchmove', 'touchend',
-  'panstart', 'panmove', 'panend', 'horizontalpan', 'verticalpan', 'swipe'
-]
-
-function registerNode (docId, node) {
-  const doc = getDoc(docId)
-  doc.nodeMap[node.nodeId] = node
-}
-
-export default class Element extends Node {
-  constructor (type = DEFAULT_TAG_NAME, props, isExtended) {
-    super()
-
-    const WeexElement = getWeexElement(type)
-    if (WeexElement && !isExtended) {
-      return new WeexElement(type, props, true)
-    }
-
-    props = props || {}
-    this.nodeType = 1
-    this.nodeId = uniqueId()
-    this.ref = this.nodeId
-    this.type = type
-    this.attr = props.attr || {}
-    this.style = props.style || {}
-    this.classStyle = props.classStyle || {}
-    this.event = {}
-    this.children = []
-    this.pureChildren = []
-  }
-
-  /**
-   * Append a child node.
-   * @param {object} node
-   * @return {undefined | number} the signal sent by native
-   */
-  appendChild (node) {
-    if (node.parentNode && node.parentNode !== this) {
-      return
-    }
-    /* istanbul ignore else */
-    if (!node.parentNode) {
-      linkParent(node, this)
-      insertIndex(node, this.children, this.children.length, true)
-      if (this.docId) {
-        registerNode(this.docId, node)
-      }
-      if (node.nodeType === 1) {
-        insertIndex(node, this.pureChildren, this.pureChildren.length)
-        const taskCenter = getTaskCenter(this.docId)
-        if (taskCenter) {
-          return taskCenter.send(
-            'dom',
-            { action: 'addElement' },
-            [this.ref, node.toJSON(), -1]
-          )
-        }
-      }
-    }
-    else {
-      moveIndex(node, this.children, this.children.length, true)
-      if (node.nodeType === 1) {
-        const index = moveIndex(node, this.pureChildren, this.pureChildren.length)
-        const taskCenter = getTaskCenter(this.docId)
-        if (taskCenter && index >= 0) {
-          return taskCenter.send(
-            'dom',
-            { action: 'moveElement' },
-            [node.ref, this.ref, index]
-          )
-        }
-      }
-    }
-  }
-
-  /**
-   * Insert a node before specified node.
-   * @param {object} node
-   * @param {object} before
-   * @return {undefined | number} the signal sent by native
-   */
-  insertBefore (node, before) {
-    if (node.parentNode && node.parentNode !== this) {
-      return
-    }
-    if (node === before || (node.nextSibling && node.nextSibling === before)) {
-      return
-    }
-    if (!node.parentNode) {
-      linkParent(node, this)
-      insertIndex(node, this.children, this.children.indexOf(before), true)
-      if (this.docId) {
-        registerNode(this.docId, node)
-      }
-      if (node.nodeType === 1) {
-        const pureBefore = nextElement(before)
-        const index = insertIndex(
-          node,
-          this.pureChildren,
-          pureBefore
-          ? this.pureChildren.indexOf(pureBefore)
-          : this.pureChildren.length
-        )
-        const taskCenter = getTaskCenter(this.docId)
-        if (taskCenter) {
-          return taskCenter.send(
-            'dom',
-            { action: 'addElement' },
-            [this.ref, node.toJSON(), index]
-          )
-        }
-      }
-    }
-    else {
-      moveIndex(node, this.children, this.children.indexOf(before), true)
-      if (node.nodeType === 1) {
-        const pureBefore = nextElement(before)
-        /* istanbul ignore next */
-        const index = moveIndex(
-          node,
-          this.pureChildren,
-          pureBefore
-          ? this.pureChildren.indexOf(pureBefore)
-          : this.pureChildren.length
-        )
-        const taskCenter = getTaskCenter(this.docId)
-        if (taskCenter && index >= 0) {
-          return taskCenter.send(
-            'dom',
-            { action: 'moveElement' },
-            [node.ref, this.ref, index]
-          )
-        }
-      }
-    }
-  }
-
-  /**
-   * Insert a node after specified node.
-   * @param {object} node
-   * @param {object} after
-   * @return {undefined | number} the signal sent by native
-   */
-  insertAfter (node, after) {
-    if (node.parentNode && node.parentNode !== this) {
-      return
-    }
-    if (node === after || (node.previousSibling && node.previousSibling === after)) {
-      return
-    }
-    if (!node.parentNode) {
-      linkParent(node, this)
-      insertIndex(node, this.children, this.children.indexOf(after) + 1, true)
-      /* istanbul ignore else */
-      if (this.docId) {
-        registerNode(this.docId, node)
-      }
-      if (node.nodeType === 1) {
-        const index = insertIndex(
-          node,
-          this.pureChildren,
-          this.pureChildren.indexOf(previousElement(after)) + 1
-        )
-        const taskCenter = getTaskCenter(this.docId)
-        /* istanbul ignore else */
-        if (taskCenter) {
-          return taskCenter.send(
-            'dom',
-            { action: 'addElement' },
-            [this.ref, node.toJSON(), index]
-          )
-        }
-      }
-    }
-    else {
-      moveIndex(node, this.children, this.children.indexOf(after) + 1, true)
-      if (node.nodeType === 1) {
-        const index = moveIndex(
-          node,
-          this.pureChildren,
-          this.pureChildren.indexOf(previousElement(after)) + 1
-        )
-        const taskCenter = getTaskCenter(this.docId)
-        if (taskCenter && index >= 0) {
-          return taskCenter.send(
-            'dom',
-            { action: 'moveElement' },
-            [node.ref, this.ref, index]
-          )
-        }
-      }
-    }
-  }
-
-  /**
-   * Remove a child node, and decide whether it should be destroyed.
-   * @param {object} node
-   * @param {boolean} preserved
-   */
-  removeChild (node, preserved) {
-    if (node.parentNode) {
-      removeIndex(node, this.children, true)
-      if (node.nodeType === 1) {
-        removeIndex(node, this.pureChildren)
-        const taskCenter = getTaskCenter(this.docId)
-        if (taskCenter) {
-          taskCenter.send(
-            'dom',
-            { action: 'removeElement' },
-            [node.ref]
-          )
-        }
-      }
-    }
-    if (!preserved) {
-      node.destroy()
-    }
-  }
-
-  /**
-   * Clear all child nodes.
-   */
-  clear () {
-    const taskCenter = getTaskCenter(this.docId)
-    /* istanbul ignore else */
-    if (taskCenter) {
-      this.pureChildren.forEach(node => {
-        taskCenter.send(
-          'dom',
-          { action: 'removeElement' },
-          [node.ref]
-        )
-      })
-    }
-    this.children.forEach(node => {
-      node.destroy()
-    })
-    this.children.length = 0
-    this.pureChildren.length = 0
-  }
-
-  /**
-   * Set an attribute, and decide whether the task should be send to native.
-   * @param {string} key
-   * @param {string | number} value
-   * @param {boolean} silent
-   */
-  setAttr (key, value, silent) {
-    if (this.attr[key] === value && silent !== false) {
-      return
-    }
-    this.attr[key] = value
-    const taskCenter = getTaskCenter(this.docId)
-    if (!silent && taskCenter) {
-      const result = {}
-      result[key] = value
-      taskCenter.send(
-        'dom',
-        { action: 'updateAttrs' },
-        [this.ref, result]
-      )
-    }
-  }
-
-  /**
-   * Set batched attributes.
-   * @param {object} batchedAttrs
-   * @param {boolean} silent
-   */
-  setAttrs (batchedAttrs, silent) {
-    if (isEmpty(batchedAttrs)) return
-    const mutations = {}
-    for (const key in batchedAttrs) {
-      if (this.attr[key] !== batchedAttrs[key]) {
-        this.attr[key] = batchedAttrs[key]
-        mutations[key] = batchedAttrs[key]
-      }
-    }
-    if (!isEmpty(mutations)) {
-      const taskCenter = getTaskCenter(this.docId)
-      if (!silent && taskCenter) {
-        taskCenter.send(
-          'dom',
-          { action: 'updateAttrs' },
-          [this.ref, mutations]
-        )
-      }
-    }
-  }
-
-  /**
-   * Set a style property, and decide whether the task should be send to native.
-   * @param {string} key
-   * @param {string | number} value
-   * @param {boolean} silent
-   */
-  setStyle (key, value, silent) {
-    if (this.style[key] === value && silent !== false) {
-      return
-    }
-    this.style[key] = value
-    const taskCenter = getTaskCenter(this.docId)
-    if (!silent && taskCenter) {
-      const result = {}
-      result[key] = value
-      taskCenter.send(
-        'dom',
-        { action: 'updateStyle' },
-        [this.ref, result]
-      )
-    }
-  }
-
-  /**
-   * Set batched style properties.
-   * @param {object} batchedStyles
-   * @param {boolean} silent
-   */
-  setStyles (batchedStyles, silent) {
-    if (isEmpty(batchedStyles)) return
-    const mutations = {}
-    for (const key in batchedStyles) {
-      if (this.style[key] !== batchedStyles[key]) {
-        this.style[key] = batchedStyles[key]
-        mutations[key] = batchedStyles[key]
-      }
-    }
-    if (!isEmpty(mutations)) {
-      const taskCenter = getTaskCenter(this.docId)
-      if (!silent && taskCenter) {
-        taskCenter.send(
-          'dom',
-          { action: 'updateStyle' },
-          [this.ref, mutations]
-        )
-      }
-    }
-  }
-
-  /**
-   * Set style properties from class.
-   * @param {object} classStyle
-   */
-  setClassStyle (classStyle) {
-    // reset previous class style to empty string
-    for (const key in this.classStyle) {
-      this.classStyle[key] = ''
-    }
-
-    Object.assign(this.classStyle, classStyle)
-    const taskCenter = getTaskCenter(this.docId)
-    if (taskCenter) {
-      taskCenter.send(
-        'dom',
-        { action: 'updateStyle' },
-        [this.ref, this.toStyle()]
-      )
-    }
-  }
-
-  /**
-   * Add an event handler.
-   * @param {string} event type
-   * @param {function} event handler
-   */
-  addEvent (type, handler, params) {
-    if (!this.event) {
-      this.event = {}
-    }
-    if (!this.event[type]) {
-      this.event[type] = { handler, params }
-      const taskCenter = getTaskCenter(this.docId)
-      if (taskCenter) {
-        taskCenter.send(
-          'dom',
-          { action: 'addEvent' },
-          [this.ref, type]
-        )
-      }
-    }
-  }
-
-  /**
-   * Remove an event handler.
-   * @param {string} event type
-   */
-  removeEvent (type) {
-    if (this.event && this.event[type]) {
-      delete this.event[type]
-      const taskCenter = getTaskCenter(this.docId)
-      if (taskCenter) {
-        taskCenter.send(
-          'dom',
-          { action: 'removeEvent' },
-          [this.ref, type]
-        )
-      }
-    }
-  }
-
-  /**
-   * Fire an event manually.
-   * @param {string} type type
-   * @param {function} event handler
-   * @param {boolean} isBubble whether or not event bubble
-   * @param {boolean} options
-   * @return {} anything returned by handler function
-   */
-  fireEvent (type, event, isBubble, options) {
-    let result = null
-    let isStopPropagation = false
-    const eventDesc = this.event[type]
-    if (eventDesc && event) {
-      const handler = eventDesc.handler
-      event.stopPropagation = () => {
-        isStopPropagation = true
-      }
-      if (options && options.params) {
-        result = handler.call(this, ...options.params, event)
-      }
-      else {
-        result = handler.call(this, event)
-      }
-    }
-
-    if (!isStopPropagation
-      && isBubble
-      && (BUBBLE_EVENTS.indexOf(type) !== -1)
-      && this.parentNode
-      && this.parentNode.fireEvent) {
-      event.currentTarget = this.parentNode
-      this.parentNode.fireEvent(type, event, isBubble) // no options
-    }
-
-    return result
-  }
-
-  /**
-   * Get all styles of current element.
-   * @return {object} style
-   */
-  toStyle () {
-    return Object.assign({}, this.classStyle, this.style)
-  }
-
-  /**
-   * Convert current element to JSON like object.
-   * @return {object} element
-   */
-  toJSON () {
-    const result = {
-      ref: this.ref.toString(),
-      type: this.type,
-      attr: this.attr,
-      style: this.toStyle()
-    }
-    const event = []
-    for (const type in this.event) {
-      const { params } = this.event[type]
-      if (!params) {
-        event.push(type)
-      }
-      else {
-        event.push({ type, params })
-      }
-    }
-    if (event.length) {
-      result.event = event
-    }
-    if (this.pureChildren.length) {
-      result.children = this.pureChildren.map((child) => child.toJSON())
-    }
-    return result
-  }
-
-  /**
-   * Convert to HTML element tag string.
-   * @return {stirng} html
-   */
-  toString () {
-    return '<' + this.type +
-    ' attr=' + JSON.stringify(this.attr) +
-    ' style=' + JSON.stringify(this.toStyle()) + '>' +
-    this.pureChildren.map((child) => child.toString()).join('') +
-    '</' + this.type + '>'
-  }
-}
-
-setElement(Element)

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/vdom/Node.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/Node.js b/html5/runtime/vdom/Node.js
deleted file mode 100644
index 645f7df..0000000
--- a/html5/runtime/vdom/Node.js
+++ /dev/null
@@ -1,47 +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 { uniqueId } from '../utils'
-import { getDoc } from './operation'
-
-export default class Node {
-  constructor () {
-    this.nodeId = uniqueId()
-    this.ref = this.nodeId
-    this.children = []
-    this.pureChildren = []
-    this.parentNode = null
-    this.nextSibling = null
-    this.previousSibling = null
-  }
-
-  /**
-  * Destroy current node, and remove itself form nodeMap.
-  */
-  destroy () {
-    const doc = getDoc(this.docId)
-    if (doc) {
-      delete this.docId
-      delete doc.nodeMap[this.nodeId]
-    }
-    this.children.forEach(child => {
-      child.destroy()
-    })
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/vdom/WeexElement.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/WeexElement.js b/html5/runtime/vdom/WeexElement.js
deleted file mode 100644
index 455e69e..0000000
--- a/html5/runtime/vdom/WeexElement.js
+++ /dev/null
@@ -1,84 +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 { getTaskCenter } from './operation'
-
-let Element
-
-export function setElement (El) {
-  Element = El
-}
-
-/**
- * A map which stores all type of elements.
- * @type {Object}
- */
-const registeredElements = {}
-
-/**
- * Register an extended element type with component methods.
- * @param  {string} type    component type
- * @param  {array}  methods a list of method names
- */
-export function registerElement (type, methods) {
-  // Skip when no special component methods.
-  if (!methods || !methods.length) {
-    return
-  }
-
-  // Init constructor.
-  class WeexElement extends Element {}
-
-  // Add methods to prototype.
-  methods.forEach(methodName => {
-    WeexElement.prototype[methodName] = function (...args) {
-      const taskCenter = getTaskCenter(this.docId)
-      if (taskCenter) {
-        return taskCenter.send('component', {
-          ref: this.ref,
-          component: type,
-          method: methodName
-        }, args)
-      }
-    }
-  })
-
-  // Add to element type map.
-  registeredElements[type] = WeexElement
-}
-
-export function unregisterElement (type) {
-  delete registeredElements[type]
-}
-
-export function getWeexElement (type) {
-  return registeredElements[type]
-}
-
-export function isWeexElement (type) {
-  return !!registeredElements[type]
-}
-
-/**
- * Clear all element types. Only for testing.
- */
-export function clearWeexElements () {
-  for (const type in registeredElements) {
-    unregisterElement(type)
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/vdom/index.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/index.js b/html5/runtime/vdom/index.js
deleted file mode 100644
index 2947f5e..0000000
--- a/html5/runtime/vdom/index.js
+++ /dev/null
@@ -1,37 +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 Node from './Node'
-import Element from './Element'
-import Comment from './Comment'
-import Document from './Document'
-
-export {
-  registerElement,
-  unregisterElement,
-  isWeexElement,
-  clearWeexElements
-} from './WeexElement'
-
-export {
-  Document,
-  Node,
-  Element,
-  Comment
-}


[02/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/vm/directive.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/vm/directive.js b/test/js-framework/unit/default/vm/directive.js
new file mode 100644
index 0000000..451b310
--- /dev/null
+++ b/test/js-framework/unit/default/vm/directive.js
@@ -0,0 +1,228 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import {
+  applyNaitveComponentOptions,
+  bindSubVm,
+  bindSubVmAfterInitialized
+} from '../../../../../runtime/frameworks/legacy/vm/directive'
+
+import {
+  initState
+} from '../../../../../runtime/frameworks/legacy/core/state'
+import config from '../../../../../runtime/frameworks/legacy/config'
+
+const { nativeComponentMap } = config
+const directive = {}
+
+function extendVm (vm, methodNames) {
+  methodNames.forEach((name) => {
+    vm[name] = directive[name]
+  })
+  initState(vm)
+}
+
+function initElement (el) {
+  el.setAttr = function (k, v) { this.attr[k] = v }
+  el.setStyle = function (k, v) { this.style[k] = v }
+  el.setClassStyle = function (style) { this.classStyle = style }
+  el.addEvent = function (t, h) {
+    this.event[t] = h
+  }
+}
+
+describe('apply component options', () => {
+  it('apply top prop', () => {
+    nativeComponentMap['test-apply'] = {
+      type: 'test-apply1',
+      append: 'tree'
+    }
+    const template = {
+      type: 'test-apply'
+    }
+    applyNaitveComponentOptions(template)
+    expect(template.type).to.be.equal('test-apply')
+    expect(template.append).to.be.equal('tree')
+
+    delete nativeComponentMap['test-apply']
+  })
+
+  it('apply a object', () => {
+    nativeComponentMap['test-apply'] = {
+      classList: ['c'],
+      attr: {
+        a: 'a',
+        b: 'b'
+      }
+    }
+    const spy = sinon.spy()
+    const template = {
+      type: 'test-apply',
+      classList: spy,
+      attr: {
+        b: '2'
+      }
+    }
+    applyNaitveComponentOptions(template)
+
+    expect(template).to.deep.equal({
+      type: 'test-apply',
+      classList: spy,
+      attr: {
+        a: 'a',
+        b: '2'
+      }
+    })
+
+    delete nativeComponentMap['test-apply']
+  })
+})
+
+// exports._bindSubVm(subVm, template)
+describe('bind external infomations to sub vm', () => {
+  let vm, subVm
+  // const methodNames = [
+  //   '_watch', '_bindKey', '_bindDir',
+  //   '_setId', '_setAttr', '_setClass', '_setStyle',
+  //   '_setEvent', '_bindEvents', '_bindElement',
+  //   '_bindSubVm', '_bindSubVmAfterInitialized']
+  beforeEach(() => {
+    vm = {
+      _data: { a: 1, b: 2, c: 'class-style1' },
+      _watchers: [],
+      _app: { eventManager: { add: () => {} }},
+      _options: {
+        style: {
+          'class-style1': {
+            aaa: 1,
+            bbb: 2
+          },
+          'class-style2': {
+            aaa: 2,
+            ccc: 3
+          }
+        }
+      },
+      foo: function () {}
+    }
+    extendVm(vm, [])
+    subVm = {
+      _options: {
+        props: {
+          a: String,
+          b: String
+        }
+      }
+    }
+  })
+
+  it('bind to no-root-element sub vm', () => {
+    bindSubVm(vm, subVm, {
+      attr: { a: 3, c: 4 },
+      style: { a: 2 },
+      events: { click: 'foo' }
+    })
+    expect(subVm.a).eql(3)
+    expect(subVm.c).is.undefined
+    expect(subVm._rootEl).is.undefined
+  })
+
+  it('bind props with external data', () => {
+    bindSubVm(vm, subVm, {
+      attr: { a: function () { return this.b } }
+    })
+    expect(subVm.a).eql(2)
+  })
+
+  it('bind styles to a sub vm with root element', () => {
+    subVm._rootEl = {
+      attr: {},
+      style: {},
+      event: []
+    }
+    const template = {
+      style: { aaa: 2, bbb: function () { return this.a } }
+    }
+    initElement(subVm._rootEl)
+    bindSubVm(vm, subVm, template)
+    bindSubVmAfterInitialized(vm, subVm, template)
+    expect(subVm._rootEl.style.aaa).eql(2)
+    expect(subVm._rootEl.style.bbb).eql(1)
+    vm.a = 3
+    expect(subVm._rootEl.style.bbb).eql(3)
+  })
+
+  it('bind simply classlist to a sub vm with root element', () => {
+    subVm._rootEl = {
+      attr: {},
+      style: {},
+      event: []
+    }
+    const template = {
+      classList: ['class-style1']
+    }
+    initElement(subVm._rootEl)
+    bindSubVm(vm, subVm, template)
+    bindSubVmAfterInitialized(vm, subVm, template)
+    expect(subVm._rootEl.classStyle.aaa).eql(1)
+    expect(subVm._rootEl.classStyle.bbb).eql(2)
+  })
+
+  it('bind classlist to a sub vm with root element', () => {
+    subVm._rootEl = {
+      attr: {},
+      style: {},
+      event: []
+    }
+    const template = {
+      classList: function () {
+        return [this.c]
+      }
+    }
+    initElement(subVm._rootEl)
+    bindSubVm(vm, subVm, template)
+    bindSubVmAfterInitialized(vm, subVm, template)
+    expect(subVm._rootEl.classStyle.aaa).eql(1)
+    expect(subVm._rootEl.classStyle.bbb).eql(2)
+    vm.c = 'class-style2'
+    expect(subVm._rootEl.classStyle.aaa).eql(2)
+    expect(subVm._rootEl.classStyle.bbb).to.be.undefined
+    expect(subVm._rootEl.classStyle.ccc).eql(3)
+  })
+
+  it('bind events to a sub vm with root element', () => {
+    subVm._rootEl = {
+      attr: {},
+      style: {},
+      event: {}
+    }
+    const template = {
+      events: { click: 'foo' }
+    }
+    initElement(subVm._rootEl)
+    bindSubVm(vm, subVm, template)
+    bindSubVmAfterInitialized(vm, subVm, template)
+    // expect(subVm._rootEl.event.click).a('function')
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/vm/dom-helper.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/vm/dom-helper.js b/test/js-framework/unit/default/vm/dom-helper.js
new file mode 100644
index 0000000..d0e5a54
--- /dev/null
+++ b/test/js-framework/unit/default/vm/dom-helper.js
@@ -0,0 +1,405 @@
+/*
+ * 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 chai from 'chai'
+const { expect } = chai
+
+import {
+  createElement,
+  createBlock,
+  createBody,
+  attachTarget,
+  moveTarget,
+  removeTarget
+} from '../../../../../runtime/frameworks/legacy/vm/dom-helper'
+import { Document } from '../../../../../runtime/vdom'
+import Listener from '../../../../../runtime/bridge/Listener'
+
+describe('help create body', () => {
+  let vm
+
+  beforeEach(() => {
+    vm = {
+      _app: { doc: new Document('foo', null, null, Listener) }
+    }
+  })
+
+  afterEach(() => {
+    vm._app.doc.destroy()
+    vm = null
+  })
+
+  it('create body with type', () => {
+    const result = createBody(vm, 'bar')
+    expect(result).is.an.object
+    expect(result.type).eql('bar')
+    expect(result.ref).eql('_root')
+    expect(result.docId).is.not.ok
+  })
+})
+
+describe('help create element', () => {
+  let vm
+
+  beforeEach(() => {
+    vm = {
+      _app: { doc: new Document('foo', null, null, Listener) }
+    }
+  })
+
+  afterEach(() => {
+    vm._app.doc.destroy()
+    vm = null
+  })
+
+  it('create element with type', () => {
+    const result = createElement(vm, 'bar')
+    expect(result).is.an.object
+    expect(result.type).eql('bar')
+    expect(result.docId).is.not.ok
+  })
+})
+
+describe('help create block', () => {
+  let vm
+
+  beforeEach(() => {
+    vm = {
+      _app: { doc: new Document('foo', null, null, Listener) }
+    }
+  })
+
+  afterEach(() => {
+    vm._app.doc.destroy()
+    vm = null
+  })
+
+  it('create block with element', () => {
+    const element = vm._app.doc.createElement('bar')
+    const result = createBlock(vm, element)
+    expect(result).is.an.object
+    expect(result.start).is.an.object
+    expect(result.end).is.an.object
+    expect(result.start.type).eql('comment')
+    expect(result.end.type).eql('comment')
+    expect(result.start.value).eql('start')
+    expect(result.end.value).eql('end')
+    expect(result.element).equal(element)
+    expect(result.element.children).eql([result.start, result.end])
+  })
+})
+
+describe('help attach target', () => {
+  let vm
+
+  beforeEach(() => {
+    vm = {
+      _app: { doc: new Document('foo', null, null, Listener) }
+    }
+  })
+
+  afterEach(() => {
+    vm._app.doc.destroy()
+    vm = null
+  })
+
+  it('attach body to documentElement', () => {
+    const oriCallnative = global.callNative
+    global.callNative = function () {}
+    const target = createBody(vm, 'bar')
+    const dest = vm._app.doc.documentElement
+    attachTarget(vm, target, dest)
+    expect(dest.children).eql([target])
+    global.callNative = oriCallnative
+  })
+
+  it('attach element to body', () => {
+    const target = createElement(vm, 'bar')
+    const dest = createBody(vm, 'baz')
+    attachTarget(vm, target, dest)
+    expect(dest.children).eql([target])
+  })
+
+  it('attach element to element', () => {
+    const target = createElement(vm, 'bar')
+    const dest = createElement(vm, 'baz')
+    attachTarget(vm, target, dest)
+    expect(dest.children).eql([target])
+  })
+
+  it('attach block to element', () => {
+    const parent = createElement(vm, 'bar')
+    const target = createBlock(vm, parent)
+    const dest = createElement(vm, 'baz')
+    attachTarget(vm, target, dest)
+    // block can't attach to another element
+    expect(dest.children).eql([])
+    expect(parent.children).eql([target.start, target.end])
+  })
+
+  it('attach element to block', () => {
+    const target = createElement(vm, 'bar')
+    const parent = createElement(vm, 'baz')
+    const dest = createBlock(vm, parent)
+    attachTarget(vm, target, dest)
+    expect(parent.children).eql([dest.start, target, dest.end])
+  })
+
+  it('attach block to block', () => {
+    const element = createElement(vm, 'bar')
+    const target = createBlock(vm, element)
+    const parent = createElement(vm, 'baz')
+    const dest = createBlock(vm, parent)
+    attachTarget(vm, target, dest)
+    // block can't attach to another element
+    expect(parent.children).eql([dest.start, dest.end])
+    expect(element.children).eql([target.start, target.end])
+  })
+
+  it('attach element to block with an update mark', () => {
+    const target = createElement(vm, 'bar')
+    const parent = createElement(vm, 'baz')
+    const dest = createBlock(vm, parent)
+    const mark = createElement(vm, 'qux')
+
+    attachTarget(vm, target, dest)
+    attachTarget(vm, mark, dest)
+    expect(parent.children).eql([dest.start, target, mark, dest.end])
+
+    dest.updateMark = mark
+    attachTarget(vm, target, dest)
+    expect(parent.children).eql([dest.start, mark, target, dest.end])
+    expect(dest.updateMark).eql(target)
+  })
+
+  it('attach block to block with an update mark', () => {
+    const element = createElement(vm, 'bar')
+    const target = createBlock(vm, element)
+    const parent = createElement(vm, 'baz')
+    const dest = createBlock(vm, parent)
+    const mark = createElement(vm, 'qux')
+
+    attachTarget(vm, target, dest)
+    attachTarget(vm, mark, dest)
+    // block can't attach to another element
+    expect(parent.children).eql([dest.start, mark, dest.end])
+    expect(element.children).eql([target.start, target.end])
+
+    dest.updateMark = mark
+    attachTarget(vm, target, dest)
+    // block can't attach to another element
+    expect(parent.children).eql([dest.start, mark, dest.end])
+    expect(element.children).eql([target.start, target.end])
+  })
+})
+
+describe('help move target', () => {
+  let vm, parent, dest, target1, block1, target2, block2, target3, block3
+
+  beforeEach(() => {
+    vm = {
+      _app: { doc: new Document('foo', null, null, Listener) }
+    }
+    parent = createElement(vm, 'r')
+    dest = createBlock(vm, parent)
+    target1 = createElement(vm, 't1')
+    attachTarget(vm, target1, dest)
+    block1 = createBlock(vm, dest)
+    target2 = createElement(vm, 't2')
+    attachTarget(vm, target2, dest)
+    block2 = createBlock(vm, dest)
+    target3 = createElement(vm, 't3')
+    attachTarget(vm, target3, block2)
+    block3 = createBlock(vm, block2)
+  })
+
+  afterEach(() => {
+    vm._app.doc.destroy()
+    vm = null
+  })
+
+  it('move an element to an element mark', () => {
+    /* eslint-disable indent */
+    expect(parent.children).eql([
+      dest.start,
+      target1, block1.start, block1.end,
+      target2, block2.start,
+        target3, block3.start, block3.end,
+      block2.end,
+      dest.end])
+    /* eslint-enable indent */
+
+    const mark = target2
+    dest.updateMark = mark
+
+    moveTarget(vm, target1, mark)
+
+    /* eslint-disable indent */
+    expect(parent.children).eql([
+      dest.start,
+      block1.start, block1.end,
+      target2, target1, block2.start,
+        target3, block3.start, block3.end,
+      block2.end,
+      dest.end])
+    /* eslint-enable indent */
+  })
+
+  it('move an element to a block mark', () => {
+    const mark = block2.end
+    dest.updateMark = mark
+
+    moveTarget(vm, target1, mark)
+
+    /* eslint-disable indent */
+    expect(parent.children).eql([
+      dest.start,
+      block1.start, block1.end,
+      target2, block2.start,
+        target3, block3.start, block3.end,
+      block2.end, target1,
+      dest.end])
+    /* eslint-enable indent */
+  })
+
+  it('move a block to an element mark', () => {
+    const mark = target2
+    dest.updateMark = mark
+
+    moveTarget(vm, block1, mark)
+
+    /* eslint-disable indent */
+    expect(parent.children).eql([
+      dest.start,
+      target1,
+      target2, block1.start, block1.end,
+      block2.start,
+        target3, block3.start, block3.end,
+      block2.end,
+      dest.end])
+    /* eslint-enable indent */
+  })
+
+  it('move a block to a block mark', () => {
+    const mark = block2.end
+    dest.updateMark = mark
+
+    moveTarget(vm, block1, mark)
+
+    /* eslint-disable indent */
+    expect(parent.children).eql([
+      dest.start,
+      target1,
+      target2,
+      block2.start,
+        target3, block3.start, block3.end,
+      block2.end, block1.start, block1.end,
+      dest.end])
+    /* eslint-enable indent */
+  })
+
+  it('move a whole block to a block mark', () => {
+    const mark = block1.end
+    dest.updateMark = mark
+
+    moveTarget(vm, block2, mark)
+
+    /* eslint-disable indent */
+    expect(parent.children).eql([
+      dest.start,
+      target1, block1.start, block1.end,
+      block2.start,
+        target3, block3.start, block3.end,
+      block2.end,
+      target2,
+      dest.end])
+    /* eslint-enable indent */
+  })
+})
+
+describe('help remove target', () => {
+  let vm
+
+  beforeEach(() => {
+    vm = {
+      _app: { doc: new Document('foo', null, null, Listener) }
+    }
+  })
+
+  afterEach(() => {
+    vm._app.doc.destroy()
+    vm = null
+  })
+
+  it('remove body', () => {
+    const oriCallnative = global.callNative
+    global.callNative = function () {}
+    const parent = vm._app.doc.documentElement
+    const element = createBody(vm, 'baz')
+    parent.appendChild(element)
+    expect(parent.children).eql([element])
+    removeTarget(vm, element)
+    expect(parent.children).eql([])
+    global.callNative = oriCallnative
+  })
+
+  it('remove element', () => {
+    const parent = createElement(vm, 'bar')
+    const element = createElement(vm, 'baz')
+    parent.appendChild(element)
+    expect(parent.children).eql([element])
+    removeTarget(vm, element)
+    expect(parent.children).eql([])
+  })
+
+  it('remove block', () => {
+    const element = createElement(vm, 'baz')
+    const prevElement = createElement(vm, 'prev')
+    const nextElement = createElement(vm, 'next')
+    const parent = createElement(vm, 'bar')
+    attachTarget(vm, prevElement, parent)
+    const block = createBlock(vm, parent)
+    attachTarget(vm, element, block)
+    attachTarget(vm, nextElement, parent)
+
+    expect(parent.children).eql([
+      prevElement, block.start, element, block.end, nextElement])
+
+    removeTarget(vm, block)
+    expect(parent.children).eql([
+      prevElement, nextElement])
+  })
+
+  it('remove block but preserved itself', () => {
+    const element = createElement(vm, 'baz')
+    const prevElement = createElement(vm, 'prev')
+    const nextElement = createElement(vm, 'next')
+    const parent = createElement(vm, 'bar')
+    attachTarget(vm, prevElement, parent)
+    const block = createBlock(vm, parent)
+    attachTarget(vm, element, block)
+    attachTarget(vm, nextElement, parent)
+
+    expect(parent.children).eql([
+      prevElement, block.start, element, block.end, nextElement])
+
+    removeTarget(vm, block, true)
+    expect(parent.children).eql([
+      prevElement, block.start, block.end, nextElement])
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/vm/events.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/vm/events.js b/test/js-framework/unit/default/vm/events.js
new file mode 100644
index 0000000..b43b016
--- /dev/null
+++ b/test/js-framework/unit/default/vm/events.js
@@ -0,0 +1,416 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import Vm from '../../../../../runtime/frameworks/legacy/vm'
+import { Document } from '../../../../../runtime/vdom'
+
+describe('bind and fire events', () => {
+  let doc, customComponentMap, spy
+
+  function checkReady (vm, handler) {
+    /* istanbul ignore else */
+    if (vm._ready) {
+      handler()
+    }
+    else {
+      vm.$on('hook:ready', handler)
+    }
+  }
+
+  beforeEach(() => {
+    spy = sinon.spy()
+    doc = new Document('test', '', spy)
+    customComponentMap = {}
+  })
+
+  afterEach(() => {
+    doc.destroy()
+  })
+
+  it('bind event and fire in a single dom', (done) => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        attr: {
+          a: function () { return this.x }
+        },
+        events: { click: 'handleClick' }
+      },
+      data: function () {
+        return { x: 1 }
+      },
+      methods: {
+        handleClick: function () {
+          this.x = 2
+        }
+      }
+    }
+
+    const app = { doc, customComponentMap }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    checkReady(vm, function () {
+      expect(doc.body.event.click.handler).a('function')
+
+      const el = doc.body
+      expect(el.attr.a).eql(1)
+      expect(spy.args.length).eql(1)
+      expect(doc.listener.updates.length).eql(0)
+
+      el.event.click.handler({ xxx: 1 })
+      expect(el.attr.a).eql(2)
+      expect(spy.args.length).eql(2)
+      expect(spy.args[1][0]).eql([
+        { module: 'dom', method: 'updateAttrs', args: [el.ref, { a: 2 }] }
+      ])
+
+      done()
+    })
+  })
+
+  it('emit, broadcast and dispatch vm events', (done) => {
+    customComponentMap.foo = {
+      template: {
+        type: 'aaa',
+        children: [{ type: 'bar', component: true }]
+      }
+    }
+    customComponentMap.bar = {
+      template: {
+        type: 'bbb'
+      }
+    }
+
+    const app = { doc, customComponentMap }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    checkReady(vm, function () {
+      doc.close()
+
+      expect(vm._childrenVms.length).eql(1)
+
+      const subVm = vm._childrenVms[0]
+      expect(vm._type).eql('foo')
+      expect(subVm._type).eql('bar')
+
+      const spyA = sinon.spy()
+      const spyB = sinon.spy()
+
+      vm.$on('customTypeA', spyA)
+      subVm.$on('customTypeA', spyB)
+
+      let detail = { aaa: 111 }
+      vm.$emit('customTypeA', detail)
+      expect(spyA.args.length).eql(1)
+      expect(spyB.args.length).eql(0)
+
+      detail = { aaa: 111 }
+      subVm.$emit('customTypeA', detail)
+      expect(spyA.args.length).eql(1)
+      expect(spyB.args.length).eql(1)
+
+      detail = { aaa: 111 }
+      vm.$broadcast('customTypeA', detail)
+      expect(spyA.args.length).eql(2)
+      expect(spyB.args.length).eql(2)
+
+      detail = { aaa: 111 }
+      subVm.$broadcast('customTypeA', detail)
+      expect(spyA.args.length).eql(2)
+      expect(spyB.args.length).eql(3)
+
+      detail = { aaa: 111 }
+      vm.$dispatch('customTypeA', detail)
+      expect(spyA.args.length).eql(3)
+      expect(spyB.args.length).eql(3)
+
+      detail = { aaa: 111 }
+      subVm.$dispatch('customTypeA', detail)
+      expect(spyA.args.length).eql(4)
+      expect(spyB.args.length).eql(4)
+
+      vm.$off('customTypeA', spyA)
+      subVm.$off('customTypeA')
+
+      detail = { aaa: 111 }
+      vm.$emit('customTypeA', detail)
+      detail = { aaa: 111 }
+      subVm.$emit('customTypeA', detail)
+      detail = { aaa: 111 }
+      vm.$broadcast('customTypeA', detail)
+      detail = { aaa: 111 }
+      subVm.$broadcast('customTypeA', detail)
+      detail = { aaa: 111 }
+      vm.$dispatch('customTypeA', detail)
+      detail = { aaa: 111 }
+      subVm.$dispatch('customTypeA', detail)
+
+      expect(spyA.args.length).eql(4)
+      expect(spyB.args.length).eql(4)
+
+      const spyAA = function (e) {
+        e.stop()
+        spyA()
+      }
+      const spyBB = function (e) {
+        e.stop()
+        spyB()
+      }
+
+      vm.$on('customTypeA', spyAA)
+      subVm.$on('customTypeA', spyBB)
+      vm.$off()
+      vm.$off('unknownTypeA', spyA)
+
+      detail = { aaa: 111 }
+      vm.$emit('customTypeA', detail)
+      expect(spyA.args.length).eql(5)
+      expect(spyB.args.length).eql(4)
+      detail = { aaa: 111 }
+      subVm.$emit('customTypeA', detail)
+      expect(spyA.args.length).eql(5)
+      expect(spyB.args.length).eql(5)
+      detail = { aaa: 111 }
+      vm.$broadcast('customTypeA', detail)
+      expect(spyA.args.length).eql(6)
+      expect(spyB.args.length).eql(5)
+      detail = { aaa: 111 }
+      subVm.$broadcast('customTypeA', detail)
+      expect(spyA.args.length).eql(6)
+      expect(spyB.args.length).eql(6)
+      detail = { aaa: 111 }
+      vm.$dispatch('customTypeA', detail)
+      expect(spyA.args.length).eql(7)
+      expect(spyB.args.length).eql(6)
+      detail = { aaa: 111 }
+      subVm.$dispatch('customTypeA', detail)
+      expect(spyA.args.length).eql(7)
+      expect(spyB.args.length).eql(7)
+
+      done()
+    })
+  })
+
+  it('broadcast for repeat vms', (done) => {
+    const vmSpy = sinon.spy()
+    const subVmSpy = sinon.spy()
+
+    customComponentMap.foo = {
+      data: function () {
+        return { list: [{ a: 1 }, { a: 2 }, { a: 3 }] }
+      },
+      template: {
+        type: 'container',
+        children: [{
+          type: 'bar', component: true,
+          repeat: function () { return this.list }
+        }]
+      },
+      events: {
+        customTypeA: function (e) {
+          vmSpy(this, e)
+        }
+      }
+    }
+    customComponentMap.bar = {
+      template: {
+        type: 'aaa'
+      },
+      events: {
+        customTypeA: function (e) {
+          subVmSpy(this, e)
+        }
+      }
+    }
+
+    const app = { doc, customComponentMap }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    checkReady(vm, function () {
+      doc.close()
+      expect(vm._childrenVms.length).eql(3)
+
+      let detail = { aaa: 111 }
+      vm.$emit('customTypeA', detail)
+      expect(vmSpy.args.length).eql(1)
+      expect(subVmSpy.args.length).eql(0)
+      expect(vmSpy.args[0][0]).equal(vm)
+      expect(vmSpy.args[0][1]).is.an.object
+      expect(vmSpy.args[0][1].detail).equal(detail)
+
+      detail = { aaa: 111 }
+      vm.$broadcast('customTypeA', detail)
+      expect(vmSpy.args.length).eql(2)
+      expect(subVmSpy.args.length).eql(3)
+      expect(vmSpy.args[1][0]).equal(vm)
+      expect(vmSpy.args[1][1]).is.an.object
+      expect(vmSpy.args[1][1].detail).equal(detail)
+      expect(subVmSpy.args[1][0]).equal(vm._childrenVms[1])
+      expect(subVmSpy.args[1][1]).is.an.object
+      expect(subVmSpy.args[1][1].detail).equal(detail)
+
+      done()
+    })
+  })
+
+  it('dispatch from repeat vms', (done) => {
+    const vmSpy = sinon.spy()
+    const subVmSpy = sinon.spy()
+
+    customComponentMap.foo = {
+      data: function () {
+        return { list: [{ a: 1 }, { a: 2 }, { a: 3 }] }
+      },
+      template: {
+        type: 'container',
+        children: [{
+          type: 'bar', component: true,
+          repeat: function () { return this.list }
+        }]
+      },
+      events: {
+        customTypeA: function (e) {
+          vmSpy(this, e)
+        }
+      }
+    }
+    customComponentMap.bar = {
+      template: {
+        type: 'aaa'
+      },
+      events: {
+        customTypeA: function (e) {
+          subVmSpy(this, e)
+        }
+      }
+    }
+
+    const app = { doc, customComponentMap }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    checkReady(vm, function () {
+      doc.close()
+      expect(vm._childrenVms.length).eql(3)
+
+      let detail = { aaa: 111 }
+      const subVm = vm._childrenVms[1]
+      subVm.$emit('customTypeA', detail)
+      expect(vmSpy.args.length).eql(0)
+      expect(subVmSpy.args.length).eql(1)
+      expect(subVmSpy.args[0][0]).equal(subVm)
+      expect(subVmSpy.args[0][1]).is.an.object
+      expect(subVmSpy.args[0][1].detail).equal(detail)
+
+      detail = { aaa: 111 }
+      subVm.$dispatch('customTypeA', detail)
+      expect(vmSpy.args.length).eql(1)
+      expect(subVmSpy.args.length).eql(2)
+      expect(vmSpy.args[0][0]).equal(vm)
+      expect(vmSpy.args[0][1]).is.an.object
+      expect(vmSpy.args[0][1].detail).equal(detail)
+      expect(subVmSpy.args[1][0]).equal(subVm)
+      expect(subVmSpy.args[1][1]).is.an.object
+      expect(subVmSpy.args[1][1].detail).equal(detail)
+
+      done()
+    })
+  })
+
+  it('lifecycel events', (done) => {
+    const vmSpy = sinon.spy()
+
+    customComponentMap.foo = {
+      template: {
+        type: 'aaa',
+        children: [{ type: 'bar', component: true }]
+      },
+      data: function () {
+        return {
+          x: 1
+        }
+      },
+      init: function (e) {
+        vmSpy(this, e)
+        expect(this._data).is.not.ok
+        expect(this.x).is.not.ok
+        expect(this._parentEl).is.not.ok
+      },
+      created: function (e) {
+        vmSpy(this, e)
+        expect(this._data).is.ok
+        expect(this.x).is.ok
+        expect(this._parentEl).is.not.ok
+      },
+      ready: function (e) {
+        vmSpy(this, e)
+        expect(this._data).is.ok
+        expect(this.x).is.ok
+        expect(this._parentEl).is.ok
+        expect(this._parentEl.children.length).eql(1)
+      }
+    }
+    customComponentMap.bar = {
+      template: {
+        type: 'bbb'
+      },
+      created: function (e) {
+        vmSpy(this, e)
+      }
+    }
+
+    const app = { doc, customComponentMap }
+    const evSpy = sinon.spy()
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app }, null, null,
+      {
+        'hook:init': () => evSpy('hook:init'),
+        'hook:created': () => evSpy('hook:created'),
+        'hook:ready': () => evSpy('hook:ready')
+      }
+    )
+
+    checkReady(vm, function () {
+      doc.close()
+
+      expect(vmSpy.args.length).eql(4)
+
+      const subVm = vm._childrenVms[0]
+      const calls = vmSpy.args.map(args => [args[0], args[1].type])
+      expect(calls).eql([
+        [vm, 'hook:init'],
+        [vm, 'hook:created'],
+        [subVm, 'hook:created'],
+        [vm, 'hook:ready']
+      ])
+
+      expect(evSpy.callCount).to.be.equal(3)
+      expect(evSpy.args).to.deep.equal([
+        ['hook:init'],
+        ['hook:created'],
+        ['hook:ready']
+      ])
+
+      done()
+    })
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/vm/vm.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/vm/vm.js b/test/js-framework/unit/default/vm/vm.js
new file mode 100644
index 0000000..761c913
--- /dev/null
+++ b/test/js-framework/unit/default/vm/vm.js
@@ -0,0 +1,1975 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import Vm from '../../../../../runtime/frameworks/legacy/vm'
+import { Document } from '../../../../../runtime/vdom'
+import { init as resetTaskHandler } from '../../../../../runtime/bridge/TaskCenter'
+import Differ from '../../../../../runtime/frameworks/legacy/app/differ'
+
+const oriCallNative = global.callNative
+
+describe('generate virtual dom for a single vm', () => {
+  const spy = sinon.spy()
+  const spy1 = sinon.spy()
+  let doc
+  let customComponentMap
+  let differ
+
+  beforeEach(() => {
+    differ = new Differ('test')
+    doc = new Document('test', '', function (actions) {
+      actions.forEach((action) => {
+        spy.apply(null, ['test', action.method].concat(action.args))
+      })
+    })
+    customComponentMap = {}
+  })
+
+  afterEach(() => {
+    spy.reset()
+    spy1.reset()
+    doc.destroy()
+  })
+
+  it('$watch', () => {
+    const data = {
+      a: {
+        b: 1
+      }
+    }
+
+    customComponentMap.foo = {
+      template: {
+        type: 'container'
+      },
+      data: data
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(doc.body).is.an.object
+
+    vm.$watch(function () {
+      return this.a.b
+    }, (value) => {
+      expect(value).eql(2)
+    })
+
+    data.a.b = 2
+  })
+
+  it('vm.data is not a object', () => {
+    const data = 'hello'
+
+    customComponentMap.foo = {
+      template: {
+        type: 'container'
+      },
+      data: data
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(doc.body).is.an.object
+  })
+
+  it('no param parentVm', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container'
+      }
+    }
+
+    const vm = new Vm('foo', customComponentMap.foo, null)
+
+    expect(vm._app).is.an.object
+    expect(doc.body).is.an.object
+  })
+
+  it('no param options', () => {
+    const app = {}
+    const vm = new Vm('foo', null, { _app: app })
+
+    expect(vm._app).is.an.object
+    expect(doc.body).is.an.object
+  })
+
+  it('old method.ready', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container'
+      },
+      methods: {
+        ready: spy1
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(doc.body).is.an.object
+    expect(doc.body.type).eql('container')
+    expect(spy1).callCount(1)
+  })
+
+  it('generate an static element ', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        attr: {
+          a: 1,
+          b: 2,
+          static: ''
+        },
+        style: {
+          c: 3,
+          d: 4
+        }
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(vm._static).eql(true)
+    expect(doc.body).is.an.object
+    expect(doc.body.type).eql('container')
+    expect(doc.body.attr).eql({ a: 1, b: 2, static: '' })
+    expect(doc.body.style).eql({ c: 3, d: 4 })
+    expect(doc.body.children).is.an.array
+    expect(doc.body.children.length).eql(0)
+  })
+
+  it('generate an single element', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        attr: {
+          a: 1,
+          b: 2
+        },
+        style: {
+          c: 3,
+          d: 4
+        }
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(doc.body).is.an.object
+    expect(doc.body.type).eql('container')
+    expect(doc.body.attr).eql({ a: 1, b: 2 })
+    expect(doc.body.style).eql({ c: 3, d: 4 })
+    expect(doc.body.children).is.an.array
+    expect(doc.body.children.length).eql(0)
+  })
+
+  it('generate an element tree', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container', attr: { a: 1, b: 2 }, style: { c: 3, d: 4 },
+        children: [
+          { type: 'image', attr: { src: '<some image url>' }},
+          { type: 'text', attr: { value: '<some text content>' }}
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.attr).eql({ a: 1, b: 2 })
+    expect(el.style).eql({ c: 3, d: 4 })
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(2)
+
+    const image = el.children[0]
+    const text = el.children[1]
+    expect(image.type).eql('image')
+    expect(image.attr).eql({ src: '<some image url>' })
+    expect(text.type).eql('text')
+    expect(text.attr).eql({ value: '<some text content>' })
+  })
+
+  it('generate an element tree with component options', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'cell',
+        children: [
+          { type: 'text', attr: { value: '<some text content>' }}
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(spy.firstCall.args[0]).to.be.equal('test')
+    expect(spy.firstCall.args[1]).to.be.equal('createBody')
+    expect(spy.firstCall.args[2]).to.deep.equal({
+      ref: '_root',
+      type: 'cell',
+      attr: {
+        append: 'tree'
+      },
+      children: [{
+        ref: spy.firstCall.args[2].children[0].ref,
+        type: 'text',
+        attr: {
+          value: '<some text content>'
+        },
+        style: {}
+      }],
+      style: {}
+    })
+
+    expect(vm._app).equal(app)
+    const el = doc.body
+    expect(el.type).eql('cell')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(1)
+
+    const text = el.children[0]
+    expect(text.type).eql('text')
+    expect(text.attr).eql({ value: '<some text content>' })
+  })
+
+  it('generate an element tree with data binding', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container', attr: { a: 1, b: 2 }, style: { c: 3, d: 4 },
+        children: [
+          { type: 'image', attr: { src: function () { return this.x } }},
+          { type: 'text', attr: { value: function () { return this.n } }},
+          { type: 'text', attr: { value: function () { return this.m } }}
+        ]
+      },
+      data: {
+        x: '<some image url>', y: '<some text content>'
+      },
+      computed: {
+        n: function () {
+          return this.y.toUpperCase()
+        },
+        m: {
+          get: function () {
+            return this.y.toUpperCase()
+          },
+          set: function (v) {
+            this.y = v
+          }
+        }
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(vm.x).eql('<some image url>')
+    expect(vm.y).eql('<some text content>')
+    expect(vm.n).eql('<SOME TEXT CONTENT>')
+    expect(vm.m).eql('<SOME TEXT CONTENT>')
+
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.attr).eql({ a: 1, b: 2 })
+    expect(el.style).eql({ c: 3, d: 4 })
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(3)
+
+    const image = el.children[0]
+    const text = el.children[1]
+    const text2 = el.children[2]
+    expect(image.type).eql('image')
+    expect(image.attr).eql({ src: '<some image url>' })
+    expect(text.type).eql('text')
+    expect(text.attr).eql({ value: '<SOME TEXT CONTENT>' })
+    expect(text2.type).eql('text')
+    expect(text2.attr).eql({ value: '<SOME TEXT CONTENT>' })
+
+    vm.x = '<some image url>'
+    differ.flush()
+    expect(el).equal(doc.body)
+    expect(image).equal(el.children[0])
+    expect(text).equal(el.children[1])
+    expect(text2).equal(el.children[2])
+
+    vm.x = 'other string value'
+    differ.flush()
+    expect(el).equal(doc.body)
+    expect(image).equal(el.children[0])
+    expect(image.attr).eql({ src: 'other string value' })
+
+    vm.y = 'other string value'
+    differ.flush()
+    expect(el).equal(doc.body)
+    expect(text).equal(el.children[1])
+    expect(text.attr).eql({ value: 'OTHER STRING VALUE' })
+    expect(text2).equal(el.children[2])
+    expect(text2.attr).eql({ value: 'OTHER STRING VALUE' })
+
+    vm.m = 'third string value'
+    differ.flush()
+    expect(vm.x).eql('other string value')
+    expect(vm.y).eql('third string value')
+    expect(vm.n).eql('THIRD STRING VALUE')
+    expect(vm.m).eql('THIRD STRING VALUE')
+    expect(el).equal(doc.body)
+    expect(text).equal(el.children[1])
+    expect(text.attr).eql({ value: 'THIRD STRING VALUE' })
+    expect(text2).equal(el.children[2])
+    expect(text2.attr).eql({ value: 'THIRD STRING VALUE' })
+  })
+
+  it('generate an element tree with shown', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [
+          { type: 'prev' },
+          {
+            shown: function () { return this.y },
+            type: 'image', attr: { src: function () { return this.x } }
+          },
+          { type: 'next' }
+        ]
+      },
+      data: {
+        x: '<some image url>', y: true
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(vm.x).eql('<some image url>')
+    expect(vm.y).eql(true)
+
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(5)
+
+    const prev = el.children[0]
+    const starter = el.children[1]
+    const image = el.children[2]
+    const ender = el.children[3]
+    const next = el.children[4]
+    expect(prev.type).eql('prev')
+    expect(starter.type).eql('comment')
+    expect(ender.type).eql('comment')
+    expect(next.type).eql('next')
+    expect(image.type).eql('image')
+    expect(image.attr).eql({ src: '<some image url>' })
+
+    vm.y = false
+    differ.flush()
+
+    expect(el).equal(doc.body)
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(4)
+    expect(prev).equal(el.children[0])
+    expect(starter).equal(el.children[1])
+    expect(ender).equal(el.children[2])
+    expect(next).equal(el.children[3])
+
+    vm.y = true
+    differ.flush()
+
+    expect(el).equal(doc.body)
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(5)
+    expect(prev).equal(el.children[0])
+    expect(starter).equal(el.children[1])
+
+    const image2 = el.children[2]
+    expect(image2.type).eql('image')
+    expect(image2.attr).eql({ src: '<some image url>' })
+    expect(ender).equal(el.children[3])
+    expect(next).equal(el.children[4])
+  })
+
+  it('generate an element tree with repeat', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [
+          { type: 'prev' },
+          {
+            repeat: function () { return this.list },
+            type: 'image', attr: { src: function () { return this.x } }
+          },
+          { type: 'next' }
+        ]
+      },
+      data: {
+        x: '<some image url>',
+        list: [
+          { uid: 1, x: 1 }, { uid: 2, x: 2 }, { uid: 3 }
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(vm.x).eql('<some image url>')
+    expect(vm.list).eql([
+      { uid: 1, x: 1, $index: 0 },
+      { uid: 2, x: 2, $index: 1 },
+      { uid: 3, $index: 2 }])
+
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(7)
+
+    const prev = el.children[0]
+    const starter = el.children[1]
+    const ender = el.children[5]
+    const next = el.children[6]
+    expect(prev.type).eql('prev')
+    expect(starter.type).eql('comment')
+    expect(ender.type).eql('comment')
+    expect(next.type).eql('next')
+
+    expect(el.children[2].type).eql('image')
+    expect(el.children[2].attr).eql({ src: 1 })
+    expect(el.children[3].type).eql('image')
+    expect(el.children[3].attr).eql({ src: 2 })
+    expect(el.children[4].type).eql('image')
+    expect(el.children[4].attr).eql({ src: '<some image url>' })
+
+    vm.list[1].x = 3
+    differ.flush()
+    expect(el.children.length).eql(7)
+    expect(el.children[2].attr).eql({ src: 1 })
+    expect(el.children[3].attr).eql({ src: 3 })
+    expect(el.children[4].attr).eql({ src: '<some image url>' })
+
+    vm.x = 'other string value'
+    differ.flush()
+    expect(el.children.length).eql(7)
+    expect(el.children[2].attr).eql({ src: 1 })
+    expect(el.children[3].attr).eql({ src: 3 })
+    expect(el.children[4].attr).eql({ src: 'other string value' })
+
+    vm.list.push({ uid: 4, x: 4 })
+    vm.list.push({ uid: 5 }, { uid: 6, x: 6 })
+
+    differ.flush()
+
+    expect(el.children.length).eql(10)
+    expect(el.children[2].attr).eql({ src: 1 })
+    expect(el.children[3].attr).eql({ src: 3 })
+    expect(el.children[4].attr).eql({ src: 'other string value' })
+    expect(el.children[5].attr).eql({ src: 4 })
+    expect(el.children[6].attr).eql({ src: 'other string value' })
+    expect(el.children[7].attr).eql({ src: 6 })
+
+    vm.list.splice(1, 2, { uid: 7, x: 7 })
+    differ.flush()
+
+    expect(el.children.length).eql(9)
+    expect(el.children[2].attr).eql({ src: 1 })
+    expect(el.children[3].attr).eql({ src: 7 })
+    expect(el.children[4].attr).eql({ src: 4 })
+    expect(el.children[5].attr).eql({ src: 'other string value' })
+    expect(el.children[6].attr).eql({ src: 6 })
+
+    vm.list.$set(0, { uid: 321, x: 32 })
+    vm.list.$set(10, { uid: 8889, x: 8888 })
+    differ.flush()
+
+    expect(el.children.length).eql(10)
+    expect(el.children[2].attr).eql({ src: 32 })
+    expect(el.children[7].attr).eql({ src: 8888 })
+
+    vm.list.unshift({ uid: 12345, x: 123456 })
+    differ.flush()
+
+    expect(el.children.length).eql(11)
+    expect(el.children[2].attr).eql({ src: 123456 })
+  })
+
+  it('generate an static element tree with shown and repeat', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        attr: {
+          static: ''
+        },
+        children: [
+          { type: 'prev' },
+          {
+            shown: function () { return this.x % 2 === 0 },
+            repeat: function () { return this.list },
+            type: 'image', attr: { src: function () { return this.x } }
+          },
+          { type: 'next' }
+        ]
+      },
+      data: {
+        x: '<some image url>',
+        list: [
+          { uid: 1, x: 1 }, { uid: 2, x: 2 }, { uid: 3 }
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(vm._static).equal(true)
+    expect(vm.x).eql('<some image url>')
+    expect(vm.list).eql([
+      { uid: 1, x: 1, $index: 0 },
+      { uid: 2, x: 2, $index: 1 },
+      { uid: 3, $index: 2 }])
+
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(11)
+
+    const prev = el.children[0]
+    const next = el.children[10]
+    expect(prev.type).eql('prev')
+    expect(el.children[1].type).eql('comment')
+    expect(el.children[1].value).eql('start')
+    expect(el.children[9].type).eql('comment')
+    expect(el.children[9].value).eql('end')
+
+    expect(el.children[2].type).eql('comment')
+    expect(el.children[2].value).eql('start')
+    expect(el.children[3].type).eql('comment')
+    expect(el.children[3].value).eql('end')
+
+    expect(el.children[4].type).eql('comment')
+    expect(el.children[4].value).eql('start')
+    expect(el.children[5].type).eql('image')
+    expect(el.children[5].attr).eql({ src: 2 })
+    expect(el.children[6].type).eql('comment')
+    expect(el.children[6].value).eql('end')
+
+    expect(el.children[7].type).eql('comment')
+    expect(el.children[7].value).eql('start')
+    expect(el.children[8].type).eql('comment')
+    expect(el.children[8].value).eql('end')
+    expect(next.type).eql('next')
+
+    vm.list[0].x = 4
+    differ.flush()
+
+    expect(el.children.length).eql(11)
+    expect(el.children[5].type).eql('image')
+    expect(el.children[5].attr).eql({ src: 2 })
+  })
+
+  it('generate an element tree which root element with shown', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        shown: function () { return this.x % 2 === 0 }
+      },
+      data: {
+        x: 2
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(vm.x).eql(2)
+  })
+
+  it('append tree', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        attr: {
+          append: 'tree'
+        },
+        children: [
+          { type: 'a' },
+          { repeat: {}}
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+  })
+
+  it('repeat is not a function', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [
+          { type: 'a' },
+          { repeat: {}}
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+  })
+
+  it('repeat oldStyle with item which not a object', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [
+          { type: 'a' },
+          { repeat: function () { return [1, 2, 3] } }
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+  })
+
+  it('classList length is zero', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        classList: []
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+  })
+
+  it('classList is a function', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        classList: function () { return [] }
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+  })
+
+  it('generate an element tree which root element with repeat', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        repeat: function () { return this.list }
+      },
+      data: {
+        list: [
+          { uid: 1, x: 1 }, { uid: 2, x: 2 }, { uid: 3 }
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(vm.list).eql([
+      { uid: 1, x: 1 },
+      { uid: 2, x: 2 },
+      { uid: 3 }
+    ])
+  })
+
+  it('generate an element tree with shown and repeat', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [
+          { type: 'prev' },
+          {
+            shown: function () { return this.x % 2 === 0 },
+            repeat: function () { return this.list },
+            type: 'image', attr: { src: function () { return this.x } }
+          },
+          { type: 'next' }
+        ]
+      },
+      data: {
+        x: '<some image url>',
+        list: [
+          { uid: 1, x: 1 }, { uid: 2, x: 2 }, { uid: 3 }
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(vm.x).eql('<some image url>')
+    expect(vm.list).eql([
+      { uid: 1, x: 1, $index: 0 },
+      { uid: 2, x: 2, $index: 1 },
+      { uid: 3, $index: 2 }])
+
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(11)
+
+    const prev = el.children[0]
+    const next = el.children[10]
+    expect(prev.type).eql('prev')
+    expect(el.children[1].type).eql('comment')
+    expect(el.children[1].value).eql('start')
+    expect(el.children[9].type).eql('comment')
+    expect(el.children[9].value).eql('end')
+
+    expect(el.children[2].type).eql('comment')
+    expect(el.children[2].value).eql('start')
+    expect(el.children[3].type).eql('comment')
+    expect(el.children[3].value).eql('end')
+
+    expect(el.children[4].type).eql('comment')
+    expect(el.children[4].value).eql('start')
+    expect(el.children[5].type).eql('image')
+    expect(el.children[5].attr).eql({ src: 2 })
+    expect(el.children[6].type).eql('comment')
+    expect(el.children[6].value).eql('end')
+
+    expect(el.children[7].type).eql('comment')
+    expect(el.children[7].value).eql('start')
+    expect(el.children[8].type).eql('comment')
+    expect(el.children[8].value).eql('end')
+    expect(next.type).eql('next')
+
+    vm.list[0].x = 4
+    differ.flush()
+
+    expect(el.children.length).eql(12)
+    expect(el.children[3].type).eql('image')
+    expect(el.children[3].attr).eql({ src: 4 })
+    expect(el.children[6].type).eql('image')
+    expect(el.children[6].attr).eql({ src: 2 })
+  })
+
+  it('generate more than one group of elements with the same repeat data', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [
+          { type: 'begin' },
+          {
+            repeat: {
+              expression: function () { return this.list },
+              value: 'v1'
+            },
+            type: 'image', attr: { src: function () { return this.v1.x } }
+          },
+          { type: 'middle' },
+          {
+            repeat: {
+              expression: function () { return this.list },
+              value: 'v2'
+            },
+            type: 'text', attr: { value: function () { return this.v2.x } }
+          },
+          { type: 'end' }
+        ]
+      },
+      data: {
+        list: [
+          { uid: 1, x: 1 }, { uid: 2, x: 2 }, { uid: 3 }
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(vm.list).eql([
+      { uid: 1, x: 1 },
+      { uid: 2, x: 2 },
+      { uid: 3 }])
+
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+
+    // [begin, comment, image x 3, comment, middle, comment, text x 3, comment, end]
+    expect(el.children.length).eql(13)
+
+    expect(el.children[0].type).eql('begin')
+    expect(el.children[1].type).eql('comment')
+    expect(el.children[1].value).eql('start')
+    expect(el.children[2].type).eql('image')
+    expect(el.children[2].attr).eql({ src: 1 })
+    expect(el.children[3].type).eql('image')
+    expect(el.children[3].attr).eql({ src: 2 })
+    expect(el.children[4].type).eql('image')
+    expect(el.children[4].attr).eql({})
+    expect(el.children[5].type).eql('comment')
+    expect(el.children[5].value).eql('end')
+    expect(el.children[6].type).eql('middle')
+    expect(el.children[7].type).eql('comment')
+    expect(el.children[7].value).eql('start')
+    expect(el.children[8].type).eql('text')
+    expect(el.children[8].attr).eql({ value: 1 })
+    expect(el.children[9].type).eql('text')
+    expect(el.children[9].attr).eql({ value: 2 })
+    expect(el.children[10].type).eql('text')
+    expect(el.children[10].attr).eql({})
+    expect(el.children[11].type).eql('comment')
+    expect(el.children[11].value).eql('end')
+    expect(el.children[12].type).eql('end')
+
+    vm.list[0].x = 4
+    differ.flush()
+
+    expect(el.children.length).eql(13)
+    expect(el.children[2].attr).eql({ src: 4 })
+    expect(el.children[8].attr).eql({ value: 4 })
+
+    // [begin, comment, image x 4, comment, middle, comment, text x 4, comment, end]
+    vm.list.push({ uid: 10, x: 10 })
+    differ.flush()
+
+    expect(el.children.length).eql(15)
+    expect(el.children[5].attr).eql({ src: 10 })
+    expect(el.children[12].attr).eql({ value: 10 })
+
+    // [begin, comment, image x 2, comment, middle, comment, text x 2, comment, end]
+    vm.list = [
+      { uid: 100, x: 100 }, { uid: 1, x: 1 }
+    ]
+    differ.flush()
+
+    expect(el.children.length).eql(11)
+    expect(el.children[2].attr).eql({ src: 100 })
+    expect(el.children[3].attr).eql({ src: 1 })
+    expect(el.children[7].attr).eql({ value: 100 })
+    expect(el.children[8].attr).eql({ value: 1 })
+
+    vm.list[0].x = 4
+    differ.flush()
+
+    expect(el.children.length).eql(11)
+    expect(el.children[2].attr).eql({ src: 4 })
+    expect(el.children[3].attr).eql({ src: 1 })
+    expect(el.children[7].attr).eql({ value: 4 })
+    expect(el.children[8].attr).eql({ value: 1 })
+
+    vm.list[1].x = 5
+    differ.flush()
+
+    expect(el.children.length).eql(11)
+    expect(el.children[2].attr).eql({ src: 4 })
+    expect(el.children[3].attr).eql({ src: 5 })
+    expect(el.children[7].attr).eql({ value: 4 })
+    expect(el.children[8].attr).eql({ value: 5 })
+  })
+
+  it('generate an element with external data', () => {
+    customComponentMap.foo = {
+      data: () => {
+        return { a: 10, b: 20 }
+      },
+      template: {
+        type: 'container',
+        attr: {
+          x: function () { return this.a },
+          y: function () { return this.b }
+        }
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app }, null, { a: 1000 })
+
+    expect(vm._app).equal(app)
+
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.attr).eql({ x: 1000, y: 20 })
+
+    vm.a = 100
+    vm.b = 200
+    differ.flush()
+
+    expect(el).equal(doc.body)
+    expect(el.attr).eql({ x: 100, y: 200 })
+  })
+})
+
+describe('generate virtual dom for sub vm', () => {
+  let doc
+  let customComponentMap
+  let differ
+
+  beforeEach(() => {
+    global.callNative = function () {}
+    resetTaskHandler()
+    doc = new Document('test', null, null)
+    customComponentMap = {}
+    differ = new Differ('test')
+  })
+
+  afterEach(() => {
+    doc.destroy()
+    global.callNative = oriCallNative
+  })
+
+  it('generate sub elements', () => {
+    customComponentMap.foo = {
+      data: function () {
+        return {
+          showbar1: false,
+          showbar2: false,
+          bar2list: [{
+            id: 'bar2-1'
+          }, {
+            id: 'bar2-2'
+          }]
+        }
+      },
+      template: {
+        type: 'div',
+        children: [
+          { type: 'bar', id: 'bar', component: true,
+            events: { click: 'handleClick' }
+          },
+          { type: 'bar1',
+            shown: function () { return this.showbar1 },
+            id: 'bar1', component: true
+          },
+          { type: 'bar2',
+              shown: function () { return this.showbar2 },
+              repeat: function () { return this.bar2list },
+              id: function () { return this.id },
+              component: true
+          }
+        ]
+      },
+      methods: {
+        handleClick: sinon.spy()
+      }
+    }
+    customComponentMap.bar = {
+      template: {
+        type: 'container',
+        children: [{ type: 'aaa' }, { type: 'bbb' }]
+      }
+    }
+    customComponentMap.bar1 = {
+      template: {
+        type: 'container',
+        children: [{ type: 'aaaa' }, { type: 'bbbb' }]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(vm._rootEl).to.deep.equal(vm._parentEl.children[0])
+
+    expect(vm._childrenVms.length).to.be.equal(1)
+    expect(vm._childrenVms[0]).to.deep.equal(vm._ids['bar'].vm)
+    expect(vm._childrenVms[0]._rootEl).to.deep.equal(vm._ids['bar'].el)
+
+    const el = doc.body
+    expect(el.type).eql('div')
+    expect(el.pureChildren).is.an.array
+    expect(el.pureChildren.length).eql(1)
+
+    const sub = el.children[0]
+    expect(sub.type).eql('container')
+    expect(sub.children).is.an.array
+    expect(sub.children.length).eql(2)
+    expect(sub.children[0].type).eql('aaa')
+    expect(sub.children[1].type).eql('bbb')
+    expect(sub.event.click).is.a.function
+
+    const spy = customComponentMap.foo.methods.handleClick
+    sub.event.click.handler(1, 2, 3)
+    expect(spy.args.length).eql(1)
+    expect(spy.args[0]).eql([1, 2, 3])
+
+    vm.showbar1 = true
+    differ.flush()
+
+    expect(vm._childrenVms.length).to.be.equal(2)
+    expect(vm._childrenVms[1]).to.deep.equal(vm._ids['bar1'].vm)
+    expect(vm._childrenVms[1]._rootEl).to.deep.equal(vm._ids['bar1'].el)
+
+    vm.showbar2 = true
+    differ.flush()
+
+    expect(vm._ids['bar2-1'].vm).to.be.not.undefined
+    expect(vm._ids['bar2-1'].el).to.be.not.undefined
+    expect(vm._ids['bar2-2'].vm).to.be.not.undefined
+    expect(vm._ids['bar2-2'].el).to.be.not.undefined
+  })
+
+  it('generate sub element with static', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        attr: {
+          static: ''
+        },
+        children: [{ type: 'bar', component: true }]
+      }
+    }
+    customComponentMap.bar = {
+      replace: true,
+      template: {
+        type: 'container',
+        children: [{ type: 'aaa' }]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    expect(vm._static).eql(true)
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(1)
+
+    const aaa = el.children[0]
+    expect(aaa.type).eql('aaa')
+    expect(vm._childrenVms.length).to.be.equal(1)
+    expect(vm._childrenVms[0]._static).eql(true)
+  })
+
+  it('generate replaced sub element', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [{ type: 'bar', component: true }]
+      }
+    }
+    customComponentMap.bar = {
+      replace: true,
+      template: {
+        type: 'container',
+        children: [{ type: 'aaa' }]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(1)
+
+    const aaa = el.children[0]
+    expect(aaa.type).eql('aaa')
+  })
+
+  it('generate replaced sub elements', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [{ type: 'bar', component: true }]
+      }
+    }
+    customComponentMap.bar = {
+      replace: true,
+      template: {
+        type: 'container',
+        children: [{ type: 'aaa' }, { type: 'bbb' }]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(4)
+
+    expect(el.children[0].type).eql('comment')
+    const aaa = el.children[1]
+    expect(aaa.type).eql('aaa')
+    const bbb = el.children[2]
+    expect(bbb.type).eql('bbb')
+    expect(el.children[3].type).eql('comment')
+  })
+
+  it('generate sub elements with no props', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [{ type: 'bar', component: true, attr: { x: 10, y: 20 }}]
+      }
+    }
+    customComponentMap.bar = {
+      data: () => {
+        return { x: 1, y: 2 }
+      },
+      template: {
+        type: 'container',
+        children: [
+          { type: 'aaa', attr: { a: function () { return this.x } }},
+          { type: 'bbb', attr: { b: function () { return this.y } }}
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(1)
+
+    const sub = el.children[0]
+    const aaa = sub.children[0]
+    expect(aaa.type).eql('aaa')
+    expect(aaa.attr).eql({ a: 10 })
+    const bbb = sub.children[1]
+    expect(bbb.type).eql('bbb')
+    expect(bbb.attr).eql({ b: 20 })
+  })
+
+  it('generate sub elements with array props', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [{ type: 'bar', component: true, attr: { x: 10, y: 20 }}]
+      }
+    }
+    customComponentMap.bar = {
+      props: ['x'],
+      data: () => {
+        return { x: 1, y: 2 }
+      },
+      template: {
+        type: 'container',
+        children: [
+          { type: 'aaa', attr: { a: function () { return this.x } }},
+          { type: 'bbb', attr: { b: function () { return this.y } }}
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(1)
+
+    const sub = el.children[0]
+    const aaa = sub.children[0]
+    expect(aaa.type).eql('aaa')
+    expect(aaa.attr).eql({ a: 10 })
+    const bbb = sub.children[1]
+    expect(bbb.type).eql('bbb')
+    expect(bbb.attr).eql({ b: 2 })
+  })
+
+  it('generate sub elements with props', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [{ type: 'bar', component: true, attr: { x: 10, y: 20 }}]
+      }
+    }
+    customComponentMap.bar = {
+      props: { x: Number },
+      data: () => {
+        return { x: 1, y: 2 }
+      },
+      template: {
+        type: 'container',
+        children: [
+          { type: 'aaa', attr: { a: function () { return this.x } }},
+          { type: 'bbb', attr: { b: function () { return this.y } }}
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(1)
+
+    const sub = el.children[0]
+    const aaa = sub.children[0]
+    expect(aaa.type).eql('aaa')
+    expect(aaa.attr).eql({ a: 10 })
+    const bbb = sub.children[1]
+    expect(bbb.type).eql('bbb')
+    expect(bbb.attr).eql({ b: 2 })
+  })
+
+  it('generate sub elements with data', () => {
+    customComponentMap.foo = {
+      data: () => {
+        return { a: 10, b: 20 }
+      },
+      template: {
+        type: 'container',
+        children: [{ type: 'bar', component: true, attr: {
+          x: function () { return this.a },
+          y: function () { return this.b }
+        }}]
+      }
+    }
+    customComponentMap.bar = {
+      props: { x: Number },
+      data: () => {
+        return { x: 1, y: 2 }
+      },
+      template: {
+        type: 'container',
+        children: [
+          { type: 'aaa', attr: { a: function () { return this.x } }},
+          { type: 'bbb', attr: { b: function () { return this.y } }}
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(1)
+
+    const sub = el.children[0]
+    const aaa = sub.children[0]
+    expect(aaa.type).eql('aaa')
+    expect(aaa.attr).eql({ a: 10 })
+    const bbb = sub.children[1]
+    expect(bbb.type).eql('bbb')
+    expect(bbb.attr).eql({ b: 2 })
+
+    vm.a = 100
+    vm.b = 200
+    differ.flush()
+
+    expect(el).equal(doc.body)
+    expect(sub).equal(el.children[0])
+    expect(aaa).equal(sub.children[0])
+    expect(aaa.attr).eql({ a: 100 })
+    expect(bbb).equal(sub.children[1])
+    expect(bbb.attr).eql({ b: 2 })
+  })
+
+  it('generate sub elements with shown data', () => {
+    customComponentMap.foo = {
+      data: () => {
+        return { a: 10, b: 20 }
+      },
+      template: {
+        type: 'container',
+        children: [{ type: 'bar', component: true, attr: {
+          x: function () { return this.a },
+          y: function () { return this.b }
+        }, shown: function () { return this.a < 15 } }]
+      }
+    }
+    customComponentMap.bar = {
+      props: { x: Number },
+      data: () => {
+        return { x: 1, y: 2 }
+      },
+      template: {
+        type: 'container',
+        children: [
+          { type: 'aaa', attr: { a: function () { return this.x } }},
+          { type: 'bbb', attr: { b: function () { return this.y } }}
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(3)
+
+    const sub = el.children[1]
+    const aaa = sub.children[0]
+    expect(aaa.type).eql('aaa')
+    expect(aaa.attr).eql({ a: 10 })
+    const bbb = sub.children[1]
+    expect(bbb.type).eql('bbb')
+    expect(bbb.attr).eql({ b: 2 })
+
+    vm.a = 20
+    differ.flush()
+
+    expect(el).equal(doc.body)
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(2)
+  })
+
+  it('generate sub elements with repeat data', () => {
+    customComponentMap.foo = {
+      data: () => {
+        return { list: [{ uid: 1, a: 1 }, { uid: 2, a: 2 }, { uid: 3, a: 3 }] }
+      },
+      template: {
+        type: 'container',
+        children: [{ type: 'bar', component: true, attr: {
+          x: function () { return this.a },
+          y: function () { return this.b }
+        }, repeat: function () { return this.list } }]
+      }
+    }
+    customComponentMap.bar = {
+      props: { x: Number },
+      data: () => {
+        return { x: 1, y: 2 }
+      },
+      template: {
+        type: 'container',
+        children: [
+          { type: 'aaa', attr: { a: function () { return this.x } }},
+          { type: 'bbb', attr: { b: function () { return this.y } }}
+        ]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(vm._app).equal(app)
+
+    const el = doc.body
+    expect(el.type).eql('container')
+    expect(el.children).is.an.array
+    expect(el.children.length).eql(5)
+
+    const sub1 = el.children[1]
+    const aaa1 = sub1.children[0]
+    expect(aaa1.type).eql('aaa')
+    expect(aaa1.attr).eql({ a: 1 })
+    const bbb1 = sub1.children[1]
+    expect(bbb1.type).eql('bbb')
+    expect(bbb1.attr).eql({ b: 2 })
+
+    const sub2 = el.children[2]
+    const aaa2 = sub2.children[0]
+    expect(aaa2.type).eql('aaa')
+    expect(aaa2.attr).eql({ a: 2 })
+    const bbb2 = sub2.children[1]
+    expect(bbb2.type).eql('bbb')
+    expect(bbb2.attr).eql({ b: 2 })
+
+    const sub3 = el.children[3]
+    const aaa3 = sub3.children[0]
+    expect(aaa3.type).eql('aaa')
+    expect(aaa3.attr).eql({ a: 3 })
+    const bbb3 = sub3.children[1]
+    expect(bbb3.type).eql('bbb')
+    expect(bbb3.attr).eql({ b: 2 })
+  })
+
+  it('generate sub elements with content', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [{ type: 'bar', component: true, children: [
+          { type: 'a' }, { type: 'b', attr: { bbb: function () { return this.x } }}
+        ] }]
+      },
+      data: () => {
+        return {
+          x: 1, y: 2
+        }
+      }
+    }
+    customComponentMap.bar = {
+      replace: true,
+      template: {
+        type: 'container',
+        children: [{ type: 'aaa' }, { type: 'content' }, { type: 'bbb' }]
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    const el = doc.body
+
+    expect(el.type).eql('container')
+    // [comment, aaa, comment, a, b, comment, bbb, comment]
+    expect(el.children.length).eql(8)
+    expect(el.children[0].type).eql('comment')
+    expect(el.children[2].type).eql('comment')
+    expect(el.children[5].type).eql('comment')
+    expect(el.children[7].type).eql('comment')
+
+    expect(el.pureChildren.length).eql(4)
+    expect(el.pureChildren[0].type).eql('aaa')
+    expect(el.pureChildren[1].type).eql('a')
+    expect(el.pureChildren[2].type).eql('b')
+    expect(el.pureChildren[2].attr).eql({ bbb: 1 })
+    expect(el.pureChildren[3].type).eql('bbb')
+
+    vm.x = 10
+    differ.flush()
+    expect(el.pureChildren[2].attr).eql({ bbb: 10 })
+  })
+
+  it('generate sub elements with dynamic types', (done) => {
+    customComponentMap.foo = {
+      data: {
+        x: 'bar',
+        y: 'hello'
+      },
+      template: {
+        type: 'div',
+        children: [{
+          type: function () { return this.x },
+          attr: { value: function () { return this.y } }
+        }]
+      }
+    }
+    customComponentMap.bar = {
+      template: {
+        type: 'text',
+        attr: { value: 'bar' }
+      }
+    }
+    customComponentMap.baz = {
+      data: function () {
+        return {
+          value: ''
+        }
+      },
+      template: {
+        type: 'text',
+        attr: { value: function () { return this.value + ' baz' } }
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(doc.body.pureChildren[0].type).eql('text')
+    expect(doc.body.pureChildren[0].attr).eql({ value: 'bar' })
+
+    vm.x = 'baz'
+    expect(doc.body.pureChildren[0].type).eql('text')
+    expect(doc.body.pureChildren[0].attr).eql({ value: 'hello baz' })
+
+    vm.y = 'bye'
+    setTimeout(() => {
+      expect(doc.body.pureChildren[0].type).eql('text')
+      expect(doc.body.pureChildren[0].attr).eql({ value: 'bye baz' })
+
+      vm.x = 'text'
+      expect(doc.body.pureChildren[0].type).eql('text')
+      expect(doc.body.pureChildren[0].attr).eql({ value: 'bye' })
+
+      done()
+    })
+  })
+
+  it('generate sub elements with repeat dynamic types', (done) => {
+    customComponentMap.foo = {
+      data: {
+        list: [
+          { uid: 1, type: 'bar' },
+          { uid: 2, type: 'baz' },
+          { uid: 3, type: 'bar' },
+          { uid: 4, type: 'baz' }
+        ]
+      },
+      template: {
+        type: 'div',
+        children: [{
+          type: function () { return this.v.type },
+          repeat: {
+            expression: function () { return this.list },
+            trackBy: 'uid',
+            value: 'v'
+          }
+        }]
+      }
+    }
+    customComponentMap.bar = {
+      template: {
+        type: 'text',
+        attr: { value: 'bar' }
+      }
+    }
+    customComponentMap.baz = {
+      template: {
+        type: 'text',
+        attr: { value: 'baz' }
+      }
+    }
+
+    const app = { doc, customComponentMap, differ }
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(doc.body.pureChildren.length).eql(4)
+    expect(doc.body.pureChildren.map(el => el.attr.value)).eql(['bar', 'baz', 'bar', 'baz'])
+
+    vm.list.splice(2, 0, { uid: 5, type: 'baz' })
+    setTimeout(() => {
+      expect(doc.body.pureChildren.length).eql(5)
+      expect(doc.body.pureChildren.map(el => el.attr.value)).eql(['bar', 'baz', 'baz', 'bar', 'baz'])
+
+      done()
+    })
+  })
+})
+
+describe('generate dom actions', () => {
+  let doc, app, spy, customComponentMap, differ
+
+  beforeEach(() => {
+    spy = sinon.spy()
+    doc = new Document('foo', '', function (actions) {
+      actions.forEach((action) => {
+        spy.apply(null, ['bar', action.method].concat(action.args))
+      })
+    })
+    differ = new Differ('foo')
+    customComponentMap = {}
+    app = { doc, customComponentMap, differ }
+  })
+
+  afterEach(() => {
+    customComponentMap = {}
+    doc.destroy()
+    app = doc = spy = null
+  })
+
+  it('received create body and add element actions', () => {
+    const handler = sinon.spy()
+
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        attr: {
+          a: 1,
+          b: 2
+        },
+        style: {
+          c: 3,
+          d: 4
+        },
+        classList: ['classA'],
+        events: { click: 'handleClick' }
+      },
+      style: {
+        classA: { d: 5, e: 6 }
+      },
+      methods: {
+        handleClick: handler
+      }
+    }
+
+    new Vm('foo', customComponentMap.foo, { _app: app })
+    const el = {
+      ref: '_root',
+      type: 'container',
+      attr: { a: 1, b: 2 }, style: { c: 3, d: 4, e: 6 },
+      event: ['click']
+    }
+
+    expect(spy.args.length).eql(1)
+    expect(spy.args[0]).eql(['bar', 'createBody', el])
+    expect(doc.body.event.click).is.a.function
+    expect(handler.args.length).eql(0)
+    doc.body.fireEvent('click')
+    expect(handler.args.length).eql(0)
+  })
+
+  it('received actions for a template with if & repeat', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [
+          { type: 'prev' },
+          {
+            shown: function () { return this.x % 2 === 0 },
+            repeat: function () { return this.list },
+            trackBy: 'uid',
+            type: 'image', attr: { src: function () { return this.x } }
+          },
+          { type: 'next' }
+        ]
+      },
+      data: {
+        x: '<some image url>',
+        list: [
+          { uid: 1, x: 1 }, { uid: 2, x: 2 }, { uid: 3 }
+        ]
+      }
+    }
+
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    const pureChildren = doc.body.pureChildren
+    const el = { ref: '_root', type: 'container', attr: {}, style: {}}
+    const prev = {
+      ref: pureChildren[0].ref,
+      type: 'prev', attr: {}, style: {}
+    }
+    const img = {
+      ref: pureChildren[1].ref,
+      type: 'image', attr: { src: 2 }, style: {}
+    }
+    const next = {
+      ref: pureChildren[2].ref,
+      type: 'next', attr: {}, style: {}
+    }
+
+    expect(spy.args.length).eql(4)
+    expect(spy.args[0]).eql(['bar', 'createBody', el])
+    expect(spy.args[1]).eql(['bar', 'addElement', '_root', prev, -1])
+    expect(spy.args[2]).eql(['bar', 'addElement', '_root', img, 1])
+    expect(spy.args[3]).eql(['bar', 'addElement', '_root', next, -1])
+
+    vm.list[1].x = 3
+    differ.flush()
+
+    // [1, 3, undefined]
+    expect(spy.args.length).eql(5)
+    expect(spy.args[4]).eql(['bar', 'removeElement', img.ref])
+
+    vm.list[1].x = 12
+    differ.flush()
+
+    // [1, !12, undefined]
+    expect(spy.args.length).eql(6)
+    img.ref = pureChildren[1].ref
+    img.attr.src = 12
+    expect(spy.args[5]).eql(['bar', 'addElement', '_root', img, 1])
+
+    vm.x = 'other string value'
+    differ.flush()
+
+    expect(spy.args.length).eql(6)
+
+    vm.list.push({ uid: 4, x: 4 })
+    vm.list.push({ uid: 5 }, { uid: 6, x: 6 })
+    differ.flush()
+
+    // [1, !12, undefined, !4, undefined, !6]
+    const img2 = {
+      ref: pureChildren[2].ref,
+      type: 'image', attr: { src: 4 }, style: {}
+    }
+    const img3 = {
+      ref: pureChildren[3].ref,
+      type: 'image', attr: { src: 6 }, style: {}
+    }
+    expect(spy.args.length).eql(8)
+    expect(spy.args[6]).eql(['bar', 'addElement', '_root', img2, 2])
+    expect(spy.args[7]).eql(['bar', 'addElement', '_root', img3, 3])
+
+    const temp1 = vm.list[1] // 12
+    const temp2 = vm.list[5] // 6
+    // vm.list.splice(0, 6, temp2, {uid: 7, x: 7}, temp1)
+    vm.list = []
+    vm.list.push(temp2, { uid: 7, x: 7 }, temp1)
+    differ.flush()
+
+    // [!6, 7, !12]
+    expect(spy.args.length).eql(10)
+    expect(spy.args[8]).eql(['bar', 'removeElement', img2.ref])
+    expect(spy.args[9]).eql(['bar', 'moveElement', img3.ref, '_root', 1])
+  })
+
+  it('received actions for element updates', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container', attr: { a: 1, b: 2 }, style: { c: 3, d: 4 },
+        children: [
+          { type: 'image', attr: { src: function () { return this.x } }},
+          { type: 'text', attr: { value: function () { return this.y } }}
+        ]
+      },
+      data: {
+        x: '<some image url>', y: '<some text content>'
+      }
+    }
+
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+    const pureChildren = doc.body.pureChildren
+    const length = spy.args.length
+
+    vm.x = '<some image url>'
+    differ.flush()
+    expect(spy.args.length - length).eql(0)
+
+    vm.x = 'other string value'
+    differ.flush()
+    const change = [
+      'bar', 'updateAttrs',
+      pureChildren[0].ref,
+      { src: 'other string value' }
+    ]
+    expect(spy.args.length - length).eql(1)
+    expect(spy.args[length]).eql(change)
+  })
+
+  it('received no action when no virtual dom different', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container', children: [
+          {
+            type: 'text',
+            shown: function () { return this.name.length > 3 },
+            attr: {
+              value: function () { return this.name.toUpperCase() }
+            }
+          }
+        ]
+      },
+      data: {
+        name: 'Mike'
+      }
+    }
+
+    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
+
+    expect(doc.body.pureChildren.length).eql(1)
+    const text = doc.body.pureChildren[0]
+    expect(text.attr.value).eql('MIKE')
+
+    const initCalls = spy.args.length
+
+    vm.name = 'MiKe'
+    differ.flush()
+
+    expect(spy.args.length).eql(initCalls)
+
+    text.setAttr('value', 'MIKE')
+    differ.flush()
+
+    expect(spy.args.length).eql(initCalls)
+
+    text.setAttr('value', 'STEVE')
+    differ.flush()
+
+    expect(spy.args.length).eql(initCalls + 1)
+    expect(spy.args[initCalls]).eql([
+      'bar', 'updateAttrs', text.ref, { value: 'STEVE' }])
+
+    vm.name = 'Steve'
+    differ.flush()
+
+    expect(spy.args.length).eql(initCalls + 1)
+  })
+
+  it('received actions for components', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'container',
+        children: [{ type: 'bar', component: true }]
+      }
+    }
+    customComponentMap.bar = {
+      template: {
+        type: 'container',
+        children: [{ type: 'aaa' }, { type: 'bbb' }]
+      }
+    }
+
+    new Vm('foo', customComponentMap.foo, { _app: app })
+    const pureChildren = doc.body.pureChildren
+    const first = pureChildren[0]
+    const second = first.pureChildren[0]
+    const third = first.pureChildren[1]
+    expect(spy.args.length).eql(4)
+    let el = { ref: '_root', type: 'container', attr: {}, style: {}}
+    expect(spy.args[0]).eql(['bar', 'createBody', el])
+    el = { ref: first.ref, type: 'container', attr: {}, style: {}}
+    expect(spy.args[1]).eql(['bar', 'addElement', '_root', el, -1])
+    el = { ref: second.ref, type: 'aaa', attr: {}, style: {}}
+    expect(spy.args[2]).eql(['bar', 'addElement', first.ref, el, -1])
+    el = { ref: third.ref, type: 'bbb', attr: {}, style: {}}
+    expect(spy.args[3]).eql(['bar', 'addElement', first.ref, el, -1])
+  })
+
+  it('received actions for complicated components', () => {
+    customComponentMap.foo = {
+      data: () => {
+        return { list: [{ uid: 1, a: 1 }, { uid: 2, a: 2 }, { uid: 3, a: 3 }] }
+      },
+      template: {
+        type: 'container',
+        children: [{ type: 'bar', component: true, attr: {
+          x: function () { return this.a },
+          y: function () { return this.b }
+        }, repeat: function () { return this.list } }]
+      }
+    }
+    customComponentMap.bar = {
+      props: { x: Number },
+      data: () => {
+        return { x: 1, y: 2 }
+      },
+      template: {
+        type: 'container',
+        children: [
+          { type: 'aaa', attr: { a: function () { return this.x } }},
+          { type: 'bbb', attr: { b: function () { return this.y } }}
+        ]
+      }
+    }
+
+    new Vm('foo', customComponentMap.foo, { _app: app })
+    const pureChildren = doc.body.pureChildren
+    const first = pureChildren[0]
+    const second = pureChildren[1]
+    const third = pureChildren[2]
+
+    /* eslint-disable indent */
+    // expect(spy.args[0]).eql([ 'bar', 'createBody', 'container' ])
+    expect(spy.args[0]).eql(['bar', 'createBody', { ref: '_root', type: 'container', attr: {}, style: {}}])
+      expect(spy.args[1]).eql(['bar', 'addElement', '_root', { ref: first.ref, type: 'container', attr: {}, style: {}}, 0])
+        expect(spy.args[2]).eql(['bar', 'addElement', first.ref, { ref: first.pureChildren[0].ref, type: 'aaa', attr: { a: 1 }, style: {}}, -1])
+        expect(spy.args[3]).eql(['bar', 'addElement', first.ref, { ref: first.pureChildren[1].ref, type: 'bbb', attr: { b: 2 }, style: {}}, -1])
+      expect(spy.args[4]).eql(['bar', 'addElement', '_root', { ref: second.ref, type: 'container', attr: {}, style: {}}, 1])
+        expect(spy.args[5]).eql(['bar', 'addElement', second.ref, { ref: second.pureChildren[0].ref, type: 'aaa', attr: { a: 2 }, style: {}}, -1])
+        expect(spy.args[6]).eql(['bar', 'addElement', second.ref, { ref: second.pureChildren[1].ref, type: 'bbb', attr: { b: 2 }, style: {}}, -1])
+      expect(spy.args[7]).eql(['bar', 'addElement', '_root', { ref: third.ref, type: 'container', attr: {}, style: {}}, 2])
+        expect(spy.args[8]).eql(['bar', 'addElement', third.ref, { ref: third.pureChildren[0].ref, type: 'aaa', attr: { a: 3 }, style: {}}, -1])
+        expect(spy.args[9]).eql(['bar', 'addElement', third.ref, { ref: third.pureChildren[1].ref, type: 'bbb', attr: { b: 2 }, style: {}}, -1])
+    /* eslint-enable indent */
+  })
+
+  it('received actions for add a tree element', () => {
+    customComponentMap.foo = {
+      template: {
+        type: 'r',
+        children: [
+          { type: 'a' }, { type: 'b', children: [
+            { type: 'd' }, { type: 'e', append: 'tree', children: [
+              { type: 'g' }, { type: 'h' }, { type: 'i' }
+            ] }, { type: 'f' }
+          ] }, { type: 'c' }
+        ]
+      }
+    }
+
+    new Vm('foo', customComponentMap.foo, { _app: app })
+    const pureChildren = doc.body.pureChildren
+
+    expect(spy.args.length).eql(7)
+    // body, r, r.a, r.b, r.b.d, r.b.e(tree), r.b.f, r.c
+
+    expect(spy.args[0][2].ref).eql('_root')
+    expect(spy.args[0][2].type).eql('r')
+
+    expect(spy.args[1][3].ref).eql(pureChildren[0].ref)
+    expect(spy.args[1][3].type).eql('a')
+    expect(spy.args[1][2]).eql('_root')
+    expect(spy.args[1][4]).eql(-1)
+
+    expect(spy.args[2][3].ref).eql(pureChildren[1].ref)
+    expect(spy.args[2][3].type).eql('b')
+    expect(spy.args[2][2]).eql('_root')
+    expect(spy.args[2][4]).eql(-1)
+
+    expect(spy.args[3][3].ref).eql(pureChildren[1].pureChildren[0].ref)
+    expect(spy.args[3][3].type).eql('d')
+    expect(spy.args[3][2]).eql(pureChildren[1].ref)
+    expect(spy.args[3][4]).eql(-1)
+
+    const tree = pureChildren[1].pureChildren[1]
+    expect(spy.args[4][3].ref).eql(tree.ref)
+    expect(spy.args[4][3].type).eql('e')
+    expect(spy.args[4][2]).eql(pureChildren[1].ref)
+    expect(spy.args[4][4]).eql(-1)
+    expect(spy.args[4][3].children).eql([
+      { ref: tree.pureChildren[0].ref, type: 'g', attr: {}, style: {}},
+      { ref: tree.pureChildren[1].ref, type: 'h', attr: {}, style: {}},
+      { ref: tree.pureChildren[2].ref, type: 'i', attr: {}, style: {}}])
+
+    expect(spy.args[5][3].ref).eql(pureChildren[1].pureChildren[2].ref)
+    expect(spy.args[5][3].type).eql('f')
+    expect(spy.args[5][2]).eql(pureChildren[1].ref)
+    expect(spy.args[5][4]).eql(-1)
+
+    expect(spy.args[6][3].ref).eql(pureChildren[2].ref)
+    expect(spy.args[6][3].type).eql('c')
+    expect(spy.args[6][2]).eql('_root')
+    expect(spy.args[6][4]).eql(-1)
+  })
+})



[08/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/vm/directive.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/vm/directive.js b/runtime/frameworks/legacy/vm/directive.js
new file mode 100644
index 0000000..77ccd9a
--- /dev/null
+++ b/runtime/frameworks/legacy/vm/directive.js
@@ -0,0 +1,377 @@
+/*
+ * 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
+ * Directive Parser
+ */
+
+import { bind, typof } from '../util/index'
+import Watcher from '../core/watcher'
+import config from '../config'
+
+const { nativeComponentMap } = config
+
+const SETTERS = {
+  attr: 'setAttr',
+  style: 'setStyle',
+  event: 'addEvent'
+}
+
+/**
+ * apply the native component's options(specified by template.type)
+ * to the template
+ */
+export function applyNaitveComponentOptions (template) {
+  const { type } = template
+  const options = nativeComponentMap[type]
+
+  if (typeof options === 'object') {
+    for (const key in options) {
+      if (template[key] == null) {
+        template[key] = options[key]
+      }
+      else if (typof(template[key]) === 'object' &&
+        typof(options[key]) === 'object') {
+        for (const subkey in options[key]) {
+          if (template[key][subkey] == null) {
+            template[key][subkey] = options[key][subkey]
+          }
+        }
+      }
+    }
+  }
+}
+
+/**
+ * bind all id, attr, classnames, style, events to an element
+ */
+export function bindElement (vm, el, template) {
+  setId(vm, el, template.id, vm)
+  setAttr(vm, el, template.attr)
+  setClass(vm, el, template.classList)
+  setStyle(vm, el, template.style)
+  bindEvents(vm, el, template.events)
+}
+
+/**
+ * bind all props to sub vm and bind all style, events to the root element
+ * of the sub vm if it doesn't have a replaced multi-node fragment
+ */
+export function bindSubVm (vm, subVm, template, repeatItem) {
+  subVm = subVm || {}
+  template = template || {}
+
+  const options = subVm._options || {}
+
+  // bind props
+  let props = options.props
+
+  if (Array.isArray(props)) {
+    props = props.reduce((result, value) => {
+      result[value] = true
+      return result
+    }, {})
+  }
+
+  mergeProps(repeatItem, props, vm, subVm)
+  mergeProps(template.attr, props, vm, subVm)
+}
+
+/**
+ * merge class and styles from vm to sub vm.
+ */
+export function bindSubVmAfterInitialized (vm, subVm, template, target = {}) {
+  mergeClassStyle(template.classList, vm, subVm)
+  mergeStyle(template.style, vm, subVm)
+
+  // bind subVm to the target element
+  if (target.children) {
+    target.children[target.children.length - 1]._vm = subVm
+  }
+  else {
+    target._vm = subVm
+  }
+}
+
+/**
+ * Bind props from vm to sub vm and watch their updates.
+ */
+function mergeProps (target, props, vm, subVm) {
+  if (!target) {
+    return
+  }
+  for (const key in target) {
+    if (!props || props[key]) {
+      const value = target[key]
+      if (typeof value === 'function') {
+        const returnValue = watch(vm, value, function (v) {
+          subVm[key] = v
+        })
+        subVm[key] = returnValue
+      }
+      else {
+        subVm[key] = value
+      }
+    }
+  }
+}
+
+/**
+ * Bind style from vm to sub vm and watch their updates.
+ */
+function mergeStyle (target, vm, subVm) {
+  for (const key in target) {
+    const value = target[key]
+    if (typeof value === 'function') {
+      const returnValue = watch(vm, value, function (v) {
+        if (subVm._rootEl) {
+          subVm._rootEl.setStyle(key, v)
+        }
+      })
+      subVm._rootEl.setStyle(key, returnValue)
+    }
+    else {
+      if (subVm._rootEl) {
+        subVm._rootEl.setStyle(key, value)
+      }
+    }
+  }
+}
+
+/**
+ * Bind class & style from vm to sub vm and watch their updates.
+ */
+function mergeClassStyle (target, vm, subVm) {
+  const css = vm._options && vm._options.style || {}
+
+  /* istanbul ignore if */
+  if (!subVm._rootEl) {
+    return
+  }
+
+  const className = '@originalRootEl'
+  css[className] = subVm._rootEl.classStyle
+
+  function addClassName (list, name) {
+    if (typof(list) === 'array') {
+      list.unshift(name)
+    }
+  }
+
+  if (typeof target === 'function') {
+    const value = watch(vm, target, v => {
+      addClassName(v, className)
+      setClassStyle(subVm._rootEl, css, v)
+    })
+    addClassName(value, className)
+    setClassStyle(subVm._rootEl, css, value)
+  }
+  else if (target != null) {
+    addClassName(target, className)
+    setClassStyle(subVm._rootEl, css, target)
+  }
+}
+
+/**
+ * bind id to an element
+ * each id is unique in a whole vm
+ */
+export function setId (vm, el, id, target) {
+  const map = Object.create(null)
+
+  Object.defineProperties(map, {
+    vm: {
+      value: target,
+      writable: false,
+      configurable: false
+    },
+    el: {
+      get: () => el || target._rootEl,
+      configurable: false
+    }
+  })
+
+  if (typeof id === 'function') {
+    const handler = id
+    id = handler.call(vm)
+    if (id || id === 0) {
+      vm._ids[id] = map
+    }
+    watch(vm, handler, (newId) => {
+      if (newId) {
+        vm._ids[newId] = map
+      }
+    })
+  }
+  else if (id && typeof id === 'string') {
+    vm._ids[id] = map
+  }
+}
+
+/**
+ * bind attr to an element
+ */
+function setAttr (vm, el, attr) {
+  bindDir(vm, el, 'attr', attr)
+}
+
+function setClassStyle (el, css, classList) {
+  if (typeof classList === 'string') {
+    classList = classList.split(/\s+/)
+  }
+  classList.forEach((name, i) => {
+    classList.splice(i, 1, ...name.split(/\s+/))
+  })
+  const classStyle = {}
+  const length = classList.length
+
+  for (let i = 0; i < length; i++) {
+    const style = css[classList[i]]
+    if (style) {
+      Object.keys(style).forEach((key) => {
+        classStyle[key] = style[key]
+      })
+    }
+  }
+  el.setClassStyle(classStyle)
+}
+
+/**
+ * bind classnames to an element
+ */
+function setClass (vm, el, classList) {
+  if (typeof classList !== 'function' && !Array.isArray(classList)) {
+    return
+  }
+  if (Array.isArray(classList) && !classList.length) {
+    el.setClassStyle({})
+    return
+  }
+
+  const style = vm._options && vm._options.style || {}
+  if (typeof classList === 'function') {
+    const value = watch(vm, classList, v => {
+      setClassStyle(el, style, v)
+    })
+    setClassStyle(el, style, value)
+  }
+  else {
+    setClassStyle(el, style, classList)
+  }
+}
+
+/**
+ * bind style to an element
+ */
+function setStyle (vm, el, style) {
+  bindDir(vm, el, 'style', style)
+}
+
+/**
+ * add an event type and handler to an element and generate a dom update
+ */
+function setEvent (vm, el, type, handler) {
+  el.addEvent(type, bind(handler, vm))
+}
+
+/**
+ * add all events of an element
+ */
+function bindEvents (vm, el, events) {
+  if (!events) {
+    return
+  }
+  const keys = Object.keys(events)
+  let i = keys.length
+  while (i--) {
+    const key = keys[i]
+    let handler = events[key]
+    if (typeof handler === 'string') {
+      handler = vm[handler]
+      /* istanbul ignore if */
+      if (!handler) {
+        console.warn(`[JS Framework] The event handler "${handler}" is not defined.`)
+      }
+    }
+    setEvent(vm, el, key, handler)
+  }
+}
+
+/**
+ * set a series of members as a kind of an element
+ * for example: style, attr, ...
+ * if the value is a function then bind the data changes
+ */
+function bindDir (vm, el, name, data) {
+  if (!data) {
+    return
+  }
+  const keys = Object.keys(data)
+  let i = keys.length
+  while (i--) {
+    const key = keys[i]
+    const value = data[key]
+    if (typeof value === 'function') {
+      bindKey(vm, el, name, key, value)
+    }
+    else {
+      el[SETTERS[name]](key, value)
+    }
+  }
+}
+
+/**
+ * bind data changes to a certain key to a name series in an element
+ */
+function bindKey (vm, el, name, key, calc) {
+  const methodName = SETTERS[name]
+  // watch the calc, and returns a value by calc.call()
+  const value = watch(vm, calc, (value) => {
+    function handler () {
+      el[methodName](key, value)
+    }
+    const differ = vm && vm._app && vm._app.differ
+    if (differ) {
+      differ.append('element', el.depth || 0, el.ref, handler)
+    }
+    else {
+      handler()
+    }
+  })
+
+  el[methodName](key, value)
+}
+
+/**
+ * watch a calc function and callback if the calc value changes
+ */
+export function watch (vm, calc, callback) {
+  if (vm._static) {
+    return calc.call(vm, vm)
+  }
+  const watcher = new Watcher(vm, calc, function (value, oldValue) {
+    /* istanbul ignore if */
+    if (typeof value !== 'object' && value === oldValue) {
+      return
+    }
+    callback(value)
+  })
+
+  return watcher.value
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/vm/dom-helper.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/vm/dom-helper.js b/runtime/frameworks/legacy/vm/dom-helper.js
new file mode 100644
index 0000000..6e5a800
--- /dev/null
+++ b/runtime/frameworks/legacy/vm/dom-helper.js
@@ -0,0 +1,258 @@
+/*
+ * 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 Document & Element Helpers.
+ *
+ * required:
+ * Document#: createElement, createComment, getRef
+ * Element#: appendChild, insertBefore, removeChild, nextSibling
+ */
+
+/**
+ * Create a body by type
+ * Using this._app.doc
+ *
+ * @param  {string} type
+ */
+export function createBody (vm, type) {
+  const doc = vm._app.doc
+  return doc.createBody(type)
+}
+
+/**
+ * Create an element by type
+ * Using this._app.doc
+ *
+ * @param  {string} type
+ */
+export function createElement (vm, type) {
+  const doc = vm._app.doc
+  return doc.createElement(type)
+}
+
+/**
+ * Create and return a frag block for an element.
+ * The frag block has a starter, ender and the element itself.
+ *
+ * @param  {object} element
+ */
+export function createBlock (vm, element) {
+  const start = createBlockStart(vm)
+  const end = createBlockEnd(vm)
+  const blockId = lastestBlockId++
+  if (element.element) {
+    let updateMark = element.updateMark
+    if (updateMark) {
+      if (updateMark.element) {
+        updateMark = updateMark.end
+      }
+      element.element.insertAfter(end, updateMark)
+      element.element.insertAfter(start, updateMark)
+      element.updateMark = end
+    }
+    else {
+      element.element.insertBefore(start, element.end)
+      element.element.insertBefore(end, element.end)
+    }
+    element = element.element
+  }
+  else {
+    element.appendChild(start)
+    element.appendChild(end)
+  }
+  return { start, end, element, blockId }
+}
+
+let lastestBlockId = 1
+
+/**
+ * Create and return a block starter.
+ * Using this._app.doc
+ */
+function createBlockStart (vm) {
+  const doc = vm._app.doc
+  const anchor = doc.createComment('start')
+  return anchor
+}
+
+/**
+ * Create and return a block ender.
+ * Using this._app.doc
+ */
+function createBlockEnd (vm) {
+  const doc = vm._app.doc
+  const anchor = doc.createComment('end')
+  return anchor
+}
+
+/**
+ * Attach target to a certain dest using appendChild by default.
+ * If the dest is a frag block then insert before the ender.
+ * If the target is a frag block then attach the starter and ender in order.
+ *
+ * @param  {object} target
+ * @param  {object} dest
+ */
+export function attachTarget (vm, target, dest) {
+  if (dest.element) {
+    const before = dest.end
+    const after = dest.updateMark
+    // push new target for watch list update later
+    if (dest.children) {
+      dest.children.push(target)
+    }
+    // for check repeat case
+    if (after) {
+      const signal = moveTarget(vm, target, after)
+      dest.updateMark = target.element ? target.end : target
+      return signal
+    }
+    else if (target.element) {
+      dest.element.insertBefore(target.start, before)
+      dest.element.insertBefore(target.end, before)
+    }
+    else {
+      return dest.element.insertBefore(target, before)
+    }
+  }
+  else {
+    if (target.element) {
+      dest.appendChild(target.start)
+      dest.appendChild(target.end)
+    }
+    else {
+      return dest.appendChild(target)
+    }
+  }
+}
+
+/**
+ * Move target before a certain element. The target maybe block or element.
+ *
+ * @param  {object} target
+ * @param  {object} before
+ */
+export function moveTarget (vm, target, after) {
+  if (target.element) {
+    return moveBlock(target, after)
+  }
+  return moveElement(target, after)
+}
+
+/**
+ * Move element before a certain element.
+ *
+ * @param  {object} element
+ * @param  {object} before
+ */
+function moveElement (element, after) {
+  const parent = after.parentNode
+  if (parent) {
+    return parent.insertAfter(element, after)
+  }
+}
+
+/**
+ * Move all elements of the block before a certain element.
+ *
+ * @param  {object} fragBlock
+ * @param  {object} before
+ */
+function moveBlock (fragBlock, after) {
+  const parent = after.parentNode
+
+  if (parent) {
+    let el = fragBlock.start
+    let signal
+    const group = [el]
+
+    while (el && el !== fragBlock.end) {
+      el = el.nextSibling
+      group.push(el)
+    }
+
+    let temp = after
+    group.every((el) => {
+      signal = parent.insertAfter(el, temp)
+      temp = el
+      return signal !== -1
+    })
+
+    return signal
+  }
+}
+
+/**
+ * Remove target from DOM tree.
+ * If the target is a frag block then call _removeBlock
+ *
+ * @param  {object} target
+ */
+export function removeTarget (vm, target, preserveBlock = false) {
+  if (target.element) {
+    removeBlock(target, preserveBlock)
+  }
+  else {
+    removeElement(target)
+  }
+  if (target._vm) {
+    target._vm.$emit('hook:destroyed')
+  }
+}
+
+/**
+ * Remove a certain element.
+ * Using this._app.doc
+ *
+ * @param  {object} target
+ */
+function removeElement (target) {
+  const parent = target.parentNode
+
+  if (parent) {
+    parent.removeChild(target)
+  }
+}
+
+/**
+ * Remove a frag block.
+ * The second param decides whether the block self should be removed too.
+ *
+ * @param  {object}  fragBlock
+ * @param  {Boolean} preserveBlock=false
+ */
+function removeBlock (fragBlock, preserveBlock = false) {
+  const result = []
+  let el = fragBlock.start.nextSibling
+
+  while (el && el !== fragBlock.end) {
+    result.push(el)
+    el = el.nextSibling
+  }
+
+  if (!preserveBlock) {
+    removeElement(fragBlock.start)
+  }
+  result.forEach((el) => {
+    removeElement(el)
+  })
+  if (!preserveBlock) {
+    removeElement(fragBlock.end)
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/vm/events.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/vm/events.js b/runtime/frameworks/legacy/vm/events.js
new file mode 100644
index 0000000..5691472
--- /dev/null
+++ b/runtime/frameworks/legacy/vm/events.js
@@ -0,0 +1,179 @@
+/*
+ * 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
+ * Everything about component event which includes event object, event listener,
+ * event emitter and lifecycle hooks.
+ */
+
+/**
+ * Event object definition. An event object has `type`, `timestamp` and
+ * `detail` from which a component emit. The event object could be dispatched to
+ * parents or broadcasted to children except `this.stop()` is called.
+ * @param {string} type
+ * @param {any}    detail
+ */
+function Evt (type, detail) {
+  if (detail instanceof Evt) {
+    return detail
+  }
+
+  this.timestamp = Date.now()
+  this.detail = detail
+  this.type = type
+
+  let shouldStop = false
+
+  /**
+   * stop dispatch and broadcast
+   */
+  this.stop = function () {
+    shouldStop = true
+  }
+
+  /**
+   * check if it can't be dispatched or broadcasted
+   */
+  this.hasStopped = function () {
+    return shouldStop
+  }
+}
+
+/**
+ * Emit an event but not broadcast down or dispatch up.
+ * @param  {string} type
+ * @param  {any}    detail
+ */
+export function $emit (type, detail) {
+  const events = this._vmEvents
+  const handlerList = events[type]
+  if (handlerList) {
+    const evt = new Evt(type, detail)
+    handlerList.forEach((handler) => {
+      handler.call(this, evt)
+    })
+  }
+}
+
+/**
+ * Emit an event and dispatch it up.
+ * @param  {string} type
+ * @param  {any}    detail
+ */
+export function $dispatch (type, detail) {
+  const evt = new Evt(type, detail)
+  this.$emit(type, evt)
+
+  if (!evt.hasStopped() && this._parent && this._parent.$dispatch) {
+    this._parent.$dispatch(type, evt)
+  }
+}
+
+/**
+ * Emit an event and broadcast it down.
+ * @param  {string} type
+ * @param  {any}    detail
+ */
+export function $broadcast (type, detail) {
+  const evt = new Evt(type, detail)
+  this.$emit(type, evt)
+
+  if (!evt.hasStopped() && this._childrenVms) {
+    this._childrenVms.forEach((subVm) => {
+      subVm.$broadcast(type, evt)
+    })
+  }
+}
+
+/**
+ * Add event listener.
+ * @param  {string}   type
+ * @param  {function} handler
+ */
+export function $on (type, handler) {
+  if (!type || typeof handler !== 'function') {
+    return
+  }
+  const events = this._vmEvents
+  const handlerList = events[type] || []
+  handlerList.push(handler)
+  events[type] = handlerList
+
+  // fixed old version lifecycle design
+  /* istanbul ignore if */
+  if (type === 'hook:ready' && this._ready) {
+    this.$emit('hook:ready')
+  }
+}
+
+/**
+ * Remove event listener.
+ * @param  {string}   type
+ * @param  {function} handler
+ */
+export function $off (type, handler) {
+  if (!type) {
+    return
+  }
+  const events = this._vmEvents
+  if (!handler) {
+    delete events[type]
+    return
+  }
+  const handlerList = events[type]
+  if (!handlerList) {
+    return
+  }
+  handlerList.$remove(handler)
+}
+
+const LIFE_CYCLE_TYPES = ['init', 'created', 'ready', 'destroyed']
+
+/**
+ * Init events:
+ * 1. listen `events` in component options & `externalEvents`.
+ * 2. bind lifecycle hooks.
+ * @param  {Vm}     vm
+ * @param  {object} externalEvents
+ */
+export function initEvents (vm, externalEvents) {
+  const options = vm._options || {}
+  const events = options.events || {}
+  for (const type1 in events) {
+    vm.$on(type1, events[type1])
+  }
+  for (const type2 in externalEvents) {
+    vm.$on(type2, externalEvents[type2])
+  }
+  LIFE_CYCLE_TYPES.forEach((type) => {
+    vm.$on(`hook:${type}`, options[type])
+  })
+}
+
+/**
+ * Bind event related methods to ViewModel instance.
+ * @param  {Vm} vm
+ */
+export function mixinEvents (vm) {
+  vm.$emit = $emit
+  vm.$dispatch = $dispatch
+  vm.$broadcast = $broadcast
+  vm.$on = $on
+  vm.$off = $off
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/vm/index.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/vm/index.js b/runtime/frameworks/legacy/vm/index.js
new file mode 100644
index 0000000..45a33af
--- /dev/null
+++ b/runtime/frameworks/legacy/vm/index.js
@@ -0,0 +1,131 @@
+/*
+ * 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
+ * ViewModel Constructor & definition
+ */
+
+import { extend } from '../util/index'
+import {
+  initState
+} from '../core/state'
+import {
+  build
+} from './compiler'
+import {
+  set,
+  del
+} from '../core/observer'
+import {
+  watch
+} from './directive'
+import {
+  initEvents,
+  mixinEvents
+} from './events'
+
+/**
+ * ViewModel constructor
+ *
+ * @param {string} type
+ * @param {object} options    component options
+ * @param {object} parentVm   which contains _app
+ * @param {object} parentEl   root element or frag block
+ * @param {object} mergedData external data
+ * @param {object} externalEvents external events
+ */
+export default function Vm (
+  type,
+  options,
+  parentVm,
+  parentEl,
+  mergedData,
+  externalEvents
+) {
+  parentVm = parentVm || {}
+  this._parent = parentVm._realParent ? parentVm._realParent : parentVm
+  this._app = parentVm._app || {}
+  parentVm._childrenVms && parentVm._childrenVms.push(this)
+
+  if (!options && this._app.customComponentMap) {
+    options = this._app.customComponentMap[type]
+  }
+  options = options || {}
+
+  const data = options.data || {}
+
+  this._options = options
+  this._methods = options.methods || {}
+  this._computed = options.computed || {}
+  this._css = options.style || {}
+  this._ids = {}
+  this._vmEvents = {}
+  this._childrenVms = []
+  this._type = type
+
+  // bind events and lifecycles
+  initEvents(this, externalEvents)
+
+  console.debug(`[JS Framework] "init" lifecycle in Vm(${this._type})`)
+  this.$emit('hook:init')
+  this._inited = true
+
+  // proxy data and methods
+  // observe data and add this to vms
+  this._data = typeof data === 'function' ? data() : data
+  if (mergedData) {
+    extend(this._data, mergedData)
+  }
+  initState(this)
+
+  console.debug(`[JS Framework] "created" lifecycle in Vm(${this._type})`)
+  this.$emit('hook:created')
+  this._created = true
+
+  // backward old ready entry
+  if (options.methods && options.methods.ready) {
+    console.warn('"exports.methods.ready" is deprecated, ' +
+      'please use "exports.created" instead')
+    options.methods.ready.call(this)
+  }
+
+  if (!this._app.doc) {
+    return
+  }
+
+  // if no parentElement then specify the documentElement
+  this._parentEl = parentEl || this._app.doc.documentElement
+  build(this)
+}
+
+mixinEvents(Vm.prototype)
+
+/**
+ * Watch an function and bind all the data appeared in it. When the related
+ * data changes, the callback will be called with new value as 1st param.
+ *
+ * @param {Function} fn
+ * @param {Function} callback
+ */
+Vm.prototype.$watch = function (fn, callback) {
+  watch(this, fn, callback)
+}
+
+Vm.set = set
+Vm.delete = del

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/vanilla/index.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/vanilla/index.js b/runtime/frameworks/vanilla/index.js
new file mode 100644
index 0000000..f3b4bc2
--- /dev/null
+++ b/runtime/frameworks/vanilla/index.js
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+const config = {}
+
+const instanceMap = {}
+
+export function init (cfg) {
+  config.Document = cfg.Document
+  config.Element = cfg.Element
+  config.Comment = cfg.Comment
+  config.sendTasks = cfg.sendTasks
+}
+
+export function createInstance (id, code, options = {}, data = {}, serviceObjects = {}) {
+  const document = new config.Document(id, options.bundleUrl)
+  const callbacks = {}
+  const instance = { id, data, document, callbacks }
+
+  let lastCallbackId = 0
+  document.addCallback = func => {
+    lastCallbackId++
+    callbacks[lastCallbackId] = func
+    return lastCallbackId
+  }
+  document.handleCallback = (funcId, data, ifLast) => {
+    const callback = callbacks[funcId]
+    if (ifLast) {
+      delete callbacks[funcId]
+    }
+    return callback(data)
+  }
+  instanceMap[id] = instance
+
+  const globalObjects = Object.assign({
+    Document: config.Document,
+    Element: config.Element,
+    Comment: config.Comment,
+    sendTasks: tasks => config.sendTasks(id, tasks, -1),
+    options,
+    document
+  }, serviceObjects)
+
+  const globalKeys = []
+  const globalValues = []
+  for (const key in globalObjects) {
+    globalKeys.push(key)
+    globalValues.push(globalObjects[key])
+  }
+  globalKeys.push(code)
+
+  const result = new Function(...globalKeys)
+  result(...globalValues)
+
+  config.sendTasks(id, [{ module: 'dom', method: 'createFinish', args: [] }], -1)
+
+  return instance
+}
+
+export function destroyInstance (id) {
+  delete instanceMap[id]
+}
+
+export function getRoot (id) {
+  return instanceMap[id].document.body.toJSON()
+}
+
+export function receiveTasks (id, tasks) {
+  const jsHandlers = {
+    fireEvent: (id, ref, type, data, domChanges) => {
+      const { document } = instanceMap[id]
+      const el = document.getRef(ref)
+      return document.fireEvent(el, type, data, domChanges)
+    },
+
+    callback: (id, funcId, data, ifLast) => {
+      const { document } = instanceMap[id]
+      return document.handleCallback(funcId, data, ifLast)
+    }
+  }
+
+  const { document } = instanceMap[id] || {}
+  if (document && Array.isArray(tasks)) {
+    const results = []
+    tasks.forEach((task) => {
+      const handler = jsHandlers[task.method]
+      const args = [...task.args]
+      if (typeof handler === 'function') {
+        args.unshift(id)
+        results.push(handler(...args))
+      }
+    })
+    return results
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/services/amd/index.js
----------------------------------------------------------------------
diff --git a/runtime/services/amd/index.js b/runtime/services/amd/index.js
new file mode 100644
index 0000000..3a7a491
--- /dev/null
+++ b/runtime/services/amd/index.js
@@ -0,0 +1,105 @@
+/*
+ * 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
+ * A simple implementation of AMD for weex.
+ */
+
+/**
+ * amd modules (<id, module> pair)
+ *  - id : instance id.
+ *  - module: {
+ *        name: module name
+ *        , factory: module's factory function
+ *        , cached: cached module,
+ *        , deps: module's dependencies, always be [] under most circumstances.
+ *      }
+ */
+const modules = {}
+
+const amdService = {
+
+  // create a amd service.
+  create: (id, env, config) => {
+    if (!env.framework.match(/Vue/i)) {
+      return
+    }
+
+    const mod = {}
+    modules[id] = mod
+    const amdObject = {
+
+      /**
+       * define a module.
+       * @param  {String} name: module name.
+       * @param  {Array} deps: dependencies. Always be empty array.
+       * @param  {function} factory: factory function.
+       */
+      define (name, deps, factory) {
+        if (mod[name]) {
+          console.warn(`[amdService] already defined module: '${name}'`)
+        }
+        if (typeof deps === 'function') {
+          factory = deps
+          deps = []
+        }
+        mod[name] = { name, factory, cached: false, deps }
+      },
+
+      /**
+       * require a module.
+       * @param  {string} name - module name.
+       */
+      require (name) {
+        const servMod = mod[name]
+        if (!servMod) {
+          return console.warn(`[amdService] module '${name}' is not defined.`)
+        }
+        if (servMod.cached) {
+          return servMod.cached
+        }
+        const exports = {}
+        const module = { exports }
+        const { deps } = servMod
+        let ret
+        if (deps && deps.length >= 1) {
+          /**
+           * to support:
+           *   define(name, ['foo', 'bar'], function (foo, bar) { ... })
+           */
+          const args = deps.map(depName => require(depName))
+          ret = servMod.factory(...args)
+        }
+        else {
+          ret = servMod.factory(amdObject.require, exports, module)
+        }
+        servMod.cached = ret || module.exports
+        return servMod.cached
+      }
+    }
+    return { instance: amdObject }
+  },
+
+  destroy: (id, env) => {
+    delete modules[id]
+  }
+}
+
+export default amdService

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/services/broadcast-channel/index.js
----------------------------------------------------------------------
diff --git a/runtime/services/broadcast-channel/index.js b/runtime/services/broadcast-channel/index.js
new file mode 100644
index 0000000..cc7524e
--- /dev/null
+++ b/runtime/services/broadcast-channel/index.js
@@ -0,0 +1,125 @@
+/*
+ * 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
+ * The polyfill of BroadcastChannel API.
+ * This api can be used to achieve inter-instance communications.
+ *
+ * https://html.spec.whatwg.org/multipage/comms.html#broadcasting-to-other-browsing-contexts
+ */
+
+import { MessageEvent } from './message-event'
+
+const channels = {}
+const instances = {}
+
+/**
+ * An empty constructor for BroadcastChannel polyfill.
+ * The real constructor will be defined when a Weex instance created because
+ * we need to track the channel by Weex instance id.
+ */
+function BroadcastChannel () {}
+
+/**
+ * Sends the given message to other BroadcastChannel objects set up for this channel.
+ * @param {any} message
+ */
+BroadcastChannel.prototype.postMessage = function (message) {
+  if (this._closed) {
+    throw new Error(`BroadcastChannel "${this.name}" is closed.`)
+  }
+
+  const subscribers = channels[this.name]
+  if (subscribers && subscribers.length) {
+    for (let i = 0; i < subscribers.length; ++i) {
+      const member = subscribers[i]
+
+      if (member._closed || member === this) continue
+
+      if (typeof member.onmessage === 'function') {
+        member.onmessage(new MessageEvent('message', { data: message }))
+      }
+    }
+  }
+}
+
+/**
+ * Closes the BroadcastChannel object, opening it up to garbage collection.
+ */
+BroadcastChannel.prototype.close = function () {
+  if (this._closed) {
+    return
+  }
+
+  this._closed = true
+
+  // remove itself from channels.
+  if (channels[this.name]) {
+    const subscribers = channels[this.name].filter(x => x !== this)
+    if (subscribers.length) {
+      channels[this.name] = subscribers
+    }
+    else {
+      delete channels[this.name]
+    }
+  }
+}
+
+export default {
+  create: (id, env, config) => {
+    instances[id] = []
+    if (typeof global.BroadcastChannel === 'function') {
+      return {}
+    }
+    const serviceObject = {
+      /**
+       * Returns a new BroadcastChannel object via which messages for the given
+       * channel name can be sent and received.
+       * @param {string} name
+       */
+      BroadcastChannel: function (name) {
+        // the name property is readonly
+        Object.defineProperty(this, 'name', {
+          configurable: false,
+          enumerable: true,
+          writable: false,
+          value: String(name)
+        })
+
+        this._closed = false
+        this.onmessage = null
+
+        if (!channels[this.name]) {
+          channels[this.name] = []
+        }
+        channels[this.name].push(this)
+        instances[id].push(this)
+      }
+    }
+    serviceObject.BroadcastChannel.prototype = BroadcastChannel.prototype
+    return {
+      instance: serviceObject
+    }
+  },
+  destroy: (id, env) => {
+    instances[id].forEach(channel => channel.close())
+    delete instances[id]
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/services/broadcast-channel/message-event.js
----------------------------------------------------------------------
diff --git a/runtime/services/broadcast-channel/message-event.js b/runtime/services/broadcast-channel/message-event.js
new file mode 100644
index 0000000..00fa36a
--- /dev/null
+++ b/runtime/services/broadcast-channel/message-event.js
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+/**
+ * Mock MessageEvent type
+ * @param {string} type
+ * @param {object} dict { data, origin, source, ports }
+ *
+ * This type has been simplified.
+ * https://html.spec.whatwg.org/multipage/comms.html#messageevent
+ * https://dom.spec.whatwg.org/#interface-event
+ */
+export function MessageEvent (type, dict = {}) {
+  this.type = type || 'message'
+
+  this.data = dict.data || null
+  this.origin = dict.origin || ''
+  this.source = dict.source || null
+  this.ports = dict.ports || []
+
+  // inherit properties
+  this.target = null
+  this.timeStamp = Date.now()
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/services/index.js
----------------------------------------------------------------------
diff --git a/runtime/services/index.js b/runtime/services/index.js
new file mode 100644
index 0000000..52cd97d
--- /dev/null
+++ b/runtime/services/index.js
@@ -0,0 +1,23 @@
+/*
+ * 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 BroadcastChannel from './broadcast-channel/index'
+
+export default {
+  BroadcastChannel
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/shared/env/console.js
----------------------------------------------------------------------
diff --git a/runtime/shared/env/console.js b/runtime/shared/env/console.js
new file mode 100644
index 0000000..eb54815
--- /dev/null
+++ b/runtime/shared/env/console.js
@@ -0,0 +1,137 @@
+/*
+ * 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
+ * This file will hack `console` methods by `WXEnvironment.logLevel`.
+ * So we can control how many and which messages will be sent by change the log level.
+ * Additionally in native platform the message content must be primitive values and
+ * using `nativeLog(...args, logLevelMark)` so we create a new `console` object in
+ * global add a format process for its methods.
+ */
+
+const LEVELS = ['off', 'error', 'warn', 'info', 'log', 'debug']
+let levelMap = {}
+
+const originalConsole = global.console
+
+/**
+ * Hack console for native environment.
+ */
+export function setNativeConsole () {
+  generateLevelMap()
+
+  /* istanbul ignore next */
+  // mock console in native environment
+  if (global.WXEnvironment && global.WXEnvironment.platform !== 'Web') {
+    global.console = {
+      debug: (...args) => {
+        if (checkLevel('debug')) { global.nativeLog(...format(args), '__DEBUG') }
+      },
+      log: (...args) => {
+        if (checkLevel('log')) { global.nativeLog(...format(args), '__LOG') }
+      },
+      info: (...args) => {
+        if (checkLevel('info')) { global.nativeLog(...format(args), '__INFO') }
+      },
+      warn: (...args) => {
+        if (checkLevel('warn')) { global.nativeLog(...format(args), '__WARN') }
+      },
+      error: (...args) => {
+        if (checkLevel('error')) { global.nativeLog(...format(args), '__ERROR') }
+      }
+    }
+  }
+
+  // Web or Node
+  else {
+    const { debug, log, info, warn, error } = console
+    console.__ori__ = { debug, log, info, warn, error }
+    console.debug = (...args) => {
+      if (checkLevel('debug')) { console.__ori__.debug.apply(console, args) }
+    }
+    console.log = (...args) => {
+      if (checkLevel('log')) { console.__ori__.log.apply(console, args) }
+    }
+    console.info = (...args) => {
+      if (checkLevel('info')) { console.__ori__.info.apply(console, args) }
+    }
+    console.warn = (...args) => {
+      if (checkLevel('warn')) { console.__ori__.warn.apply(console, args) }
+    }
+    console.error = (...args) => {
+      if (checkLevel('error')) { console.__ori__.error.apply(console, args) }
+    }
+  }
+}
+
+/**
+ * Reset hacked console to original.
+ */
+/* istanbul ignore next */
+export function resetNativeConsole () {
+  levelMap = {}
+  global.console = originalConsole
+}
+
+/**
+ * Generate map for which types of message will be sent in a certain message level
+ * as the order of LEVELS.
+ */
+function generateLevelMap () {
+  LEVELS.forEach(level => {
+    const levelIndex = LEVELS.indexOf(level)
+    levelMap[level] = {}
+    LEVELS.forEach(type => {
+      const typeIndex = LEVELS.indexOf(type)
+      if (typeIndex <= levelIndex) {
+        levelMap[level][type] = true
+      }
+    })
+  })
+}
+
+/**
+ * Check if a certain type of message will be sent in current log level of env.
+ * @param  {string} type
+ * @return {boolean}
+ */
+function checkLevel (type) {
+  const logLevel = (global.WXEnvironment && global.WXEnvironment.logLevel) || 'log'
+  return levelMap[logLevel] && levelMap[logLevel][type]
+}
+
+/**
+ * Convert all log arguments into primitive values.
+ * @param  {array} args
+ * @return {array}
+ */
+/* istanbul ignore next */
+function format (args) {
+  return args.map((v) => {
+    const type = Object.prototype.toString.call(v)
+    if (type.toLowerCase() === '[object object]') {
+      v = JSON.stringify(v)
+    }
+    else {
+      v = String(v)
+    }
+    return v
+  })
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/shared/env/freeze.js
----------------------------------------------------------------------
diff --git a/runtime/shared/env/freeze.js b/runtime/shared/env/freeze.js
new file mode 100644
index 0000000..f997a5f
--- /dev/null
+++ b/runtime/shared/env/freeze.js
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ */
+/**
+ * Freeze the prototype of javascript build-in objects.
+ */
+/* istanbul ignore next */
+export function freezePrototype () {
+  Object.freeze(Object)
+  Object.freeze(Array)
+
+  // Object.freeze(Object.prototype)
+  freezeObjectProto()
+  Object.freeze(Array.prototype)
+  Object.freeze(String.prototype)
+  Object.freeze(Number.prototype)
+  Object.freeze(Boolean.prototype)
+
+  // Object.freeze(Error.prototype)
+  freezeErrorProto()
+  Object.freeze(Date.prototype)
+  Object.freeze(RegExp.prototype)
+}
+
+function freezeObjectProto () {
+  const proto = Object.prototype
+  const protoName = 'Object.prototype'
+  freezeProtoProperty(proto, '__defineGetter__', protoName)
+  freezeProtoProperty(proto, '__defineSetter__', protoName)
+  freezeProtoProperty(proto, '__lookupGetter__', protoName)
+  freezeProtoProperty(proto, '__lookupSetter__', protoName)
+  freezeProtoProperty(proto, 'constructor', protoName)
+  freezeProtoProperty(proto, 'hasOwnProperty', protoName)
+  freezeProtoProperty(proto, 'isPrototypeOf', protoName)
+  freezeProtoProperty(proto, 'propertyIsEnumerable', protoName)
+  freezeProtoProperty(proto, 'toLocaleString', protoName)
+  freezeProtoProperty(proto, 'toString', protoName)
+  freezeProtoProperty(proto, 'valueOf', protoName)
+  Object.seal(proto)
+}
+
+function freezeErrorProto () {
+  const proto = Error.prototype
+  const protoName = 'Error.prototype'
+  freezeProtoProperty(proto, 'name', protoName)
+  freezeProtoProperty(proto, 'message', protoName)
+  freezeProtoProperty(proto, 'toString', protoName)
+  freezeProtoProperty(proto, 'constructor', protoName)
+  Object.seal(proto)
+}
+
+function freezeProtoProperty (proto, propertyName, protoName) {
+  if (!proto.hasOwnProperty(propertyName)) {
+    return
+  }
+
+  const origin = proto[propertyName]
+  Object.defineProperty(proto, propertyName, {
+    get: function () {
+      return origin
+    },
+    set: function (value) {
+      if (this === proto) {
+        throw Error(`Cannot assign to read only property ${propertyName} of ${protoName}`)
+      }
+
+      Object.defineProperty(this, propertyName, {
+        value: value,
+        writable: true
+      })
+
+      return value
+    }
+  })
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/shared/env/setTimeout.js
----------------------------------------------------------------------
diff --git a/runtime/shared/env/setTimeout.js b/runtime/shared/env/setTimeout.js
new file mode 100644
index 0000000..d545d6d
--- /dev/null
+++ b/runtime/shared/env/setTimeout.js
@@ -0,0 +1,63 @@
+/*
+ * 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
+ * Polyfill `setTimeout` on Android V8 using native method
+ * `setTimeoutNative(callbackId, time)` and JS method
+ * `setTimeoutCallback(callbackId)`.
+ * This polyfill is only used in virtual-DOM diff & flush agorithm. Not
+ * accessed by JS Bundle code (The timer APIs polyfill for JS Bundle is in
+ * `html5/default/app/ctrl.js`).
+ */
+
+const originalSetTimeout = global.setTimeout
+const setTimeoutNative = global.setTimeoutNative
+
+/**
+ * Set up native timer
+ */
+/* istanbul ignore next */
+export function setNativeTimer () {
+  if (typeof setTimeout === 'undefined' &&
+  typeof setTimeoutNative === 'function') {
+    const timeoutMap = {}
+    let timeoutId = 0
+
+    global.setTimeout = (cb, time) => {
+      timeoutMap[++timeoutId] = cb
+      setTimeoutNative(timeoutId.toString(), time)
+    }
+
+    global.setTimeoutCallback = (id) => {
+      if (typeof timeoutMap[id] === 'function') {
+        timeoutMap[id]()
+        delete timeoutMap[id]
+      }
+    }
+  }
+}
+
+/* istanbul ignore next */
+export function resetNativeTimer () {
+  global.setTimeout = originalSetTimeout
+  global.setTimeoutCallback = null
+}
+
+setNativeTimer()

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/shared/index.js
----------------------------------------------------------------------
diff --git a/runtime/shared/index.js b/runtime/shared/index.js
new file mode 100644
index 0000000..a478b86
--- /dev/null
+++ b/runtime/shared/index.js
@@ -0,0 +1,33 @@
+/*
+ * 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 './polyfill/arrayFrom'
+import './polyfill/objectAssign'
+import './polyfill/objectSetPrototypeOf'
+
+// import promise hack and polyfills
+import './polyfill/promise'
+import 'core-js/modules/es6.object.to-string'
+import 'core-js/modules/es6.string.iterator'
+import 'core-js/modules/web.dom.iterable'
+import 'core-js/modules/es6.promise'
+
+export * from './env/console'
+export * from './env/setTimeout'
+export * from './env/freeze'

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/shared/polyfill/arrayFrom.js
----------------------------------------------------------------------
diff --git a/runtime/shared/polyfill/arrayFrom.js b/runtime/shared/polyfill/arrayFrom.js
new file mode 100644
index 0000000..af5e78c
--- /dev/null
+++ b/runtime/shared/polyfill/arrayFrom.js
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ */
+
+/* eslint-disable */
+
+// Production steps of ECMA-262, Edition 6, 22.1.2.1
+// Reference: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from
+
+/* istanbul ignore if */
+if (!Array.from) {
+  Array.from = (function() {
+    var toStr = Object.prototype.toString;
+    var isCallable = function(fn) {
+      return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
+    };
+    var toInteger = function(value) {
+      var number = Number(value);
+      if (isNaN(number)) {
+        return 0;
+      }
+      if (number === 0 || !isFinite(number)) {
+        return number;
+      }
+      return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
+    };
+    var maxSafeInteger = Math.pow(2, 53) - 1;
+    var toLength = function(value) {
+      var len = toInteger(value);
+      return Math.min(Math.max(len, 0), maxSafeInteger);
+    };
+
+    // The length property of the from method is 1.
+    return function from(arrayLike/*, mapFn, thisArg */) {
+      // 1. Let C be the this value.
+      var C = this;
+
+      // 2. Let items be ToObject(arrayLike).
+      var items = Object(arrayLike);
+
+      // 3. ReturnIfAbrupt(items).
+      if (arrayLike == null) {
+        throw new TypeError('Array.from requires an array-like object - not null or undefined');
+      }
+
+      // 4. If mapfn is undefined, then let mapping be false.
+      var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
+      var T;
+      if (typeof mapFn !== 'undefined') {
+        // 5. else
+        // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
+        if (!isCallable(mapFn)) {
+          throw new TypeError('Array.from: when provided, the second argument must be a function');
+        }
+
+        // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
+        if (arguments.length > 2) {
+          T = arguments[2];
+        }
+      }
+
+      // 10. Let lenValue be Get(items, "length").
+      // 11. Let len be ToLength(lenValue).
+      var len = toLength(items.length);
+
+      // 13. If IsConstructor(C) is true, then
+      // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
+      // 14. a. Else, Let A be ArrayCreate(len).
+      var A = isCallable(C) ? Object(new C(len)) : new Array(len);
+
+      // 16. Let k be 0.
+      var k = 0;
+      // 17. Repeat, while k < len… (also steps a - h)
+      var kValue;
+      while (k < len) {
+        kValue = items[k];
+        if (mapFn) {
+          A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
+        } else {
+          A[k] = kValue;
+        }
+        k += 1;
+      }
+      // 18. Let putStatus be Put(A, "length", len, true).
+      A.length = len;
+      // 20. Return A.
+      return A;
+    };
+  }());
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/shared/polyfill/objectAssign.js
----------------------------------------------------------------------
diff --git a/runtime/shared/polyfill/objectAssign.js b/runtime/shared/polyfill/objectAssign.js
new file mode 100644
index 0000000..7aa5993
--- /dev/null
+++ b/runtime/shared/polyfill/objectAssign.js
@@ -0,0 +1,19 @@
+/*
+ * 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 'core-js/fn/object/assign'

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/shared/polyfill/objectSetPrototypeOf.js
----------------------------------------------------------------------
diff --git a/runtime/shared/polyfill/objectSetPrototypeOf.js b/runtime/shared/polyfill/objectSetPrototypeOf.js
new file mode 100644
index 0000000..6364ce4
--- /dev/null
+++ b/runtime/shared/polyfill/objectSetPrototypeOf.js
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+/* eslint-disable */
+
+// https://gist.github.com/WebReflection/5593554
+
+/* istanbul ignore if */
+if (!Object.setPrototypeOf) {
+  Object.setPrototypeOf = (function(Object, magic) {
+    var set;
+    function setPrototypeOf(O, proto) {
+      set.call(O, proto);
+      return O;
+    }
+    try {
+      // this works already in Firefox and Safari
+      set = Object.getOwnPropertyDescriptor(Object.prototype, magic).set;
+      set.call({}, null);
+    } catch (e) {
+      if (
+        // IE < 11 cannot be shimmed
+        Object.prototype !== {}[magic] ||
+        // neither can any browser that actually
+        // implemented __proto__ correctly
+        // (all but old V8 will return here)
+        {__proto__: null}.__proto__ === void 0
+        // this case means null objects cannot be passed
+        // through setPrototypeOf in a reliable way
+        // which means here a **Sham** is needed instead
+      ) {
+        return;
+      }
+      // nodejs 0.8 and 0.10 are (buggy and..) fine here
+      // probably Chrome or some old Mobile stock browser
+      set = function(proto) {
+        this[magic] = proto;
+      };
+      // please note that this will **not** work
+      // in those browsers that do not inherit
+      // __proto__ by mistake from Object.prototype
+      // in these cases we should probably throw an error
+      // or at least be informed about the issue
+      setPrototypeOf.polyfill = setPrototypeOf(
+        setPrototypeOf({}, null),
+        Object.prototype
+      ) instanceof Object;
+      // setPrototypeOf.polyfill === true means it works as meant
+      // setPrototypeOf.polyfill === false means it's not 100% reliable
+      // setPrototypeOf.polyfill === undefined
+      // or
+      // setPrototypeOf.polyfill ==  null means it's not a polyfill
+      // which means it works as expected
+      // we can even delete Object.prototype.__proto__;
+    }
+    return setPrototypeOf;
+  }(Object, '__proto__'));
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/shared/polyfill/promise.js
----------------------------------------------------------------------
diff --git a/runtime/shared/polyfill/promise.js b/runtime/shared/polyfill/promise.js
new file mode 100644
index 0000000..39fe816
--- /dev/null
+++ b/runtime/shared/polyfill/promise.js
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+// fix Promise Problem on JSContext of iOS7~8
+// @see https://bugs.webkit.org/show_bug.cgi?id=135866
+
+const { WXEnvironment } = global
+
+/* istanbul ignore next */
+if (WXEnvironment && WXEnvironment.platform === 'iOS') {
+  global.Promise = undefined
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/shared/utils.js
----------------------------------------------------------------------
diff --git a/runtime/shared/utils.js b/runtime/shared/utils.js
new file mode 100644
index 0000000..c2ab10f
--- /dev/null
+++ b/runtime/shared/utils.js
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+/**
+ * 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)
+}
+
+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
+}
+
+/**
+ * Detect if the param is falsy or empty
+ * @param {any} any
+ */
+export function isEmpty (any) {
+  if (!any || typeof any !== 'object') {
+    return true
+  }
+
+  for (const key in any) {
+    if (Object.prototype.hasOwnProperty.call(any, key)) {
+      return false
+    }
+  }
+  return true
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/vdom/Comment.js
----------------------------------------------------------------------
diff --git a/runtime/vdom/Comment.js b/runtime/vdom/Comment.js
new file mode 100644
index 0000000..79f9e15
--- /dev/null
+++ b/runtime/vdom/Comment.js
@@ -0,0 +1,43 @@
+/*
+ * 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 Node from './Node'
+import { uniqueId } from '../shared/utils'
+
+export default class Comment extends Node {
+  constructor (value) {
+    super()
+
+    this.nodeType = 8
+    this.nodeId = uniqueId()
+    this.ref = this.nodeId
+    this.type = 'comment'
+    this.value = value
+    this.children = []
+    this.pureChildren = []
+  }
+
+  /**
+  * Convert to HTML comment string.
+  * @return {stirng} html
+  */
+  toString () {
+    return '<!-- ' + this.value + ' -->'
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/vdom/Document.js
----------------------------------------------------------------------
diff --git a/runtime/vdom/Document.js b/runtime/vdom/Document.js
new file mode 100644
index 0000000..c64fba5
--- /dev/null
+++ b/runtime/vdom/Document.js
@@ -0,0 +1,189 @@
+/*
+ * 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 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'
+
+/**
+ * Update all changes for an element.
+ * @param {object} element
+ * @param {object} changes
+ */
+function updateElement (el, changes) {
+  const attrs = changes.attrs || {}
+  for (const name in attrs) {
+    el.setAttr(name, attrs[name], true)
+  }
+  const style = changes.style || {}
+  for (const name in style) {
+    el.setStyle(name, style[name], true)
+  }
+}
+
+export default class Document {
+  constructor (id, url, handler) {
+    id = id ? id.toString() : ''
+    this.id = id
+    this.URL = url
+
+    addDoc(id, this)
+    this.nodeMap = {}
+    const L = Document.Listener || Listener
+    this.listener = new L(id, handler || createHandler(id, Document.handler)) // deprecated
+    this.taskCenter = new TaskCenter(id, handler ? (id, ...args) => handler(...args) : Document.handler)
+    this.createDocumentElement()
+  }
+
+  /**
+  * Get the node from nodeMap.
+  * @param {string} reference id
+  * @return {object} node
+  */
+  getRef (ref) {
+    return this.nodeMap[ref]
+  }
+
+  /**
+  * Turn on batched updates.
+  */
+  open () {
+    this.listener.batched = false
+  }
+
+  /**
+  * Turn off batched updates.
+  */
+  close () {
+    this.listener.batched = true
+  }
+
+  /**
+  * Create the document element.
+  * @return {object} documentElement
+  */
+  createDocumentElement () {
+    if (!this.documentElement) {
+      const el = new Element('document')
+      el.docId = this.id
+      el.ownerDocument = this
+      el.role = 'documentElement'
+      el.depth = 0
+      el.ref = '_documentElement'
+      this.nodeMap._documentElement = el
+      this.documentElement = el
+
+      Object.defineProperty(el, 'appendChild', {
+        configurable: true,
+        enumerable: true,
+        writable: true,
+        value: (node) => {
+          appendBody(this, node)
+        }
+      })
+
+      Object.defineProperty(el, 'insertBefore', {
+        configurable: true,
+        enumerable: true,
+        writable: true,
+        value: (node, before) => {
+          appendBody(this, node, before)
+        }
+      })
+    }
+
+    return this.documentElement
+  }
+
+  /**
+  * Create the body element.
+  * @param {string} type
+  * @param {objct} props
+  * @return {object} body element
+  */
+  createBody (type, props) {
+    if (!this.body) {
+      const el = new Element(type, props)
+      setBody(this, el)
+    }
+
+    return this.body
+  }
+
+  /**
+  * Create an element.
+  * @param {string} tagName
+  * @param {objct} props
+  * @return {object} element
+  */
+  createElement (tagName, props) {
+    return new Element(tagName, props)
+  }
+
+  /**
+  * Create an comment.
+  * @param {string} text
+  * @return {object} comment
+  */
+  createComment (text) {
+    return new Comment(text)
+  }
+
+  /**
+  * Fire an event on specified element manually.
+  * @param {object} element
+  * @param {string} event type
+  * @param {object} event object
+  * @param {object} dom changes
+  * @param {object} options
+  * @return {} anything returned by handler function
+  */
+  fireEvent (el, type, event, domChanges, options) {
+    if (!el) {
+      return
+    }
+    event = event || {}
+    event.type = event.type || type
+    event.target = el
+    event.currentTarget = el
+    event.timestamp = Date.now()
+    if (domChanges) {
+      updateElement(el, domChanges)
+    }
+    const isBubble = this.getRef('_root').attr['bubble'] === 'true'
+    return el.fireEvent(type, event, isBubble, options)
+  }
+
+  /**
+  * Destroy current document, and remove itself form docMap.
+  */
+  destroy () {
+    this.taskCenter.destroyCallback()
+    delete this.listener
+    delete this.nodeMap
+    delete this.taskCenter
+    removeDoc(this.id)
+  }
+}
+
+// default task handler
+Document.handler = null

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/vdom/Element.js
----------------------------------------------------------------------
diff --git a/runtime/vdom/Element.js b/runtime/vdom/Element.js
new file mode 100644
index 0000000..3160b6d
--- /dev/null
+++ b/runtime/vdom/Element.js
@@ -0,0 +1,524 @@
+/*
+ * 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 Node from './Node'
+import {
+  getDoc,
+  getTaskCenter,
+  linkParent,
+  nextElement,
+  previousElement,
+  insertIndex,
+  moveIndex,
+  removeIndex
+} from './operation'
+import { uniqueId, isEmpty } from '../shared/utils'
+import { getWeexElement, setElement } from './WeexElement'
+
+const DEFAULT_TAG_NAME = 'div'
+const BUBBLE_EVENTS = [
+  'click', 'longpress', 'touchstart', 'touchmove', 'touchend',
+  'panstart', 'panmove', 'panend', 'horizontalpan', 'verticalpan', 'swipe'
+]
+
+function registerNode (docId, node) {
+  const doc = getDoc(docId)
+  doc.nodeMap[node.nodeId] = node
+}
+
+export default class Element extends Node {
+  constructor (type = DEFAULT_TAG_NAME, props, isExtended) {
+    super()
+
+    const WeexElement = getWeexElement(type)
+    if (WeexElement && !isExtended) {
+      return new WeexElement(type, props, true)
+    }
+
+    props = props || {}
+    this.nodeType = 1
+    this.nodeId = uniqueId()
+    this.ref = this.nodeId
+    this.type = type
+    this.attr = props.attr || {}
+    this.style = props.style || {}
+    this.classStyle = props.classStyle || {}
+    this.event = {}
+    this.children = []
+    this.pureChildren = []
+  }
+
+  /**
+   * Append a child node.
+   * @param {object} node
+   * @return {undefined | number} the signal sent by native
+   */
+  appendChild (node) {
+    if (node.parentNode && node.parentNode !== this) {
+      return
+    }
+    /* istanbul ignore else */
+    if (!node.parentNode) {
+      linkParent(node, this)
+      insertIndex(node, this.children, this.children.length, true)
+      if (this.docId) {
+        registerNode(this.docId, node)
+      }
+      if (node.nodeType === 1) {
+        insertIndex(node, this.pureChildren, this.pureChildren.length)
+        const taskCenter = getTaskCenter(this.docId)
+        if (taskCenter) {
+          return taskCenter.send(
+            'dom',
+            { action: 'addElement' },
+            [this.ref, node.toJSON(), -1]
+          )
+        }
+      }
+    }
+    else {
+      moveIndex(node, this.children, this.children.length, true)
+      if (node.nodeType === 1) {
+        const index = moveIndex(node, this.pureChildren, this.pureChildren.length)
+        const taskCenter = getTaskCenter(this.docId)
+        if (taskCenter && index >= 0) {
+          return taskCenter.send(
+            'dom',
+            { action: 'moveElement' },
+            [node.ref, this.ref, index]
+          )
+        }
+      }
+    }
+  }
+
+  /**
+   * Insert a node before specified node.
+   * @param {object} node
+   * @param {object} before
+   * @return {undefined | number} the signal sent by native
+   */
+  insertBefore (node, before) {
+    if (node.parentNode && node.parentNode !== this) {
+      return
+    }
+    if (node === before || (node.nextSibling && node.nextSibling === before)) {
+      return
+    }
+    if (!node.parentNode) {
+      linkParent(node, this)
+      insertIndex(node, this.children, this.children.indexOf(before), true)
+      if (this.docId) {
+        registerNode(this.docId, node)
+      }
+      if (node.nodeType === 1) {
+        const pureBefore = nextElement(before)
+        const index = insertIndex(
+          node,
+          this.pureChildren,
+          pureBefore
+          ? this.pureChildren.indexOf(pureBefore)
+          : this.pureChildren.length
+        )
+        const taskCenter = getTaskCenter(this.docId)
+        if (taskCenter) {
+          return taskCenter.send(
+            'dom',
+            { action: 'addElement' },
+            [this.ref, node.toJSON(), index]
+          )
+        }
+      }
+    }
+    else {
+      moveIndex(node, this.children, this.children.indexOf(before), true)
+      if (node.nodeType === 1) {
+        const pureBefore = nextElement(before)
+        /* istanbul ignore next */
+        const index = moveIndex(
+          node,
+          this.pureChildren,
+          pureBefore
+          ? this.pureChildren.indexOf(pureBefore)
+          : this.pureChildren.length
+        )
+        const taskCenter = getTaskCenter(this.docId)
+        if (taskCenter && index >= 0) {
+          return taskCenter.send(
+            'dom',
+            { action: 'moveElement' },
+            [node.ref, this.ref, index]
+          )
+        }
+      }
+    }
+  }
+
+  /**
+   * Insert a node after specified node.
+   * @param {object} node
+   * @param {object} after
+   * @return {undefined | number} the signal sent by native
+   */
+  insertAfter (node, after) {
+    if (node.parentNode && node.parentNode !== this) {
+      return
+    }
+    if (node === after || (node.previousSibling && node.previousSibling === after)) {
+      return
+    }
+    if (!node.parentNode) {
+      linkParent(node, this)
+      insertIndex(node, this.children, this.children.indexOf(after) + 1, true)
+      /* istanbul ignore else */
+      if (this.docId) {
+        registerNode(this.docId, node)
+      }
+      if (node.nodeType === 1) {
+        const index = insertIndex(
+          node,
+          this.pureChildren,
+          this.pureChildren.indexOf(previousElement(after)) + 1
+        )
+        const taskCenter = getTaskCenter(this.docId)
+        /* istanbul ignore else */
+        if (taskCenter) {
+          return taskCenter.send(
+            'dom',
+            { action: 'addElement' },
+            [this.ref, node.toJSON(), index]
+          )
+        }
+      }
+    }
+    else {
+      moveIndex(node, this.children, this.children.indexOf(after) + 1, true)
+      if (node.nodeType === 1) {
+        const index = moveIndex(
+          node,
+          this.pureChildren,
+          this.pureChildren.indexOf(previousElement(after)) + 1
+        )
+        const taskCenter = getTaskCenter(this.docId)
+        if (taskCenter && index >= 0) {
+          return taskCenter.send(
+            'dom',
+            { action: 'moveElement' },
+            [node.ref, this.ref, index]
+          )
+        }
+      }
+    }
+  }
+
+  /**
+   * Remove a child node, and decide whether it should be destroyed.
+   * @param {object} node
+   * @param {boolean} preserved
+   */
+  removeChild (node, preserved) {
+    if (node.parentNode) {
+      removeIndex(node, this.children, true)
+      if (node.nodeType === 1) {
+        removeIndex(node, this.pureChildren)
+        const taskCenter = getTaskCenter(this.docId)
+        if (taskCenter) {
+          taskCenter.send(
+            'dom',
+            { action: 'removeElement' },
+            [node.ref]
+          )
+        }
+      }
+    }
+    if (!preserved) {
+      node.destroy()
+    }
+  }
+
+  /**
+   * Clear all child nodes.
+   */
+  clear () {
+    const taskCenter = getTaskCenter(this.docId)
+    /* istanbul ignore else */
+    if (taskCenter) {
+      this.pureChildren.forEach(node => {
+        taskCenter.send(
+          'dom',
+          { action: 'removeElement' },
+          [node.ref]
+        )
+      })
+    }
+    this.children.forEach(node => {
+      node.destroy()
+    })
+    this.children.length = 0
+    this.pureChildren.length = 0
+  }
+
+  /**
+   * Set an attribute, and decide whether the task should be send to native.
+   * @param {string} key
+   * @param {string | number} value
+   * @param {boolean} silent
+   */
+  setAttr (key, value, silent) {
+    if (this.attr[key] === value && silent !== false) {
+      return
+    }
+    this.attr[key] = value
+    const taskCenter = getTaskCenter(this.docId)
+    if (!silent && taskCenter) {
+      const result = {}
+      result[key] = value
+      taskCenter.send(
+        'dom',
+        { action: 'updateAttrs' },
+        [this.ref, result]
+      )
+    }
+  }
+
+  /**
+   * Set batched attributes.
+   * @param {object} batchedAttrs
+   * @param {boolean} silent
+   */
+  setAttrs (batchedAttrs, silent) {
+    if (isEmpty(batchedAttrs)) return
+    const mutations = {}
+    for (const key in batchedAttrs) {
+      if (this.attr[key] !== batchedAttrs[key]) {
+        this.attr[key] = batchedAttrs[key]
+        mutations[key] = batchedAttrs[key]
+      }
+    }
+    if (!isEmpty(mutations)) {
+      const taskCenter = getTaskCenter(this.docId)
+      if (!silent && taskCenter) {
+        taskCenter.send(
+          'dom',
+          { action: 'updateAttrs' },
+          [this.ref, mutations]
+        )
+      }
+    }
+  }
+
+  /**
+   * Set a style property, and decide whether the task should be send to native.
+   * @param {string} key
+   * @param {string | number} value
+   * @param {boolean} silent
+   */
+  setStyle (key, value, silent) {
+    if (this.style[key] === value && silent !== false) {
+      return
+    }
+    this.style[key] = value
+    const taskCenter = getTaskCenter(this.docId)
+    if (!silent && taskCenter) {
+      const result = {}
+      result[key] = value
+      taskCenter.send(
+        'dom',
+        { action: 'updateStyle' },
+        [this.ref, result]
+      )
+    }
+  }
+
+  /**
+   * Set batched style properties.
+   * @param {object} batchedStyles
+   * @param {boolean} silent
+   */
+  setStyles (batchedStyles, silent) {
+    if (isEmpty(batchedStyles)) return
+    const mutations = {}
+    for (const key in batchedStyles) {
+      if (this.style[key] !== batchedStyles[key]) {
+        this.style[key] = batchedStyles[key]
+        mutations[key] = batchedStyles[key]
+      }
+    }
+    if (!isEmpty(mutations)) {
+      const taskCenter = getTaskCenter(this.docId)
+      if (!silent && taskCenter) {
+        taskCenter.send(
+          'dom',
+          { action: 'updateStyle' },
+          [this.ref, mutations]
+        )
+      }
+    }
+  }
+
+  /**
+   * Set style properties from class.
+   * @param {object} classStyle
+   */
+  setClassStyle (classStyle) {
+    // reset previous class style to empty string
+    for (const key in this.classStyle) {
+      this.classStyle[key] = ''
+    }
+
+    Object.assign(this.classStyle, classStyle)
+    const taskCenter = getTaskCenter(this.docId)
+    if (taskCenter) {
+      taskCenter.send(
+        'dom',
+        { action: 'updateStyle' },
+        [this.ref, this.toStyle()]
+      )
+    }
+  }
+
+  /**
+   * Add an event handler.
+   * @param {string} event type
+   * @param {function} event handler
+   */
+  addEvent (type, handler, params) {
+    if (!this.event) {
+      this.event = {}
+    }
+    if (!this.event[type]) {
+      this.event[type] = { handler, params }
+      const taskCenter = getTaskCenter(this.docId)
+      if (taskCenter) {
+        taskCenter.send(
+          'dom',
+          { action: 'addEvent' },
+          [this.ref, type]
+        )
+      }
+    }
+  }
+
+  /**
+   * Remove an event handler.
+   * @param {string} event type
+   */
+  removeEvent (type) {
+    if (this.event && this.event[type]) {
+      delete this.event[type]
+      const taskCenter = getTaskCenter(this.docId)
+      if (taskCenter) {
+        taskCenter.send(
+          'dom',
+          { action: 'removeEvent' },
+          [this.ref, type]
+        )
+      }
+    }
+  }
+
+  /**
+   * Fire an event manually.
+   * @param {string} type type
+   * @param {function} event handler
+   * @param {boolean} isBubble whether or not event bubble
+   * @param {boolean} options
+   * @return {} anything returned by handler function
+   */
+  fireEvent (type, event, isBubble, options) {
+    let result = null
+    let isStopPropagation = false
+    const eventDesc = this.event[type]
+    if (eventDesc && event) {
+      const handler = eventDesc.handler
+      event.stopPropagation = () => {
+        isStopPropagation = true
+      }
+      if (options && options.params) {
+        result = handler.call(this, ...options.params, event)
+      }
+      else {
+        result = handler.call(this, event)
+      }
+    }
+
+    if (!isStopPropagation
+      && isBubble
+      && (BUBBLE_EVENTS.indexOf(type) !== -1)
+      && this.parentNode
+      && this.parentNode.fireEvent) {
+      event.currentTarget = this.parentNode
+      this.parentNode.fireEvent(type, event, isBubble) // no options
+    }
+
+    return result
+  }
+
+  /**
+   * Get all styles of current element.
+   * @return {object} style
+   */
+  toStyle () {
+    return Object.assign({}, this.classStyle, this.style)
+  }
+
+  /**
+   * Convert current element to JSON like object.
+   * @return {object} element
+   */
+  toJSON () {
+    const result = {
+      ref: this.ref.toString(),
+      type: this.type,
+      attr: this.attr,
+      style: this.toStyle()
+    }
+    const event = []
+    for (const type in this.event) {
+      const { params } = this.event[type]
+      if (!params) {
+        event.push(type)
+      }
+      else {
+        event.push({ type, params })
+      }
+    }
+    if (event.length) {
+      result.event = event
+    }
+    if (this.pureChildren.length) {
+      result.children = this.pureChildren.map((child) => child.toJSON())
+    }
+    return result
+  }
+
+  /**
+   * Convert to HTML element tag string.
+   * @return {stirng} html
+   */
+  toString () {
+    return '<' + this.type +
+    ' attr=' + JSON.stringify(this.attr) +
+    ' style=' + JSON.stringify(this.toStyle()) + '>' +
+    this.pureChildren.map((child) => child.toString()).join('') +
+    '</' + this.type + '>'
+  }
+}
+
+setElement(Element)

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/vdom/Node.js
----------------------------------------------------------------------
diff --git a/runtime/vdom/Node.js b/runtime/vdom/Node.js
new file mode 100644
index 0000000..4f13edb
--- /dev/null
+++ b/runtime/vdom/Node.js
@@ -0,0 +1,47 @@
+/*
+ * 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 { uniqueId } from '../shared/utils'
+import { getDoc } from './operation'
+
+export default class Node {
+  constructor () {
+    this.nodeId = uniqueId()
+    this.ref = this.nodeId
+    this.children = []
+    this.pureChildren = []
+    this.parentNode = null
+    this.nextSibling = null
+    this.previousSibling = null
+  }
+
+  /**
+  * Destroy current node, and remove itself form nodeMap.
+  */
+  destroy () {
+    const doc = getDoc(this.docId)
+    if (doc) {
+      delete this.docId
+      delete doc.nodeMap[this.nodeId]
+    }
+    this.children.forEach(child => {
+      child.destroy()
+    })
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/vdom/WeexElement.js
----------------------------------------------------------------------
diff --git a/runtime/vdom/WeexElement.js b/runtime/vdom/WeexElement.js
new file mode 100644
index 0000000..455e69e
--- /dev/null
+++ b/runtime/vdom/WeexElement.js
@@ -0,0 +1,84 @@
+/*
+ * 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 { getTaskCenter } from './operation'
+
+let Element
+
+export function setElement (El) {
+  Element = El
+}
+
+/**
+ * A map which stores all type of elements.
+ * @type {Object}
+ */
+const registeredElements = {}
+
+/**
+ * Register an extended element type with component methods.
+ * @param  {string} type    component type
+ * @param  {array}  methods a list of method names
+ */
+export function registerElement (type, methods) {
+  // Skip when no special component methods.
+  if (!methods || !methods.length) {
+    return
+  }
+
+  // Init constructor.
+  class WeexElement extends Element {}
+
+  // Add methods to prototype.
+  methods.forEach(methodName => {
+    WeexElement.prototype[methodName] = function (...args) {
+      const taskCenter = getTaskCenter(this.docId)
+      if (taskCenter) {
+        return taskCenter.send('component', {
+          ref: this.ref,
+          component: type,
+          method: methodName
+        }, args)
+      }
+    }
+  })
+
+  // Add to element type map.
+  registeredElements[type] = WeexElement
+}
+
+export function unregisterElement (type) {
+  delete registeredElements[type]
+}
+
+export function getWeexElement (type) {
+  return registeredElements[type]
+}
+
+export function isWeexElement (type) {
+  return !!registeredElements[type]
+}
+
+/**
+ * Clear all element types. Only for testing.
+ */
+export function clearWeexElements () {
+  for (const type in registeredElements) {
+    unregisterElement(type)
+  }
+}



[21/22] incubator-weex git commit: * [build] update build scripts of js framework

Posted by ha...@apache.org.
* [build] update build scripts of js framework


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

Branch: refs/heads/master
Commit: 9dd6cba1eb14fd3e629678635f7662670d472db7
Parents: 51dd277
Author: Hanks <zh...@gmail.com>
Authored: Fri Dec 29 20:58:43 2017 +0800
Committer: Hanks <zh...@gmail.com>
Committed: Tue Jan 2 14:39:07 2018 +0800

----------------------------------------------------------------------
 .flowconfig             |  17 -----
 build/build.js          | 118 +++++++++++------------------------
 build/config.js         | 143 ++++++++++++++++++++++++-------------------
 dangerfile.js           |  15 ++---
 package.json            |  31 +++++-----
 runtime/vdom/Element.js |   8 +--
 6 files changed, 147 insertions(+), 185 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/9dd6cba1/.flowconfig
----------------------------------------------------------------------
diff --git a/.flowconfig b/.flowconfig
deleted file mode 100644
index d92b990..0000000
--- a/.flowconfig
+++ /dev/null
@@ -1,17 +0,0 @@
-[ignore]
-.*/node_modules/.*
-.*/test/.*
-.*/build/.*
-.*/examples/.*
-.*/doc/.*
-.*/android/.*
-.*/ios/.*
-.*/bin/.*
-.*/scripts/.*
-.*/dist/.*
-.*/flow-typed/.*
-
-[include]
-
-[options]
-experimental.strict_type_args=false

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/9dd6cba1/build/build.js
----------------------------------------------------------------------
diff --git a/build/build.js b/build/build.js
index fb05e4b..d845816 100644
--- a/build/build.js
+++ b/build/build.js
@@ -16,23 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-"use strict"
 
 const fs = require('fs')
 const path = require('path')
-const gzip = require('zlib').createGzip()
-const pkg = require('../package.json')
 const rollup = require('rollup')
 const watch = require('rollup-watch')
-const webpack = require('webpack')
-
 const getConfig = require('./config')
 
-// create dist folder
-if (!fs.existsSync('dist')) {
-  fs.mkdirSync('dist')
-}
-
 let isWatch = false
 if (process.argv[3]) {
   isWatch = process.argv[3] === '--watch' || process.argv[3] === '-w'
@@ -46,100 +36,66 @@ else {
   console.log('\nPlease specify the package you want to build. [native, runtime, browser, vue]')
 }
 
-function extend(to, ...froms) {
-  froms.forEach(function (from) {
-    for (const key in from) {
-      if (from.hasOwnProperty(key)) {
-        to[key] = from[key]
-      }
-    }
-  })
-  return to
-}
-
-function runRollupOnWatch(config) {
+function runRollupOnWatch (config) {
   const watcher = watch(rollup, config)
   watcher.on('event', event => {
-    switch ( event.code ) {
-      case 'STARTING':
-        console.log('checking rollup-watch version...')
-        break
-
-      case 'BUILD_START':
-        console.log('bundling...')
-        break
-
-      case 'BUILD_END':
+    switch (event.code) {
+      case 'STARTING': console.log('checking rollup-watch version...'); break
+      case 'BUILD_START': console.log('bundling...'); break
+      case 'BUILD_END': {
         console.log('bundled in ' + path.relative(process.cwd(), config.dest)
           + ' (' + event.duration + 'ms)')
-        console.log('Watching for changes...' )
-        break
-
-      case 'ERROR':
-        console.error('ERROR: ', event.error)
-        break
-
-      default:
-        console.error('unknown event', event)
+        console.log('Watching for changes...')
+      } break
+      case 'ERROR': console.error('ERROR: ', event.error); break
+      default: console.error('unknown event', event)
     }
   })
 }
 
-function runRollup (config) {
-  return new Promise((resolve, reject) => {
-    rollup.rollup(config).then(bundle => {
-      bundle.write(config).then(() => {
-        report(config.dest)
-        resolve()
-      })
-    })
-  })
+async function runRollup (config) {
+  const bundle = await rollup.rollup(config)
+  const outputOptions = config.output
+  if (Array.isArray(outputOptions)) {
+    for (let i = 0; i < outputOptions.length; ++i) {
+      await bundle.write(outputOptions[i])
+      report(outputOptions[i].output.file)
+    }
+  }
+  else {
+    await bundle.write(outputOptions)
+    report(outputOptions.file)
+  }
 }
 
-function build (name) {
+async function build (name) {
   let pkgName = 'weex-js-framework'
   switch (name) {
-    case 'native': pkgName = 'weex-js-framework'; break;
-    case 'runtime': pkgName = 'weex-js-runtime'; break;
-    case 'legacy': pkgName = 'weex-legacy-framework'; break;
-    case 'vanilla': pkgName = 'weex-vanilla-framework'; break;
+    case 'native': pkgName = 'weex-js-framework'; break
+    case 'vue': pkgName = 'weex-vue'; break
+    case 'rax': pkgName = 'weex-rax'; break
+    case 'runtime': pkgName = 'weex-js-runtime'; break
+    case 'legacy': pkgName = 'weex-legacy-framework'; break
+    case 'vanilla': pkgName = 'weex-vanilla-framework'; break
   }
 
   const config = getConfig(pkgName)
   const minifyConfig = getConfig(pkgName, true)
+  const ES6Config = getConfig(pkgName, false, true)
+  const ES6MinifyConfig = getConfig(pkgName, true, true)
 
   if (isWatch) {
-    return runRollupOnWatch(config)
+    return await runRollupOnWatch(config)
   }
   else {
-    console.log(`\n => start to build ${name} (${pkgName})\n`)
-    return new Promise((resolve, reject) => {
-      return runRollup(config).then(() => {
-        let p = Promise.resolve()
-        return p.then(function () {
-          return runRollup(minifyConfig).then(() => {
-            zip(minifyConfig.dest, resolve)
-          })
-        })
-      })
-    })
+    console.log(`\n => start to build ${pkgName}\n`)
+    await runRollup(config)
+    await runRollup(minifyConfig)
+    await runRollup(ES6Config)
+    await runRollup(ES6MinifyConfig)
   }
 }
 
-function zip (filePath, callback) {
-  const read = fs.createReadStream(filePath)
-  const write = fs.createWriteStream(filePath + '.gz')
-  read.pipe(gzip).pipe(write).on('close', () => {
-    report(filePath + '.gz')
-    callback && callback()
-  })
-}
-
-function now () {
-  const time = Date.now() - (new Date()).getTimezoneOffset() * 60000
-  return (new Date(time)).toISOString().replace('T', ' ').substring(0, 16)
-}
-
 function report (filePath) {
   const size = (fs.statSync(filePath).size / 1024).toFixed(2) + 'KB'
   const file = path.relative(process.cwd(), filePath)

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/9dd6cba1/build/config.js
----------------------------------------------------------------------
diff --git a/build/config.js b/build/config.js
index 5a75a74..6c3873b 100644
--- a/build/config.js
+++ b/build/config.js
@@ -1,4 +1,3 @@
-
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
  * or more contributor license agreements.  See the NOTICE file
@@ -17,106 +16,126 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+
 const path = require('path')
 const json = require('rollup-plugin-json')
 const eslint = require('rollup-plugin-eslint')
 const replace = require('rollup-plugin-replace')
 const nodeResolve = require('rollup-plugin-node-resolve')
 const uglify = require('rollup-plugin-uglify')
+const uglifyES = require('uglify-es')
 const commonjs = require('rollup-plugin-commonjs')
 const buble = require('rollup-plugin-buble')
-const subversion = require('../package.json').subversion
+const packageJSON = require('../package.json')
+const deps = packageJSON.dependencies
+const subversion = packageJSON.subversion
 
 const frameworkBanner = `;(this.getJSFMVersion = function()`
   + `{return "${subversion.framework}"});\n`
-  + `var global = this, process = { env: {} };var setTimeout = global.setTimeout;\n`
+  + `var global = this; var process = {env:{}}; var setTimeout = global.setTimeout;\n`
 
 const configs = {
   'weex-js-framework': {
-    moduleName: 'Weex',
-    entry: absolute('html5/render/native/index.js'),
-    dest: absolute('packages/weex-js-framework/index.js'),
-    banner: `(this.nativeLog || function(s) {console.log(s)})`
-      + `('START JS FRAMEWORK ${subversion.framework}, Build ${now()}.');\n`
-      + frameworkBanner,
-    format: 'umd',
-    plugins: [
-      nodeResolve({
-        jsnext: true,
-        main: true
-      }),
-    ]
+    input: absolute('runtime/entries/index.js'),
+    output: {
+      name: 'Weex',
+      file: absolute('pre-build/weex-js-framework'),
+      banner: `(this.nativeLog || function(s) {console.log(s)})`
+        + `('START JS FRAMEWORK ${subversion.framework}, Build ${now()}. `
+        + `(Vue: ${deps['weex-vue-framework']}, Rax: ${deps['weex-rax-framework']})');\n`
+        + frameworkBanner
+    }
+  },
+  'weex-vue': {
+    input: absolute('runtime/entries/vue.js'),
+    output: {
+      name: 'WeexVue',
+      file: absolute('pre-build/weex-vue'),
+      banner: `(this.nativeLog || function(s) {console.log(s)})`
+        + `('Weex JS Framework ${subversion.framework}, Build ${now()}. `
+        + `(Vue: ${deps['weex-vue-framework']})');\n`
+        + frameworkBanner
+    }
+  },
+  'weex-rax': {
+    input: absolute('runtime/entries/rax.js'),
+    output: {
+      name: 'WeexRax',
+      file: absolute('pre-build/weex-rax'),
+      banner: `(this.nativeLog || function(s) {console.log(s)})`
+        + `('Weex JS Framework ${subversion.framework}, Build ${now()}. `
+        + `(Rax: ${deps['weex-rax-framework']})');\n`
+        + frameworkBanner
+    }
   },
   'weex-js-runtime': {
-    moduleName: 'WeexRuntime',
-    entry: absolute('html5/runtime/index.js'),
-    dest: absolute('packages/weex-js-runtime/index.js'),
-    banner: `/* WEEX JS RUNTIME ${subversion.framework}, Build ${now()}. */\n\n`,
-    format: 'umd',
-    plugins: [
-      nodeResolve({
-        jsnext: true,
-        main: true
-      }),
-    ]
+    input: absolute('runtime/api/index.js'),
+    output: {
+      name: 'WeexJSRuntime',
+      file: absolute('packages/weex-js-runtime/index'),
+      banner: `/* Weex JS Runtime ${subversion.framework}, Build ${now()}. */\n\n`
+    }
   },
   'weex-legacy-framework': {
-    moduleName: 'WeexLegacyFramework',
-    entry: absolute('html5/frameworks/legacy/index.js'),
-    dest: absolute('packages/weex-legacy-framework/index.js'),
-    banner: `/* Weex Legacy Framework ${subversion.framework}, Build ${now()}. */\n`,
-    format: 'umd',
-    plugins: [
-      nodeResolve({
-        jsnext: true,
-        main: true
-      }),
-    ]
+    input: absolute('runtime/frameworks/legacy/index.js'),
+    output: {
+      name: 'WeexLegacyFramework',
+      file: absolute('packages/weex-legacy-framework/index'),
+      banner: `/* Weex Legacy Framework ${subversion.framework}, Build ${now()}. */\n`
+    }
   },
   'weex-vanilla-framework': {
-    moduleName: 'WeexVanillaFramework',
-    entry: absolute('html5/frameworks/vanilla/index.js'),
-    dest: absolute('packages/weex-vanilla-framework/index.js'),
-    banner: `/* Weex Vanilla Framework ${subversion.framework}, Build ${now()}. */\n`,
-    format: 'umd',
-    plugins: [
-      nodeResolve({
-        jsnext: true,
-        main: true
-      }),
-    ]
+    input: absolute('runtime/frameworks/vanilla/index.js'),
+    output: {
+      name: 'WeexVanillaFramework',
+      file: absolute('packages/weex-vanilla-framework/index'),
+      banner: `/* Weex Vanilla Framework ${subversion.framework}, Build ${now()}. */\n`
+    }
   }
 }
 
-function getConfig (name, minify) {
+function getConfig (name, minify, es6) {
   const opt = configs[name]
+  if (!opt.plugins) {
+    opt.plugins = []
+  }
+  const output = opt.output
+  const suffix = `${es6 ? '.es6' : ''}${minify ? '.min' : ''}.js`
   const config = {
-    moduleName: opt.moduleName,
-    entry: opt.entry,
-    dest: minify ? opt.dest && opt.dest.replace(/\.js$/, '.min.js') : opt.dest,
-    format: opt.format,
-    banner: opt.banner,
+    input: opt.input,
+    output: {
+      name: output.name,
+      file: output.file + suffix,
+      format: output.format || 'umd',
+      banner: output.banner
+    },
     plugins: opt.plugins.concat([
+      nodeResolve({ jsnext: true, main: true }),
       json(),
       replace({
-        'process.env.VIEWPORT_WIDTH': 750,
+        '__WEEX_VERSION__': JSON.stringify(subversion.framework),
         'process.env.NODE_ENV': JSON.stringify(minify ? 'production' : 'development'),
         'process.env.VUE_ENV': JSON.stringify('WEEX'),
+        'process.env.SUPPORT_ES2015': !!es6,
         'process.env.NODE_DEBUG': false
       }),
-      commonjs(),
-      buble()
+      commonjs()
     ])
   }
-
+  if (!es6) {
+    config.plugins.push(buble())
+  }
   if (minify) {
-    config.plugins.push(uglify())
+    config.output.sourcemap = true
+    config.plugins.push(es6
+      ? uglify({ safari10: true, toplevel: true }, uglifyES.minify)
+      : uglify()
+    )
   }
   else {
-    config.sourceMap = 'inline'
+    config.output.sourcemap = 'inline'
     config.plugins.unshift(eslint({ exclude: ['**/*.json', '**/*.css'] }))
   }
-
   return config
 }
 

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/9dd6cba1/dangerfile.js
----------------------------------------------------------------------
diff --git a/dangerfile.js b/dangerfile.js
index 40cbadb..660c4af 100644
--- a/dangerfile.js
+++ b/dangerfile.js
@@ -72,12 +72,12 @@ if (unFlowedFiles.length > 0) {
 // Error or Warn when delete public interface
 var isNotDanger = false;
 console.log('pr.title:'+danger.github.pr.title)
-if(!isNotDanger && danger.github.pr.title 
+if(!isNotDanger && danger.github.pr.title
   && danger.github.pr.title.match(/@notdanger/i)){
   isNotDanger = true;
 }
 console.log('pr.body:'+danger.github.pr.body)
-if(!isNotDanger && danger.github.pr.body 
+if(!isNotDanger && danger.github.pr.body
   && danger.github.pr.body.match(/@notdanger/i)){
   isNotDanger = true;
 }
@@ -146,10 +146,10 @@ const getFileType = file => {
   } else if (file.match(/android\/sdk\/src\/main\/java\/.+\.java/)) {
     return type_android_sdk;
   } else if (
-    file.match(/html5\/(shared|frameworks|render|runtime|services)\/.+\.js/)
+    file.match(/runtime\/.+\.js/)
   ) {
     return type_jsfm;
-  } else if (file.match(/html5\/test\/.+\.js/)) {
+  } else if (file.match(/test\/js-framework\/.+\.js/)) {
     return type_jsfm_test;
   } else if (file.match(/doc\/\.+\.md/)) {
     return type_doc;
@@ -250,7 +250,8 @@ const ignoreCopyrightVerifyPath = [
   'test',
   'packages',
   'pre-build',
-  'html5/test/case',
+  'runtime/frameworks/legacy/core',
+  'test/js\-framework/case',
   'android/playground/app/src/main/assets',
   'android/sdk/assets',
   'ios/playground/bundlejs',
@@ -275,7 +276,7 @@ filesToVerifySrcHeader.forEach(filepath => {
   }
 
   // check cn for source code
-  var reg = /[\u4e00-\u9FA5]+/; 
+  var reg = /[\u4e00-\u9FA5]+/;
   var res = reg.test(content);
   if(res){
     console.error("Code file "+ filepath +" has cn source code.");
@@ -356,7 +357,7 @@ function getContent(url) {
         console.log('response:', response.statusCode)
         if (response.statusCode < 200 || response.statusCode > 299) {
           if (response.statusCode === 404  || response.statusCode === 502) {
-            // ignore this, probably a renamed file,or .so that can't blame 
+            // ignore this, probably a renamed file,or .so that can't blame
             return resolve('')
           }
           reject(new Error('Failed to load page, status code: ' + response.statusCode + ', '

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/9dd6cba1/package.json
----------------------------------------------------------------------
diff --git a/package.json b/package.json
index 0282224..636a1f0 100644
--- a/package.json
+++ b/package.json
@@ -32,11 +32,13 @@
     "yunos"
   ],
   "engines": {
-    "node": ">=4"
+    "node": ">=8"
   },
   "scripts": {
     "postinstall": "bash ./scripts/install-hooks.sh",
     "build:native": "node build/build.js native",
+    "build:vue": "node build/build.js vue",
+    "build:rax": "node build/build.js rax",
     "build:runtime": "node build/build.js runtime",
     "build:legacy": "node build/build.js legacy",
     "build:vanilla": "node build/build.js vanilla",
@@ -58,8 +60,8 @@
     "dev:examples:web": "webpack --watch --config build/webpack.examples.web.config.js",
     "dev:test": "webpack --watch --config build/webpack.test.config.js",
     "lint": "eslint html5",
-    "test:case": "mocha --require reify html5/test/case/tester.js",
-    "test:unit": "mocha --require reify html5/test/unit/**/*",
+    "test:case": "mocha --require reify test/js-framework/case/tester.js",
+    "test:unit": "mocha --require reify test/js-framework/unit/**/*",
     "test": "npm run lint && npm run test:unit && npm run test:case",
     "test:cover-html": "babel-istanbul cover --report html node_modules/mocha/bin/_mocha -- --require reify --reporter dot html5/test/unit/ && open coverage/index.html",
     "test:cover": "babel-istanbul cover --report text node_modules/mocha/bin/_mocha -- --require reify --reporter dot html5/test/unit/",
@@ -100,6 +102,7 @@
     "danger": "^0.18.0",
     "dateformat": "^2.0.0",
     "eslint": "^2.11.1",
+    "eslint-plugin-flowtype": "^2.40.1",
     "fs-extra": "^2.0.0",
     "github": "^9.2.0",
     "http-server": "^0.9.0",
@@ -110,20 +113,21 @@
     "parse-diff": "^0.4.0",
     "phantomjs-prebuilt": "^2.1.7",
     "reify": "^0.3.8",
-    "rollup": "^0.36.0",
-    "rollup-plugin-buble": "^0.14.0",
-    "rollup-plugin-commonjs": "^5.0.4",
-    "rollup-plugin-eslint": "^3.0.0",
-    "rollup-plugin-json": "^2.0.2",
-    "rollup-plugin-node-resolve": "^2.0.0",
-    "rollup-plugin-postcss": "^0.2.0",
-    "rollup-plugin-replace": "^1.1.1",
+    "rollup": "^0.53.2",
+    "rollup-plugin-buble": "^0.18.0",
+    "rollup-plugin-commonjs": "^8.2.6",
+    "rollup-plugin-eslint": "^4.0.0",
+    "rollup-plugin-json": "^2.3.0",
+    "rollup-plugin-node-resolve": "^3.0.0",
+    "rollup-plugin-replace": "^2.0.0",
     "rollup-plugin-uglify": "^2.0.1",
-    "rollup-watch": "^2.5.0",
+    "rollup-watch": "^4.3.1",
     "selenium-server": "2.53.1",
     "serve": "^1.4.0",
+    "shelljs": "^0.7.8",
     "sinon": "^2.1.0",
     "sinon-chai": "^2.8.0",
+    "uglify-es": "^3.3.4",
     "uglify-js": "^2.8.29",
     "vue": "2.4.3",
     "vue-loader": "^12.2.1",
@@ -134,7 +138,6 @@
     "weex-loader": "^0.5.3",
     "weex-vdom-tester": "^0.2.0",
     "weex-wd": "^1.0.23",
-    "xmldom": "^0.1.27",
-    "shelljs": "^0.7.8"
+    "xmldom": "^0.1.27"
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/9dd6cba1/runtime/vdom/Element.js
----------------------------------------------------------------------
diff --git a/runtime/vdom/Element.js b/runtime/vdom/Element.js
index 3160b6d..8e7b572 100644
--- a/runtime/vdom/Element.js
+++ b/runtime/vdom/Element.js
@@ -133,8 +133,8 @@ export default class Element extends Node {
           node,
           this.pureChildren,
           pureBefore
-          ? this.pureChildren.indexOf(pureBefore)
-          : this.pureChildren.length
+            ? this.pureChildren.indexOf(pureBefore)
+            : this.pureChildren.length
         )
         const taskCenter = getTaskCenter(this.docId)
         if (taskCenter) {
@@ -155,8 +155,8 @@ export default class Element extends Node {
           node,
           this.pureChildren,
           pureBefore
-          ? this.pureChildren.indexOf(pureBefore)
-          : this.pureChildren.length
+            ? this.pureChildren.indexOf(pureBefore)
+            : this.pureChildren.length
         )
         const taskCenter = getTaskCenter(this.docId)
         if (taskCenter && index >= 0) {


[05/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/multi/clear-moduleB.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/multi/clear-moduleB.source.js b/test/js-framework/case/multi/clear-moduleB.source.js
new file mode 100644
index 0000000..c6338fd
--- /dev/null
+++ b/test/js-framework/case/multi/clear-moduleB.source.js
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+define('@weex-component/moduleB', function(require, exports, module) {
+
+  ;module.exports = {
+    data: {
+      text: ''
+    },
+    created: function() {
+      var smp = require('sample')
+      this.text = smp ? smp.name : 'nothing'
+    }
+  }
+
+  ;module.exports.style = {}
+
+  ;module.exports.template = {
+	  "type": "div",
+	  "children": [
+	    {
+	      "type": "text",
+	      "attr": {
+	        "value": function() { return this.text }
+	      }
+	    }
+	  ]
+  }
+
+})
+
+// require module
+bootstrap('@weex-component/moduleB', {
+    "transformerVersion": "0.3.1"
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/prepare.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/prepare.js b/test/js-framework/case/prepare.js
new file mode 100644
index 0000000..03d21be
--- /dev/null
+++ b/test/js-framework/case/prepare.js
@@ -0,0 +1,83 @@
+/*
+ * 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 fs from 'fs'
+import path from 'path'
+
+// load test driver
+import {
+  Runtime,
+  Instance
+} from 'weex-vdom-tester'
+
+// load env
+import shared from '../../../runtime/shared'
+import { Document, Element, Comment } from '../../../runtime/vdom'
+import Listener from '../../../runtime/bridge/Listener'
+import { TaskCenter, init } from '../../../runtime/bridge/TaskCenter'
+
+// load framework
+import * as defaultFramework from '../../../runtime/frameworks/legacy'
+import { subversion } from '../../../package.json'
+
+shared.setNativeConsole()
+
+// mock config & global APIs
+global.callNativeHandler = function () {}
+
+// init special API called `callAddElement()`
+// which is supported temporarily in native render
+global.callAddElement = function (id, ref, json, index) {
+  return callNativeHandler(id, [{ module: 'dom', method: 'addElement', args: [ref, json, index] }])
+}
+
+init()
+
+// create test driver runtime
+export function createRuntime () {
+  const config = {
+    Document, Element, Comment, Listener,
+    TaskCenter,
+    sendTasks (...args) {
+      return callNativeHandler(...args)
+    }
+  }
+
+  Document.Listener = Listener
+  Document.handler = config.sendTasks
+
+  defaultFramework.init(config)
+
+  Object.assign(global, {
+    frameworkVersion: subversion.native,
+    transformerVersion: subversion.transformer
+  })
+
+  return new Runtime(defaultFramework)
+}
+
+// create framework instance
+export function createApp (runtime) {
+  return new Instance(runtime)
+}
+
+// read source code from file
+export function getCode (filePath) {
+  const absolutPath = path.join(__dirname, filePath)
+  return fs.readFileSync(absolutPath, 'utf8')
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/signals/modal.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/signals/modal.output.js b/test/js-framework/case/signals/modal.output.js
new file mode 100644
index 0000000..f645aa2
--- /dev/null
+++ b/test/js-framework/case/signals/modal.output.js
@@ -0,0 +1,9 @@
+{
+  type: 'div',
+  children: [{
+    type: 'text',
+    attr: {
+      value: 'foo'
+    }
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/signals/modal.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/signals/modal.source.js b/test/js-framework/case/signals/modal.source.js
new file mode 100644
index 0000000..bdef1b8
--- /dev/null
+++ b/test/js-framework/case/signals/modal.source.js
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+define('@weex-component/modal', function (require, exports, module) {
+
+;
+  var modal = require('@weex-module/modal')
+  module.exports = {
+    data: function () {
+      return {
+        x: 'foo'
+      }
+    },
+    created: function () {
+      var message = 'Here is a message for you!'
+      var okTitle = 'OK'
+      modal.alert({
+        message: message,
+        okTitle: okTitle
+      }, function() {
+        this.x = 'bar'
+      }.bind(this))
+    }
+  }
+
+
+;module.exports.template = {
+  "type": "div",
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "value": function () {
+          return this.x
+        }
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/modal')


[09/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/app/downgrade.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/app/downgrade.js b/runtime/frameworks/legacy/app/downgrade.js
new file mode 100644
index 0000000..327d70a
--- /dev/null
+++ b/runtime/frameworks/legacy/app/downgrade.js
@@ -0,0 +1,172 @@
+/*
+ * 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 semver from 'semver'
+import { isPlainObject, typof } from '../util/index'
+
+/**
+ * Normalize a version string.
+ * @param  {String} Version. ie: 1, 1.0, 1.0.0
+ * @return {String} Version
+ */
+export function normalizeVersion (v) {
+  const isValid = semver.valid(v)
+  if (isValid) {
+    return v
+  }
+
+  v = typeof (v) === 'string' ? v : ''
+  const split = v.split('.')
+  let i = 0
+  const result = []
+
+  while (i < 3) {
+    const s = typeof (split[i]) === 'string' && split[i] ? split[i] : '0'
+    result.push(s)
+    i++
+  }
+
+  return result.join('.')
+}
+
+/**
+ * Get informations from different error key. Like:
+ * - code
+ * - errorMessage
+ * - errorType
+ * - isDowngrade
+ * @param  {string} key
+ * @param  {string} val
+ * @param  {string} criteria
+ * @return {object}
+ */
+export function getError (key, val, criteria) {
+  const result = {
+    isDowngrade: true,
+    errorType: 1,
+    code: 1000
+  }
+  const getMsg = function (key, val, criteria) {
+    return 'Downgrade[' + key + '] :: deviceInfo '
+      + val + ' matched criteria ' + criteria
+  }
+  const _key = key.toLowerCase()
+
+  result.errorMessage = getMsg(key, val, criteria)
+
+  if (_key.indexOf('osversion') >= 0) {
+    result.code = 1001
+  }
+  else if (_key.indexOf('appversion') >= 0) {
+    result.code = 1002
+  }
+  else if (_key.indexOf('weexversion') >= 0) {
+    result.code = 1003
+  }
+  else if (_key.indexOf('devicemodel') >= 0) {
+    result.code = 1004
+  }
+
+  return result
+}
+
+/**
+ * WEEX framework input(deviceInfo)
+ * {
+ *   platform: 'iOS' or 'android'
+ *   osVersion: '1.0.0' or '1.0' or '1'
+ *   appVersion: '1.0.0' or '1.0' or '1'
+ *   weexVersion: '1.0.0' or '1.0' or '1'
+ *   dDeviceModel: 'MODEL_NAME'
+ * }
+ *
+ * downgrade config(config)
+ * {
+ *   ios: {
+ *     osVersion: '>1.0.0' or '>=1.0.0' or '<1.0.0' or '<=1.0.0' or '1.0.0'
+ *     appVersion: '>1.0.0' or '>=1.0.0' or '<1.0.0' or '<=1.0.0' or '1.0.0'
+ *     weexVersion: '>1.0.0' or '>=1.0.0' or '<1.0.0' or '<=1.0.0' or '1.0.0'
+ *     deviceModel: ['modelA', 'modelB', ...]
+ *   },
+ *   android: {
+ *     osVersion: '>1.0.0' or '>=1.0.0' or '<1.0.0' or '<=1.0.0' or '1.0.0'
+ *     appVersion: '>1.0.0' or '>=1.0.0' or '<1.0.0' or '<=1.0.0' or '1.0.0'
+ *     weexVersion: '>1.0.0' or '>=1.0.0' or '<1.0.0' or '<=1.0.0' or '1.0.0'
+ *     deviceModel: ['modelA', 'modelB', ...]
+ *   }
+ * }
+ *
+ *
+ * @param  {object} deviceInfo Weex SDK framework input
+ * @param  {object} config     user input
+ * @return {Object}            { isDowngrade: true/false, errorMessage... }
+ */
+export function check (config, deviceInfo) {
+  deviceInfo = deviceInfo || global.WXEnvironment
+  deviceInfo = isPlainObject(deviceInfo) ? deviceInfo : {}
+
+  let result = {
+    isDowngrade: false // defautl is pass
+  }
+
+  if (typof(config) === 'function') {
+    let customDowngrade = config.call(this, deviceInfo, {
+      semver: semver,
+      normalizeVersion
+    })
+
+    customDowngrade = !!customDowngrade
+
+    result = customDowngrade ? getError('custom', '', 'custom params') : result
+  }
+  else {
+    config = isPlainObject(config) ? config : {}
+
+    const platform = deviceInfo.platform || 'unknow'
+    const dPlatform = platform.toLowerCase()
+    const cObj = config[dPlatform] || {}
+
+    for (const i in deviceInfo) {
+      const key = i
+      const keyLower = key.toLowerCase()
+      const val = deviceInfo[i]
+      const isVersion = keyLower.indexOf('version') >= 0
+      const isDeviceModel = keyLower.indexOf('devicemodel') >= 0
+      const criteria = cObj[i]
+
+      if (criteria && isVersion) {
+        const c = normalizeVersion(criteria)
+        const d = normalizeVersion(deviceInfo[i])
+
+        if (semver.satisfies(d, c)) {
+          result = getError(key, val, criteria)
+          break
+        }
+      }
+      else if (isDeviceModel) {
+        const _criteria = typof(criteria) === 'array' ? criteria : [criteria]
+        if (_criteria.indexOf(val) >= 0) {
+          result = getError(key, val, criteria)
+          break
+        }
+      }
+    }
+  }
+
+  return result
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/app/index.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/app/index.js b/runtime/frameworks/legacy/app/index.js
new file mode 100644
index 0000000..5ae389d
--- /dev/null
+++ b/runtime/frameworks/legacy/app/index.js
@@ -0,0 +1,55 @@
+/*
+ * 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
+ * Weex instance constructor & definition
+ */
+
+import { requireModule } from './register'
+import { updateActions, callTasks } from './ctrl/index'
+import App from './instance'
+
+/**
+ * @deprecated
+ */
+App.prototype.requireModule = function (name) {
+  return requireModule(this, name)
+}
+
+/**
+ * @deprecated
+ */
+App.prototype.updateActions = function () {
+  return updateActions(this)
+}
+
+/**
+ * @deprecated
+ */
+App.prototype.callTasks = function (tasks) {
+  return callTasks(this, tasks)
+}
+
+/**
+ * Prevent modification of App and App.prototype
+ */
+Object.freeze(App)
+Object.freeze(App.prototype)
+
+export default App

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/app/instance.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/app/instance.js b/runtime/frameworks/legacy/app/instance.js
new file mode 100644
index 0000000..55bb4c5
--- /dev/null
+++ b/runtime/frameworks/legacy/app/instance.js
@@ -0,0 +1,47 @@
+/*
+ * 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
+ * Weex App constructor & definition
+ */
+
+import Differ from './differ'
+import renderer from '../config'
+
+/**
+ * App constructor for Weex framework.
+ * @param {string} id
+ * @param {object} options
+ */
+export default function App (id, options) {
+  this.id = id
+  this.options = options || {}
+  this.vm = null
+  this.customComponentMap = {}
+  this.commonModules = {}
+
+  // document
+  this.doc = new renderer.Document(
+    id,
+    this.options.bundleUrl,
+    null,
+    renderer.Listener
+  )
+  this.differ = new Differ(id)
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/app/register.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/app/register.js b/runtime/frameworks/legacy/app/register.js
new file mode 100644
index 0000000..bd0302e
--- /dev/null
+++ b/runtime/frameworks/legacy/app/register.js
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+let nativeModules = {}
+
+// for testing
+
+/**
+ * for testing
+ */
+export function getModule (moduleName) {
+  return nativeModules[moduleName]
+}
+
+/**
+ * for testing
+ */
+export function clearModules () {
+  nativeModules = {}
+}
+
+// for framework
+
+/**
+ * init modules for an app instance
+ * the second param determines whether to replace an existed method
+ */
+export function initModules (modules, ifReplace) {
+  for (const moduleName in modules) {
+    // init `modules[moduleName][]`
+    let methods = nativeModules[moduleName]
+    if (!methods) {
+      methods = {}
+      nativeModules[moduleName] = methods
+    }
+
+    // push each non-existed new method
+    modules[moduleName].forEach(function (method) {
+      if (typeof method === 'string') {
+        method = {
+          name: method
+        }
+      }
+
+      if (!methods[method.name] || ifReplace) {
+        methods[method.name] = method
+      }
+    })
+  }
+}
+
+/**
+ * init app methods
+ */
+export function initMethods (Vm, apis) {
+  const p = Vm.prototype
+
+  for (const apiName in apis) {
+    if (!p.hasOwnProperty(apiName)) {
+      p[apiName] = apis[apiName]
+    }
+  }
+}
+
+/**
+ * get a module of methods for an app instance
+ */
+export function requireModule (app, name) {
+  const methods = nativeModules[name]
+  const target = {}
+  for (const methodName in methods) {
+    Object.defineProperty(target, methodName, {
+      configurable: true,
+      enumerable: true,
+      get: function moduleGetter () {
+        return (...args) => app.callTasks({
+          module: name,
+          method: methodName,
+          args: args
+        })
+      },
+      set: function moduleSetter (value) {
+        if (typeof value === 'function') {
+          return app.callTasks({
+            module: name,
+            method: methodName,
+            args: [value]
+          })
+        }
+      }
+    })
+  }
+  return target
+}
+
+/**
+ * get a custom component options
+ */
+export function requireCustomComponent (app, name) {
+  const { customComponentMap } = app
+  return customComponentMap[name]
+}
+
+/**
+ * register a custom component options
+ */
+export function registerCustomComponent (app, name, def) {
+  const { customComponentMap } = app
+
+  if (customComponentMap[name]) {
+    console.error(`[JS Framework] define a component(${name}) that already exists`)
+    return
+  }
+
+  customComponentMap[name] = def
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/app/viewport.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/app/viewport.js b/runtime/frameworks/legacy/app/viewport.js
new file mode 100644
index 0000000..60f8eff
--- /dev/null
+++ b/runtime/frameworks/legacy/app/viewport.js
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+export function setViewport (app, configs = {}) {
+  /* istanbul ignore if */
+  if (process.env.NODE_ENV === 'development') {
+    console.debug(`[JS Framework] Set viewport (width: ${configs.width}) for app#${app.id}.`)
+    validateViewport(configs)
+  }
+
+  // Send viewport configs to native
+  if (app && app.callTasks) {
+    return app.callTasks([{
+      module: 'meta',
+      method: 'setViewport',
+      args: [configs]
+    }])
+  }
+
+  /* istanbul ignore next */
+  else if (process.env.NODE_ENV === 'development') {
+    console.warn(`[JS Framework] Can't find "callTasks" method on current app.`)
+  }
+}
+
+/**
+ * Validate the viewport config.
+ * @param {Object} configs
+ */
+export function validateViewport (configs = {}) {
+  const { width } = configs
+  if (width) {
+    if (typeof width !== 'number' && width !== 'device-width') {
+      console.warn(`[JS Framework] Not support to use ${width} as viewport width.`)
+      return false
+    }
+    return true
+  }
+  console.warn('[JS Framework] the viewport config should contain the "width" property.')
+  return false
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/config.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/config.js b/runtime/frameworks/legacy/config.js
new file mode 100644
index 0000000..ebf64d2
--- /dev/null
+++ b/runtime/frameworks/legacy/config.js
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+// @todo: It should be registered by native from `registerComponents()`.
+
+export default {
+  nativeComponentMap: {
+    text: true,
+    image: true,
+    container: true,
+    slider: {
+      type: 'slider',
+      append: 'tree'
+    },
+    cell: {
+      type: 'cell',
+      append: 'tree'
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/core/LICENSE
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/core/LICENSE b/runtime/frameworks/legacy/core/LICENSE
new file mode 100644
index 0000000..4645bc4
--- /dev/null
+++ b/runtime/frameworks/legacy/core/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2015 Yuxi Evan You
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/core/array.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/core/array.js b/runtime/frameworks/legacy/core/array.js
new file mode 100644
index 0000000..856792f
--- /dev/null
+++ b/runtime/frameworks/legacy/core/array.js
@@ -0,0 +1,100 @@
+/* eslint-disable */
+
+
+import { def } from '../util/index'
+
+const arrayProto = Array.prototype
+export const arrayMethods = Object.create(arrayProto)
+
+/**
+ * Intercept mutating methods and emit events
+ */
+
+;[
+  'push',
+  'pop',
+  'shift',
+  'unshift',
+  'splice',
+  'sort',
+  'reverse'
+]
+.forEach(function (method) {
+  // cache original method
+  const original = arrayProto[method]
+  def(arrayMethods, method, function mutator () {
+    // avoid leaking arguments:
+    // http://jsperf.com/closure-with-arguments
+    let i = arguments.length
+    const args = new Array(i)
+    while (i--) {
+      args[i] = arguments[i]
+    }
+    const result = original.apply(this, args)
+    const ob = this.__ob__
+    let inserted
+    switch (method) {
+      case 'push':
+        inserted = args
+        break
+      case 'unshift':
+        inserted = args
+        break
+      case 'splice':
+        inserted = args.slice(2)
+        break
+    }
+    if (inserted) ob.observeArray(inserted)
+    // notify change
+    ob.dep.notify()
+    return result
+  })
+})
+
+/**
+ * Swap the element at the given index with a new value
+ * and emits corresponding event.
+ *
+ * @param {Number} index
+ * @param {*} val
+ * @return {*} - replaced element
+ */
+
+def(
+  arrayProto,
+  '$set',
+  function $set (index, val) {
+    console.warn(`[JS Framework] "Array.prototype.$set" is not a standard API,`
+      + ` it will be removed in the next version.`)
+    if (index >= this.length) {
+      this.length = index + 1
+    }
+    return this.splice(index, 1, val)[0]
+  }
+)
+
+/**
+ * Convenience method to remove the element at given index.
+ *
+ * @param {Number} index
+ * @param {*} val
+ */
+
+def(
+  arrayProto,
+  '$remove',
+  function $remove (index) {
+    console.warn(`[JS Framework] "Array.prototype.$remove" is not a standard API,`
+      + ` it will be removed in the next version.`)
+    /* istanbul ignore if */
+    if (!this.length) return
+    /* istanbul ignore else */
+    if (typeof index !== 'number') {
+      index = this.indexOf(index)
+    }
+    /* istanbul ignore else */
+    if (index > -1) {
+      this.splice(index, 1)
+    }
+  }
+)

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/core/dep.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/core/dep.js b/runtime/frameworks/legacy/core/dep.js
new file mode 100644
index 0000000..6c29bf6
--- /dev/null
+++ b/runtime/frameworks/legacy/core/dep.js
@@ -0,0 +1,80 @@
+/* eslint-disable */
+
+
+import { remove } from '../util/index'
+
+let uid = 0
+
+/**
+ * A dep is an observable that can have multiple
+ * directives subscribing to it.
+ *
+ * @constructor
+ */
+
+export default function Dep () {
+  this.id = uid++
+  this.subs = []
+}
+
+// the current target watcher being evaluated.
+// this is globally unique because there could be only one
+// watcher being evaluated at any time.
+Dep.target = null
+let targetStack = []
+
+export function pushTarget (_target) {
+  if (Dep.target) targetStack.push(Dep.target)
+  Dep.target = _target
+}
+
+export function popTarget () {
+  Dep.target = targetStack.pop()
+}
+
+export function resetTarget () {
+  Dep.target = null
+  targetStack = []
+}
+
+/**
+ * Add a directive subscriber.
+ *
+ * @param {Directive} sub
+ */
+
+Dep.prototype.addSub = function (sub) {
+  this.subs.push(sub)
+}
+
+/**
+ * Remove a directive subscriber.
+ *
+ * @param {Directive} sub
+ */
+
+Dep.prototype.removeSub = function (sub) {
+  remove(this.subs, sub)
+}
+
+/**
+ * Add self as a dependency to the target watcher.
+ */
+
+Dep.prototype.depend = function () {
+  if (Dep.target) {
+    Dep.target.addDep(this)
+  }
+}
+
+/**
+ * Notify all subscribers of a new value.
+ */
+
+Dep.prototype.notify = function () {
+  // stablize the subscriber list first
+  const subs = this.subs.slice()
+  for (let i = 0, l = subs.length; i < l; i++) {
+    subs[i].update()
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/core/object.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/core/object.js b/runtime/frameworks/legacy/core/object.js
new file mode 100644
index 0000000..86f83e3
--- /dev/null
+++ b/runtime/frameworks/legacy/core/object.js
@@ -0,0 +1,91 @@
+/* eslint-disable */
+
+
+import * as _ from '../util/index'
+var objProto = Object.prototype
+
+/**
+ * Add a new property to an observed object
+ * and emits corresponding event
+ *
+ * @param {String} key
+ * @param {*} val
+ * @public
+ */
+
+_.define(
+  objProto,
+  '$add',
+  function $add (key, val) {
+    console.warn(`[JS Framework] "Object.prototype.$add" is not a standard API,`
+      + ` it will be removed in the next version.`)
+    if (this.hasOwnProperty(key)) return
+    var ob = this.__ob__
+    if (!ob || _.isReserved(key)) {
+      this[key] = val
+      return
+    }
+    ob.convert(key, val)
+    ob.notify()
+    if (ob.vms) {
+      var i = ob.vms.length
+      while (i--) {
+        var vm = ob.vms[i]
+        vm._proxy(key)
+        // vm._digest() // todo
+      }
+    }
+  }
+)
+
+/**
+ * Set a property on an observed object, calling add to
+ * ensure the property is observed.
+ *
+ * @param {String} key
+ * @param {*} val
+ * @public
+ */
+
+_.define(
+  objProto,
+  '$set',
+  function $set (key, val) {
+    console.warn(`[JS Framework] "Object.prototype.$set" is not a standard API,`
+      + ` it will be removed in the next version.`)
+    this.$add(key, val)
+    this[key] = val
+  }
+)
+
+/**
+ * Deletes a property from an observed object
+ * and emits corresponding event
+ *
+ * @param {String} key
+ * @public
+ */
+
+_.define(
+  objProto,
+  '$delete',
+  function $delete (key) {
+    console.warn(`[JS Framework] "Object.prototype.$delete" is not a standard API,`
+      + ` it will be removed in the next version.`)
+    if (!this.hasOwnProperty(key)) return
+    delete this[key]
+    var ob = this.__ob__
+    if (!ob || _.isReserved(key)) {
+      return
+    }
+    ob.notify()
+    if (ob.vms) {
+      var i = ob.vms.length
+      while (i--) {
+        var vm = ob.vms[i]
+        vm._unproxy(key)
+        // vm._digest() // todo
+      }
+    }
+  }
+)

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/core/observer.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/core/observer.js b/runtime/frameworks/legacy/core/observer.js
new file mode 100644
index 0000000..103dd0d
--- /dev/null
+++ b/runtime/frameworks/legacy/core/observer.js
@@ -0,0 +1,323 @@
+/* eslint-disable */
+
+
+import Dep from './dep'
+import { arrayMethods } from './array'
+import {
+  def,
+  remove,
+  isObject,
+  isPlainObject,
+  hasProto,
+  hasOwn,
+  isReserved
+} from '../util/index'
+
+const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
+
+/**
+ * Observer class that are attached to each observed
+ * object. Once attached, the observer converts target
+ * object's property keys into getter/setters that
+ * collect dependencies and dispatches updates.
+ *
+ * @param {Array|Object} value
+ * @constructor
+ */
+
+export function Observer (value) {
+  this.value = value
+  this.dep = new Dep()
+  def(value, '__ob__', this)
+  if (Array.isArray(value)) {
+    const augment = hasProto
+      ? protoAugment
+      : copyAugment
+    augment(value, arrayMethods, arrayKeys)
+    this.observeArray(value)
+  } else {
+    this.walk(value)
+  }
+}
+
+// Instance methods
+
+/**
+ * Walk through each property and convert them into
+ * getter/setters. This method should only be called when
+ * value type is Object.
+ *
+ * @param {Object} obj
+ */
+
+Observer.prototype.walk = function (obj) {
+  for (let key in obj) {
+    this.convert(key, obj[key])
+  }
+}
+
+/**
+ * Observe a list of Array items.
+ *
+ * @param {Array} items
+ */
+
+Observer.prototype.observeArray = function (items) {
+  for (let i = 0, l = items.length; i < l; i++) {
+    observe(items[i])
+  }
+}
+
+/**
+ * Convert a property into getter/setter so we can emit
+ * the events when the property is accessed/changed.
+ *
+ * @param {String} key
+ * @param {*} val
+ */
+
+Observer.prototype.convert = function (key, val) {
+  defineReactive(this.value, key, val)
+}
+
+/**
+ * Add an owner vm, so that when $set/$delete mutations
+ * happen we can notify owner vms to proxy the keys and
+ * digest the watchers. This is only called when the object
+ * is observed as an instance's root $data.
+ *
+ * @param {Vue} vm
+ */
+
+Observer.prototype.addVm = function (vm) {
+  (this.vms || (this.vms = [])).push(vm)
+}
+
+/**
+ * Remove an owner vm. This is called when the object is
+ * swapped out as an instance's $data object.
+ *
+ * @param {Vue} vm
+ */
+
+/* istanbul ignore next */
+Observer.prototype.removeVm = function (vm) {
+  remove(this.vms, vm)
+}
+
+// helpers
+
+/**
+ * Augment an target Object or Array by intercepting
+ * the prototype chain using __proto__
+ *
+ * @param {Object|Array} target
+ * @param {Object} src
+ */
+
+function protoAugment (target, src) {
+  /* eslint-disable no-proto */
+  target.__proto__ = src
+  /* eslint-enable no-proto */
+}
+
+/**
+ * Augment an target Object or Array by defining
+ * hidden properties.
+ *
+ * @param {Object|Array} target
+ * @param {Object} proto
+ */
+
+/* istanbul ignore next */
+function copyAugment (target, src, keys) {
+  for (let i = 0, l = keys.length; i < l; i++) {
+    const key = keys[i]
+    def(target, key, src[key])
+  }
+}
+
+/**
+ * Attempt to create an observer instance for a value,
+ * returns the new observer if successfully observed,
+ * or the existing observer if the value already has one.
+ *
+ * @param {*} value
+ * @param {Vue} [vm]
+ * @return {Observer|undefined}
+ * @static
+ */
+
+export function observe (value, vm) {
+  if (!isObject(value)) {
+    return
+  }
+  let ob
+  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
+    ob = value.__ob__
+  } else if (
+    (Array.isArray(value) || isPlainObject(value)) &&
+    Object.isExtensible(value) &&
+    !value._isVue
+  ) {
+    ob = new Observer(value)
+  }
+  if (ob && vm) {
+    ob.addVm(vm)
+  }
+  return ob
+}
+
+/**
+ * Define a reactive property on an Object.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @param {*} val
+ */
+
+export function defineReactive (obj, key, val) {
+  const dep = new Dep()
+
+  const property = Object.getOwnPropertyDescriptor(obj, key)
+  if (property && property.configurable === false) {
+    return
+  }
+
+  // cater for pre-defined getter/setters
+  const getter = property && property.get
+  const setter = property && property.set
+
+  let childOb = observe(val)
+  Object.defineProperty(obj, key, {
+    enumerable: true,
+    configurable: true,
+    get: function reactiveGetter () {
+      const value = getter ? getter.call(obj) : val
+      if (Dep.target) {
+        dep.depend()
+        if (childOb) {
+          childOb.dep.depend()
+        }
+        if (Array.isArray(value)) {
+          for (let e, i = 0, l = value.length; i < l; i++) {
+            e = value[i]
+            e && e.__ob__ && e.__ob__.dep.depend()
+          }
+        }
+      }
+      return value
+    },
+    set: function reactiveSetter (newVal) {
+      const value = getter ? getter.call(obj) : val
+      if (newVal === value) {
+        return
+      }
+      if (setter) {
+        setter.call(obj, newVal)
+      } else {
+        val = newVal
+      }
+      childOb = observe(newVal)
+      dep.notify()
+    }
+  })
+}
+
+/**
+ * Set a property on an object. Adds the new property and
+ * triggers change notification if the property doesn't
+ * already exist.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @param {*} val
+ * @public
+ */
+
+/* istanbul ignore next */
+export function set (obj, key, val) {
+  if (Array.isArray(obj)) {
+    return obj.splice(key, 1, val)
+  }
+  if (hasOwn(obj, key)) {
+    obj[key] = val
+    return
+  }
+  if (obj._isVue) {
+    set(obj._data, key, val)
+    return
+  }
+  const ob = obj.__ob__
+  if (!ob) {
+    obj[key] = val
+    return
+  }
+  ob.convert(key, val)
+  ob.dep.notify()
+  if (ob.vms) {
+    let i = ob.vms.length
+    while (i--) {
+      const vm = ob.vms[i]
+      proxy(vm, key)
+      // vm.$forceUpdate()
+    }
+  }
+  return val
+}
+
+/**
+ * Delete a property and trigger change if necessary.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ */
+
+/* istanbul ignore next */
+export function del (obj, key) {
+  if (!hasOwn(obj, key)) {
+    return
+  }
+  delete obj[key]
+  const ob = obj.__ob__
+
+  if (!ob) {
+    if (obj._isVue) {
+      delete obj._data[key]
+      // obj.$forceUpdate()
+    }
+    return
+  }
+  ob.dep.notify()
+  if (ob.vms) {
+    let i = ob.vms.length
+    while (i--) {
+      const vm = ob.vms[i]
+      unproxy(vm, key)
+      // vm.$forceUpdate()
+    }
+  }
+}
+
+const KEY_WORDS = ['$index', '$value', '$event']
+export function proxy (vm, key) {
+  if (KEY_WORDS.indexOf(key) > -1 || !isReserved(key)) {
+    Object.defineProperty(vm, key, {
+      configurable: true,
+      enumerable: true,
+      get: function proxyGetter () {
+        return vm._data[key]
+      },
+      set: function proxySetter (val) {
+        vm._data[key] = val
+      }
+    })
+  }
+}
+
+/* istanbul ignore next */
+export function unproxy (vm, key) {
+  if (!isReserved(key)) {
+    delete vm[key]
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/core/state.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/core/state.js b/runtime/frameworks/legacy/core/state.js
new file mode 100644
index 0000000..5300dbf
--- /dev/null
+++ b/runtime/frameworks/legacy/core/state.js
@@ -0,0 +1,92 @@
+/* eslint-disable */
+
+
+import Watcher from './watcher'
+import Dep from './dep'
+import {
+  observe,
+  proxy,
+  unproxy
+} from './observer'
+import {
+  isPlainObject,
+  bind
+} from '../util/index'
+
+export function initState (vm) {
+  vm._watchers = []
+  initData(vm)
+  initComputed(vm)
+  initMethods(vm)
+}
+
+export function initData (vm) {
+  let data = vm._data
+
+  if (!isPlainObject(data)) {
+    data = {}
+  }
+  // proxy data on instance
+  const keys = Object.keys(data)
+  let i = keys.length
+  while (i--) {
+    proxy(vm, keys[i])
+  }
+  // observe data
+  observe(data, vm)
+}
+
+/* istanbul ignore next */
+function noop () {
+}
+
+export function initComputed (vm) {
+  const computed = vm._computed
+  if (computed) {
+    for (let key in computed) {
+      const userDef = computed[key]
+      const def = {
+        enumerable: true,
+        configurable: true
+      }
+      if (typeof userDef === 'function') {
+        def.get = makeComputedGetter(userDef, vm)
+        def.set = noop
+      } else {
+        def.get = userDef.get
+          ? userDef.cache !== false
+            ? makeComputedGetter(userDef.get, vm)
+            : bind(userDef.get, vm)
+          : noop
+        def.set = userDef.set
+          ? bind(userDef.set, vm)
+          : noop
+      }
+      Object.defineProperty(vm, key, def)
+    }
+  }
+}
+
+function makeComputedGetter (getter, owner) {
+  const watcher = new Watcher(owner, getter, null, {
+    lazy: true
+  })
+  return function computedGetter () {
+    if (watcher.dirty) {
+      watcher.evaluate()
+    }
+    if (Dep.target) {
+      watcher.depend()
+    }
+    return watcher.value
+  }
+}
+
+export function initMethods (vm) {
+  const methods = vm._methods
+  if (methods) {
+    for (let key in methods) {
+      vm[key] = methods[key]
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/core/watcher.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/core/watcher.js b/runtime/frameworks/legacy/core/watcher.js
new file mode 100644
index 0000000..cfd6370
--- /dev/null
+++ b/runtime/frameworks/legacy/core/watcher.js
@@ -0,0 +1,254 @@
+/* eslint-disable */
+
+
+import Dep, { pushTarget, popTarget } from './dep'
+// import { pushWatcher } from './batcher'
+import {
+  remove,
+  extend,
+  isObject,
+  createNewSet
+  // _Set as Set
+} from '../util/index'
+
+let uid = 0
+
+/**
+ * A watcher parses an expression, collects dependencies,
+ * and fires callback when the expression value changes.
+ * This is used for both the $watch() api and directives.
+ *
+ * @param {Vue} vm
+ * @param {String|Function} expOrFn
+ * @param {Function} cb
+ * @param {Object} options
+ *                 - {Array} filters
+ *                 - {Boolean} twoWay
+ *                 - {Boolean} deep
+ *                 - {Boolean} user
+ *                 - {Boolean} sync
+ *                 - {Boolean} lazy
+ *                 - {Function} [preProcess]
+ *                 - {Function} [postProcess]
+ * @constructor
+ */
+
+export default function Watcher (vm, expOrFn, cb, options) {
+  // mix in options
+  if (options) {
+    extend(this, options)
+  }
+  const isFn = typeof expOrFn === 'function'
+  this.vm = vm
+  vm._watchers.push(this)
+  this.expression = expOrFn
+  this.cb = cb
+  this.id = ++uid // uid for batching
+  this.active = true
+  this.dirty = this.lazy // for lazy watchers
+  this.deps = []
+  this.newDeps = []
+  this.depIds = createNewSet() // new Set()
+  this.newDepIds = createNewSet() // new Set()
+  // parse expression for getter
+  if (isFn) {
+    this.getter = expOrFn
+  }
+  this.value = this.lazy
+    ? undefined
+    : this.get()
+  // state for avoiding false triggers for deep and Array
+  // watchers during vm._digest()
+  this.queued = this.shallow = false
+}
+
+/**
+ * Evaluate the getter, and re-collect dependencies.
+ */
+
+Watcher.prototype.get = function () {
+  pushTarget(this)
+  const value = this.getter.call(this.vm, this.vm)
+  // "touch" every property so they are all tracked as
+  // dependencies for deep watching
+  if (this.deep) {
+    traverse(value)
+  }
+  popTarget()
+  this.cleanupDeps()
+  return value
+}
+
+/**
+ * Add a dependency to this directive.
+ *
+ * @param {Dep} dep
+ */
+
+Watcher.prototype.addDep = function (dep) {
+  const id = dep.id
+  if (!this.newDepIds.has(id)) {
+    this.newDepIds.add(id)
+    this.newDeps.push(dep)
+    if (!this.depIds.has(id)) {
+      dep.addSub(this)
+    }
+  }
+}
+
+/**
+ * Clean up for dependency collection.
+ */
+
+Watcher.prototype.cleanupDeps = function () {
+  let i = this.deps.length
+  while (i--) {
+    const dep = this.deps[i]
+    if (!this.newDepIds.has(dep.id)) {
+      dep.removeSub(this)
+    }
+  }
+  let tmp = this.depIds
+  this.depIds = this.newDepIds
+  this.newDepIds = tmp
+  this.newDepIds.clear()
+  tmp = this.deps
+  this.deps = this.newDeps
+  this.newDeps = tmp
+  this.newDeps.length = 0
+}
+
+/**
+ * Subscriber interface.
+ * Will be called when a dependency changes.
+ *
+ * @param {Boolean} shallow
+ */
+
+Watcher.prototype.update = function (shallow) {
+  if (this.lazy) {
+    this.dirty = true
+  } else {
+    this.run()
+  }
+  // } else if (this.sync) {
+  //   this.run()
+  // } else {
+  //   // if queued, only overwrite shallow with non-shallow,
+  //   // but not the other way around.
+  //   this.shallow = this.queued
+  //     ? shallow
+  //       ? this.shallow
+  //       : false
+  //     : !!shallow
+  //   this.queued = true
+  //   pushWatcher(this)
+  // }
+}
+
+/**
+ * Batcher job interface.
+ * Will be called by the batcher.
+ */
+
+Watcher.prototype.run = function () {
+  if (this.active) {
+    const value = this.get()
+    if (
+      value !== this.value ||
+      // Deep watchers and watchers on Object/Arrays should fire even
+      // when the value is the same, because the value may
+      // have mutated; but only do so if this is a
+      // non-shallow update (caused by a vm digest).
+      ((isObject(value) || this.deep) && !this.shallow)
+    ) {
+      // set new value
+      const oldValue = this.value
+      this.value = value
+      this.cb.call(this.vm, value, oldValue)
+    }
+    this.queued = this.shallow = false
+  }
+}
+
+/**
+ * Evaluate the value of the watcher.
+ * This only gets called for lazy watchers.
+ */
+
+Watcher.prototype.evaluate = function () {
+  this.value = this.get()
+  this.dirty = false
+}
+
+/**
+ * Depend on all deps collected by this watcher.
+ */
+
+Watcher.prototype.depend = function () {
+  let i = this.deps.length
+  while (i--) {
+    this.deps[i].depend()
+  }
+}
+
+/**
+ * Remove self from all dependencies' subcriber list.
+ */
+
+Watcher.prototype.teardown = function () {
+  if (this.active) {
+    // remove self from vm's watcher list
+    // this is a somewhat expensive operation so we skip it
+    // if the vm is being destroyed or is performing a v-for
+    // re-render (the watcher list is then filtered by v-for).
+    if (!this.vm._isBeingDestroyed && !this.vm._vForRemoving) {
+      remove(this.vm._watchers, this)
+    }
+    let i = this.deps.length
+    while (i--) {
+      this.deps[i].removeSub(this)
+    }
+    this.active = false
+    this.vm = this.cb = this.value = null
+  }
+}
+
+/**
+ * Recrusively traverse an object to evoke all converted
+ * getters, so that every nested property inside the object
+ * is collected as a "deep" dependency.
+ *
+ * @param {*} val
+ * @param {Set} seen
+ */
+
+const seenObjects = createNewSet() // new Set()
+/* istanbul ignore next */
+function traverse (val, seen) {
+  let i, keys, isA, isO
+  if (!seen) {
+    seen = seenObjects
+    seen.clear()
+  }
+  isA = Array.isArray(val)
+  isO = isObject(val)
+  if (isA || isO) {
+    if (val.__ob__) {
+      const depId = val.__ob__.dep.id
+      if (seen.has(depId)) {
+        return
+      } else {
+        seen.add(depId)
+      }
+    }
+    if (isA) {
+      i = val.length
+      while (i--) traverse(val[i], seen)
+    } else if (isO) {
+      keys = Object.keys(val)
+      i = keys.length
+      while (i--) traverse(val[keys[i]], seen)
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/index.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/index.js b/runtime/frameworks/legacy/index.js
new file mode 100644
index 0000000..366ecb3
--- /dev/null
+++ b/runtime/frameworks/legacy/index.js
@@ -0,0 +1,40 @@
+/*
+ * 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 Weex framework entry.
+ */
+
+import * as methods from './api/methods'
+
+import Vm from './vm'
+export { createInstance } from './static/create'
+export { init, refreshInstance, destroyInstance } from './static/life'
+import { registerComponents, registerModules, registerMethods } from './static/register'
+export { receiveTasks } from './static/bridge'
+export { getRoot } from './static/misc'
+
+// register special methods for Weex framework
+registerMethods(methods)
+
+/**
+ * Prevent modification of Vm and Vm.prototype
+ */
+Object.freeze(Vm)
+
+export { registerComponents, registerModules, registerMethods }

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/static/bridge.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/static/bridge.js b/runtime/frameworks/legacy/static/bridge.js
new file mode 100644
index 0000000..665b1ad
--- /dev/null
+++ b/runtime/frameworks/legacy/static/bridge.js
@@ -0,0 +1,56 @@
+/*
+ * 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 { instanceMap } from './map'
+import {
+  fireEvent,
+  callback
+} from '../app/ctrl/index'
+
+const jsHandlers = {
+  fireEvent: (id, ...args) => {
+    return fireEvent(instanceMap[id], ...args)
+  },
+  callback: (id, ...args) => {
+    return callback(instanceMap[id], ...args)
+  }
+}
+
+/**
+ * Accept calls from native (event or callback).
+ *
+ * @param  {string} id
+ * @param  {array} tasks list with `method` and `args`
+ */
+export function receiveTasks (id, tasks) {
+  const instance = instanceMap[id]
+  if (instance && Array.isArray(tasks)) {
+    const results = []
+    tasks.forEach((task) => {
+      const handler = jsHandlers[task.method]
+      const args = [...task.args]
+      /* istanbul ignore else */
+      if (typeof handler === 'function') {
+        args.unshift(id)
+        results.push(handler(...args))
+      }
+    })
+    return results
+  }
+  return new Error(`invalid instance id "${id}" or tasks`)
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/static/create.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/static/create.js b/runtime/frameworks/legacy/static/create.js
new file mode 100644
index 0000000..1c963b3
--- /dev/null
+++ b/runtime/frameworks/legacy/static/create.js
@@ -0,0 +1,51 @@
+/*
+ * 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 App from '../app/index'
+import { instanceMap } from './map'
+import { init as initApp } from '../app/ctrl/index'
+import { resetTarget } from '../core/dep'
+
+/**
+ * Create a Weex instance.
+ *
+ * @param  {string} id
+ * @param  {string} code
+ * @param  {object} options
+ *         option `HAS_LOG` enable print log
+ * @param  {object} data
+ * @param  {object} info { created, ... services }
+ */
+export function createInstance (id, code, options, data, info) {
+  const { services } = info || {}
+  resetTarget()
+  let instance = instanceMap[id]
+  /* istanbul ignore else */
+  options = options || {}
+  let result
+  /* istanbul ignore else */
+  if (!instance) {
+    instance = new App(id, options)
+    instanceMap[id] = instance
+    result = initApp(instance, code, data, services)
+  }
+  else {
+    result = new Error(`invalid instance id "${id}"`)
+  }
+  return (result instanceof Error) ? result : instance
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/static/life.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/static/life.js b/runtime/frameworks/legacy/static/life.js
new file mode 100644
index 0000000..9cb8abc
--- /dev/null
+++ b/runtime/frameworks/legacy/static/life.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.
+ */
+import config from '../config'
+import {
+  refresh,
+  destroy
+} from '../app/ctrl/index'
+import { instanceMap } from './map'
+import { resetTarget } from '../core/dep'
+
+/**
+ * Init config informations for Weex framework
+ * @param  {object} cfg
+ */
+export function init (cfg) {
+  config.Document = cfg.Document
+  config.Element = cfg.Element
+  config.Comment = cfg.Comment
+  config.sendTasks = cfg.sendTasks
+  config.Listener = cfg.Listener
+}
+
+/**
+ * Refresh a Weex instance with data.
+ *
+ * @param  {string} id
+ * @param  {object} data
+ */
+export function refreshInstance (id, data) {
+  const instance = instanceMap[id]
+  let result
+  /* istanbul ignore else */
+  if (instance) {
+    result = refresh(instance, data)
+  }
+  else {
+    result = new Error(`invalid instance id "${id}"`)
+  }
+  return result
+}
+
+/**
+ * Destroy a Weex instance.
+ * @param  {string} id
+ */
+export function destroyInstance (id) {
+  // Markup some global state in native side
+  if (typeof markupState === 'function') {
+    markupState()
+  }
+
+  resetTarget()
+  const instance = instanceMap[id]
+  /* istanbul ignore else */
+  if (!instance) {
+    return new Error(`invalid instance id "${id}"`)
+  }
+  destroy(instance)
+  delete instanceMap[id]
+  // notifyContextDisposed is used to tell v8 to do a full GC,
+  // but this would have a negative performance impact on weex,
+  // because all the inline cache in v8 would get cleared
+  // during a full GC.
+  // To take care of both memory and performance, just tell v8
+  // to do a full GC every eighteen times.
+  const idNum = Math.round(id)
+  const round = 18
+  if (idNum > 0) {
+    const remainder = idNum % round
+    if (!remainder && typeof notifyTrimMemory === 'function') {
+      notifyTrimMemory()
+    }
+  }
+  return instanceMap
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/static/map.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/static/map.js b/runtime/frameworks/legacy/static/map.js
new file mode 100644
index 0000000..8c94ef4
--- /dev/null
+++ b/runtime/frameworks/legacy/static/map.js
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+export const instanceMap = {}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/static/misc.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/static/misc.js b/runtime/frameworks/legacy/static/misc.js
new file mode 100644
index 0000000..afbc5e3
--- /dev/null
+++ b/runtime/frameworks/legacy/static/misc.js
@@ -0,0 +1,40 @@
+/*
+ * 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 { instanceMap } from './map'
+import {
+  getRootElement
+} from '../app/ctrl/index'
+
+/**
+ * Get a whole element tree of an instance for debugging.
+ * @param  {string} id
+ * @return {object} a virtual dom tree
+ */
+export function getRoot (id) {
+  const instance = instanceMap[id]
+  let result
+  /* istanbul ignore else */
+  if (instance) {
+    result = getRootElement(instance)
+  }
+  else {
+    result = new Error(`invalid instance id "${id}"`)
+  }
+  return result
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/static/register.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/static/register.js b/runtime/frameworks/legacy/static/register.js
new file mode 100644
index 0000000..f21df89
--- /dev/null
+++ b/runtime/frameworks/legacy/static/register.js
@@ -0,0 +1,75 @@
+/*
+ * 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 Vm from '../vm/index'
+import config from '../config'
+import {
+  initModules,
+  initMethods
+} from '../app/register'
+
+const {
+  nativeComponentMap
+} = config
+
+/**
+ * Register the name of each native component.
+ * @param  {array} components array of name
+ */
+export function registerComponents (components) {
+  if (Array.isArray(components)) {
+    components.forEach(function register (name) {
+      /* istanbul ignore if */
+      if (!name) {
+        return
+      }
+      if (typeof name === 'string') {
+        nativeComponentMap[name] = true
+      }
+      /* istanbul ignore else */
+      else if (typeof name === 'object' && typeof name.type === 'string') {
+        nativeComponentMap[name.type] = name
+      }
+    })
+  }
+}
+
+/**
+ * Register the name and methods of each module.
+ * @param  {object} modules a object of modules
+ */
+export function registerModules (modules) {
+  /* istanbul ignore else */
+  if (typeof modules === 'object') {
+    initModules(modules)
+  }
+}
+
+/**
+ * Register the name and methods of each api.
+ * @param  {object} apis a object of apis
+ */
+export function registerMethods (methods) {
+  /* istanbul ignore else */
+  if (typeof methods === 'object') {
+    initMethods(Vm, methods)
+  }
+}
+
+// @todo: Hack for this framework only. Will be re-designed or removed later.
+global.registerMethods = registerMethods

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/util/LICENSE
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/util/LICENSE b/runtime/frameworks/legacy/util/LICENSE
new file mode 100644
index 0000000..4645bc4
--- /dev/null
+++ b/runtime/frameworks/legacy/util/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2013-2015 Yuxi Evan You
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/util/index.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/util/index.js b/runtime/frameworks/legacy/util/index.js
new file mode 100644
index 0000000..0d716f4
--- /dev/null
+++ b/runtime/frameworks/legacy/util/index.js
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+export {
+  extend,
+  def,
+  remove,
+  hasOwn,
+  bind,
+  toArray,
+  isObject,
+  isPlainObject
+} from './shared'
+
+/**
+ * Check if a string starts with $ or _
+ *
+ * @param {String} str
+ * @return {Boolean}
+ */
+
+export function isReserved (str) {
+  const c = (str + '').charCodeAt(0)
+  return c === 0x24 || c === 0x5F
+}
+
+// can we use __proto__?
+export const hasProto = '__proto__' in {}
+
+let _Set
+/* istanbul ignore next */
+if (typeof Set !== 'undefined' && Set.toString().match(/native code/)) {
+  // use native Set when available.
+  _Set = Set
+}
+else {
+  // a non-standard Set polyfill that only works with primitive keys.
+  _Set = function () {
+    this.set = Object.create(null)
+  }
+  _Set.prototype.has = function (key) {
+    return this.set[key] !== undefined
+  }
+  _Set.prototype.add = function (key) {
+    if (key == null || this.set[key]) {
+      return
+    }
+    this.set[key] = 1
+  }
+  _Set.prototype.clear = function () {
+    this.set = Object.create(null)
+  }
+}
+
+export { _Set }
+
+/**
+ * Polyfill in iOS7 by native because the JavaScript polyfill has memory problem.
+ * @return {object}
+ */
+
+export function createNewSet () {
+  /* istanbul ignore next */
+  /* eslint-disable */
+  if (typeof nativeSet === 'object') {
+    return nativeSet.create()
+  }
+  /* eslint-enable */
+  return new _Set()
+}
+
+/**
+ * Create a cached version of a pure function.
+ *
+ * @param {Function} fn
+ * @return {Function}
+ */
+
+export function cached (fn) {
+  const cache = Object.create(null)
+  return function cachedFn (str) {
+    const hit = cache[str]
+    return hit || (cache[str] = fn(str))
+  }
+}
+
+/**
+ * Camelize a hyphen-delmited string.
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+const camelizeRE = /-(\w)/g
+export const camelize = cached(str => {
+  return str.replace(camelizeRE, toUpper)
+})
+
+function toUpper (_, c) {
+  return c ? c.toUpperCase() : ''
+}
+
+/**
+ * Hyphenate a camelCase string.
+ *
+ * @param {String} str
+ * @return {String}
+ */
+
+const hyphenateRE = /([a-z\d])([A-Z])/g
+export const hyphenate = cached(str => {
+  return str
+    .replace(hyphenateRE, '$1-$2')
+    .toLowerCase()
+})
+
+export function typof (v) {
+  const s = Object.prototype.toString.call(v)
+  return s.substring(8, s.length - 1).toLowerCase()
+}
+
+// weex name rules
+
+const WEEX_COMPONENT_REG = /^@weex-component\//
+const WEEX_MODULE_REG = /^@weex-module\//
+const NORMAL_MODULE_REG = /^\.{1,2}\//
+const JS_SURFIX_REG = /\.js$/
+
+export const isWeexComponent = name => !!name.match(WEEX_COMPONENT_REG)
+export const isWeexModule = name => !!name.match(WEEX_MODULE_REG)
+export const isNormalModule = name => !!name.match(NORMAL_MODULE_REG)
+export const isNpmModule = name => !isWeexComponent(name) && !isWeexModule(name) && !isNormalModule(name)
+
+export function removeWeexPrefix (str) {
+  const result = str.replace(WEEX_COMPONENT_REG, '').replace(WEEX_MODULE_REG, '')
+  return result
+}
+
+export function removeJSSurfix (str) {
+  return str.replace(JS_SURFIX_REG, '')
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/util/shared.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/util/shared.js b/runtime/frameworks/legacy/util/shared.js
new file mode 100644
index 0000000..faf76e1
--- /dev/null
+++ b/runtime/frameworks/legacy/util/shared.js
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+/**
+ * Mix properties into target object.
+ *
+ * @param {Object} to
+ * @param {Object} from
+ */
+
+function extend (target, ...src) {
+  /* istanbul ignore else */
+  if (typeof Object.assign === 'function') {
+    Object.assign(target, ...src)
+  }
+  else {
+    const first = src.shift()
+    for (const key in first) {
+      target[key] = first[key]
+    }
+    if (src.length) {
+      extend(target, ...src)
+    }
+  }
+  return target
+}
+
+/**
+ * Define a property.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @param {*} val
+ * @param {Boolean} [enumerable]
+ */
+
+function def (obj, key, val, enumerable) {
+  Object.defineProperty(obj, key, {
+    value: val,
+    enumerable: !!enumerable,
+    writable: true,
+    configurable: true
+  })
+}
+
+/**
+ * Remove an item from an array
+ *
+ * @param {Array} arr
+ * @param {*} item
+ */
+
+function remove (arr, item) {
+  if (arr.length) {
+    const index = arr.indexOf(item)
+    if (index > -1) {
+      return arr.splice(index, 1)
+    }
+  }
+}
+
+/**
+ * Check whether the object has the property.
+ *
+ * @param {Object} obj
+ * @param {String} key
+ * @return {Boolean}
+ */
+const hasOwnProperty = Object.prototype.hasOwnProperty
+function hasOwn (obj, key) {
+  return hasOwnProperty.call(obj, key)
+}
+
+/**
+ * Simple bind, faster than native
+ *
+ * @param {Function} fn
+ * @param {Object} ctx
+ * @return {Function}
+ */
+
+function bind (fn, ctx) {
+  return function (a) {
+    const l = arguments.length
+    return l
+      ? l > 1
+        ? fn.apply(ctx, arguments)
+        : fn.call(ctx, a)
+      : fn.call(ctx)
+  }
+}
+
+/**
+ * Convert an Array-like object to a real Array.
+ *
+ * @param {Array-like} list
+ * @param {Number} [start] - start index
+ * @return {Array}
+ */
+
+function toArray (list, start) {
+  start = start || 0
+  let i = list.length - start
+  const ret = new Array(i)
+  while (i--) {
+    ret[i] = list[i + start]
+  }
+  return ret
+}
+
+/**
+ * Quick object check - this is primarily used to tell
+ * Objects from primitive values when we know the value
+ * is a JSON-compliant type.
+ *
+ * @param {*} obj
+ * @return {Boolean}
+ */
+
+function isObject (obj) {
+  return obj !== null && typeof obj === 'object'
+}
+
+/**
+ * Strict object type check. Only returns true
+ * for plain JavaScript objects.
+ *
+ * @param {*} obj
+ * @return {Boolean}
+ */
+
+const toString = Object.prototype.toString
+const OBJECT_STRING = '[object Object]'
+function isPlainObject (obj) {
+  return toString.call(obj) === OBJECT_STRING
+}
+
+export {
+  extend,
+  def,
+  remove,
+  hasOwn,
+  bind,
+  toArray,
+  isObject,
+  isPlainObject
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/vm/compiler.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/vm/compiler.js b/runtime/frameworks/legacy/vm/compiler.js
new file mode 100644
index 0000000..ffca01f
--- /dev/null
+++ b/runtime/frameworks/legacy/vm/compiler.js
@@ -0,0 +1,636 @@
+/*
+ * 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
+ * ViewModel template parser & data-binding process
+ */
+
+import {
+  extend,
+  isObject,
+  bind
+} from '../util/index'
+import {
+  initData,
+  initComputed
+} from '../core/state'
+import {
+  bindElement,
+  setId,
+  bindSubVm,
+  bindSubVmAfterInitialized,
+  applyNaitveComponentOptions,
+  watch
+} from './directive'
+import {
+  createBlock,
+  createBody,
+  createElement,
+  attachTarget,
+  moveTarget,
+  removeTarget
+} from './dom-helper'
+
+/**
+ * build()
+ *   compile(template, parentNode)
+ *     if (type is content) create contentNode
+ *     else if (dirs have v-for) foreach -> create context
+ *       -> compile(templateWithoutFor, parentNode): diff(list) onchange
+ *     else if (dirs have v-if) assert
+ *       -> compile(templateWithoutIf, parentNode): toggle(shown) onchange
+ *     else if (type is dynamic)
+ *       -> compile(templateWithoutDynamicType, parentNode): watch(type) onchange
+ *     else if (type is custom)
+ *       addChildVm(vm, parentVm)
+ *       build(externalDirs)
+ *       foreach childNodes -> compile(childNode, template)
+ *     else if (type is native)
+ *       set(dirs): update(id/attr/style/class) onchange
+ *       append(template, parentNode)
+ *       foreach childNodes -> compile(childNode, template)
+ */
+export function build (vm) {
+  const opt = vm._options || {}
+  const template = opt.template || {}
+
+  if (opt.replace) {
+    if (template.children && template.children.length === 1) {
+      compile(vm, template.children[0], vm._parentEl)
+    }
+    else {
+      compile(vm, template.children, vm._parentEl)
+    }
+  }
+  else {
+    compile(vm, template, vm._parentEl)
+  }
+
+  console.debug(`[JS Framework] "ready" lifecycle in Vm(${vm._type})`)
+  vm.$emit('hook:ready')
+  vm._ready = true
+}
+
+/**
+ * Generate elements by child or children and append to parent elements.
+ * Root element info would be merged if has. The first argument may be an array
+ * if the root element with options.replace has not only one child.
+ *
+ * @param {object|array} target
+ * @param {object}       dest
+ * @param {object}       meta
+ */
+function compile (vm, target, dest, meta) {
+  const app = vm._app || {}
+
+  if (app.lastSignal === -1) {
+    return
+  }
+
+  if (target.attr && target.attr.hasOwnProperty('static')) {
+    vm._static = true
+  }
+
+  if (targetIsFragment(target)) {
+    compileFragment(vm, target, dest, meta)
+    return
+  }
+  meta = meta || {}
+  if (targetIsContent(target)) {
+    console.debug('[JS Framework] compile "content" block by', target)
+    vm._content = createBlock(vm, dest)
+    return
+  }
+
+  if (targetNeedCheckRepeat(target, meta)) {
+    console.debug('[JS Framework] compile "repeat" logic by', target)
+    if (dest.type === 'document') {
+      console.warn('[JS Framework] The root element does\'t support `repeat` directive!')
+    }
+    else {
+      compileRepeat(vm, target, dest)
+    }
+    return
+  }
+  if (targetNeedCheckShown(target, meta)) {
+    console.debug('[JS Framework] compile "if" logic by', target)
+    if (dest.type === 'document') {
+      console.warn('[JS Framework] The root element does\'t support `if` directive!')
+    }
+    else {
+      compileShown(vm, target, dest, meta)
+    }
+    return
+  }
+  const typeGetter = meta.type || target.type
+  if (targetNeedCheckType(typeGetter, meta)) {
+    compileType(vm, target, dest, typeGetter, meta)
+    return
+  }
+  const type = typeGetter
+  const component = targetIsComposed(vm, target, type)
+  if (component) {
+    console.debug('[JS Framework] compile composed component by', target)
+    compileCustomComponent(vm, component, target, dest, type, meta)
+    return
+  }
+  console.debug('[JS Framework] compile native component by', target)
+  compileNativeComponent(vm, target, dest, type)
+}
+
+/**
+ * Check if target is a fragment (an array).
+ *
+ * @param  {object}  target
+ * @return {boolean}
+ */
+function targetIsFragment (target) {
+  return Array.isArray(target)
+}
+
+/**
+ * Check if target type is content/slot.
+ *
+ * @param  {object}  target
+ * @return {boolean}
+ */
+function targetIsContent (target) {
+  return target.type === 'content' || target.type === 'slot'
+}
+
+/**
+ * Check if target need to compile by a list.
+ *
+ * @param  {object}  target
+ * @param  {object}  meta
+ * @return {boolean}
+ */
+function targetNeedCheckRepeat (target, meta) {
+  return !meta.hasOwnProperty('repeat') && target.repeat
+}
+
+/**
+ * Check if target need to compile by a boolean value.
+ *
+ * @param  {object}  target
+ * @param  {object}  meta
+ * @return {boolean}
+ */
+function targetNeedCheckShown (target, meta) {
+  return !meta.hasOwnProperty('shown') && target.shown
+}
+
+/**
+ * Check if target need to compile by a dynamic type.
+ *
+ * @param  {string|function} typeGetter
+ * @param  {object}          meta
+ * @return {boolean}
+ */
+function targetNeedCheckType (typeGetter, meta) {
+  return (typeof typeGetter === 'function') && !meta.hasOwnProperty('type')
+}
+
+/**
+ * Check if this kind of component is composed.
+ *
+ * @param  {string}  type
+ * @return {boolean}
+ */
+function targetIsComposed (vm, target, type) {
+  let component
+  if (vm._app && vm._app.customComponentMap) {
+    component = vm._app.customComponentMap[type]
+  }
+  if (vm._options && vm._options.components) {
+    component = vm._options.components[type]
+  }
+  if (target.component) {
+    component = component || {}
+  }
+  return component
+}
+
+/**
+ * Compile a list of targets.
+ *
+ * @param {object} target
+ * @param {object} dest
+ * @param {object} meta
+ */
+function compileFragment (vm, target, dest, meta) {
+  const fragBlock = createBlock(vm, dest)
+  target.forEach((child) => {
+    compile(vm, child, fragBlock, meta)
+  })
+}
+
+/**
+ * Compile a target with repeat directive.
+ *
+ * @param {object} target
+ * @param {object} dest
+ */
+function compileRepeat (vm, target, dest) {
+  const repeat = target.repeat
+  const oldStyle = typeof repeat === 'function'
+  let getter = repeat.getter || repeat.expression || repeat
+  if (typeof getter !== 'function') {
+    getter = function () { return [] }
+  }
+  const key = repeat.key || '$index'
+  const value = repeat.value || '$value'
+  const trackBy = repeat.trackBy || target.trackBy ||
+    (target.attr && target.attr.trackBy)
+
+  const fragBlock = createBlock(vm, dest)
+  fragBlock.children = []
+  fragBlock.data = []
+  fragBlock.vms = []
+
+  bindRepeat(vm, target, fragBlock, { getter, key, value, trackBy, oldStyle })
+}
+
+/**
+ * Compile a target with if directive.
+ *
+ * @param {object} target
+ * @param {object} dest
+ * @param {object} meta
+ */
+function compileShown (vm, target, dest, meta) {
+  const newMeta = { shown: true }
+  const fragBlock = createBlock(vm, dest)
+
+  if (dest.element && dest.children) {
+    dest.children.push(fragBlock)
+  }
+
+  if (meta.repeat) {
+    newMeta.repeat = meta.repeat
+  }
+
+  bindShown(vm, target, fragBlock, newMeta)
+}
+
+/**
+ * Compile a target with dynamic component type.
+ *
+ * @param {object}   target
+ * @param {object}   dest
+ * @param {function} typeGetter
+ */
+function compileType (vm, target, dest, typeGetter, meta) {
+  const type = typeGetter.call(vm)
+  const newMeta = extend({ type }, meta)
+  const fragBlock = createBlock(vm, dest)
+
+  if (dest.element && dest.children) {
+    dest.children.push(fragBlock)
+  }
+
+  watch(vm, typeGetter, (value) => {
+    const newMeta = extend({ type: value }, meta)
+    removeTarget(vm, fragBlock, true)
+    compile(vm, target, fragBlock, newMeta)
+  })
+
+  compile(vm, target, fragBlock, newMeta)
+}
+
+/**
+ * Compile a composed component.
+ *
+ * @param {object} target
+ * @param {object} dest
+ * @param {string} type
+ */
+function compileCustomComponent (vm, component, target, dest, type, meta) {
+  const Ctor = vm.constructor
+  const subVm = new Ctor(type, component, vm, dest, undefined, {
+    'hook:init': function () {
+      if (vm._static) {
+        this._static = vm._static
+      }
+      setId(vm, null, target.id, this)
+      // bind template earlier because of lifecycle issues
+      this._externalBinding = {
+        parent: vm,
+        template: target
+      }
+    },
+    'hook:created': function () {
+      bindSubVm(vm, this, target, meta.repeat)
+    },
+    'hook:ready': function () {
+      if (this._content) {
+        compileChildren(vm, target, this._content)
+      }
+    }
+  })
+  bindSubVmAfterInitialized(vm, subVm, target, dest)
+}
+
+/**
+ * Generate element from template and attach to the dest if needed.
+ * The time to attach depends on whether the mode status is node or tree.
+ *
+ * @param {object} template
+ * @param {object} dest
+ * @param {string} type
+ */
+function compileNativeComponent (vm, template, dest, type) {
+  applyNaitveComponentOptions(template)
+
+  let element
+  if (dest.ref === '_documentElement') {
+    // if its parent is documentElement then it's a body
+    console.debug(`[JS Framework] compile to create body for ${type}`)
+    element = createBody(vm, type)
+  }
+  else {
+    console.debug(`[JS Framework] compile to create element for ${type}`)
+    element = createElement(vm, type)
+  }
+
+  if (!vm._rootEl) {
+    vm._rootEl = element
+    // bind event earlier because of lifecycle issues
+    const binding = vm._externalBinding || {}
+    const target = binding.template
+    const parentVm = binding.parent
+    if (target && target.events && parentVm && element) {
+      for (const type in target.events) {
+        const handler = parentVm[target.events[type]]
+        if (handler) {
+          element.addEvent(type, bind(handler, parentVm))
+        }
+      }
+    }
+  }
+
+  bindElement(vm, element, template)
+
+  if (template.attr && template.attr.append) { // backward, append prop in attr
+    template.append = template.attr.append
+  }
+
+  if (template.append) { // give the append attribute for ios adaptation
+    element.attr = element.attr || {}
+    element.attr.append = template.append
+  }
+
+  const treeMode = template.append === 'tree'
+  const app = vm._app || {}
+  if (app.lastSignal !== -1 && !treeMode) {
+    console.debug('[JS Framework] compile to append single node for', element)
+    app.lastSignal = attachTarget(vm, element, dest)
+  }
+  if (app.lastSignal !== -1) {
+    compileChildren(vm, template, element)
+  }
+  if (app.lastSignal !== -1 && treeMode) {
+    console.debug('[JS Framework] compile to append whole tree for', element)
+    app.lastSignal = attachTarget(vm, element, dest)
+  }
+}
+
+/**
+ * Set all children to a certain parent element.
+ *
+ * @param {object} template
+ * @param {object} dest
+ */
+function compileChildren (vm, template, dest) {
+  const app = vm._app || {}
+  const children = template.children
+  if (children && children.length) {
+    children.every((child) => {
+      compile(vm, child, dest)
+      return app.lastSignal !== -1
+    })
+  }
+}
+
+/**
+ * Watch the list update and refresh the changes.
+ *
+ * @param {object} target
+ * @param {object} fragBlock {vms, data, children}
+ * @param {object} info      {getter, key, value, trackBy, oldStyle}
+ */
+function bindRepeat (vm, target, fragBlock, info) {
+  const vms = fragBlock.vms
+  const children = fragBlock.children
+  const { getter, trackBy, oldStyle } = info
+  const keyName = info.key
+  const valueName = info.value
+
+  function compileItem (item, index, context) {
+    let mergedData
+    if (oldStyle) {
+      mergedData = item
+      if (isObject(item)) {
+        mergedData[keyName] = index
+        if (!mergedData.hasOwnProperty('INDEX')) {
+          Object.defineProperty(mergedData, 'INDEX', {
+            value: () => {
+              console.warn('[JS Framework] "INDEX" in repeat is deprecated, ' +
+                'please use "$index" instead')
+            }
+          })
+        }
+      }
+      else {
+        console.warn('[JS Framework] Each list item must be an object in old-style repeat, '
+          + 'please use `repeat={{v in list}}` instead.')
+        mergedData = {}
+        mergedData[keyName] = index
+        mergedData[valueName] = item
+      }
+    }
+    else {
+      mergedData = {}
+      mergedData[keyName] = index
+      mergedData[valueName] = item
+    }
+    const newContext = mergeContext(context, mergedData)
+    vms.push(newContext)
+    compile(newContext, target, fragBlock, { repeat: item })
+  }
+
+  const list = watchBlock(vm, fragBlock, getter, 'repeat',
+    (data) => {
+      console.debug('[JS Framework] the "repeat" item has changed', data)
+      if (!fragBlock || !data) {
+        return
+      }
+
+      const oldChildren = children.slice()
+      const oldVms = vms.slice()
+      const oldData = fragBlock.data.slice()
+      // 1. collect all new refs track by
+      const trackMap = {}
+      const reusedMap = {}
+      data.forEach((item, index) => {
+        const key = trackBy ? item[trackBy] : (oldStyle ? item[keyName] : index)
+        /* istanbul ignore if */
+        if (key == null || key === '') {
+          return
+        }
+        trackMap[key] = item
+      })
+
+      // 2. remove unused element foreach old item
+      const reusedList = []
+      oldData.forEach((item, index) => {
+        const key = trackBy ? item[trackBy] : (oldStyle ? item[keyName] : index)
+        if (trackMap.hasOwnProperty(key)) {
+          reusedMap[key] = {
+            item, index, key,
+            target: oldChildren[index],
+            vm: oldVms[index]
+          }
+          reusedList.push(item)
+        }
+        else {
+          removeTarget(vm, oldChildren[index])
+        }
+      })
+
+      // 3. create new element foreach new item
+      children.length = 0
+      vms.length = 0
+      fragBlock.data = data.slice()
+      fragBlock.updateMark = fragBlock.start
+
+      data.forEach((item, index) => {
+        const key = trackBy ? item[trackBy] : (oldStyle ? item[keyName] : index)
+        const reused = reusedMap[key]
+        if (reused) {
+          if (reused.item === reusedList[0]) {
+            reusedList.shift()
+          }
+          else {
+            reusedList.$remove(reused.item)
+            moveTarget(vm, reused.target, fragBlock.updateMark, true)
+          }
+          children.push(reused.target)
+          vms.push(reused.vm)
+          if (oldStyle) {
+            reused.vm = item
+          }
+          else {
+            reused.vm[valueName] = item
+          }
+          reused.vm[keyName] = index
+          fragBlock.updateMark = reused.target
+        }
+        else {
+          compileItem(item, index, vm)
+        }
+      })
+
+      delete fragBlock.updateMark
+    }
+  )
+
+  fragBlock.data = list.slice(0)
+  list.forEach((item, index) => {
+    compileItem(item, index, vm)
+  })
+}
+
+/**
+ * Watch the display update and add/remove the element.
+ *
+ * @param  {object} target
+ * @param  {object} fragBlock
+ * @param  {object} context
+ */
+function bindShown (vm, target, fragBlock, meta) {
+  const display = watchBlock(vm, fragBlock, target.shown, 'shown',
+    (display) => {
+      console.debug('[JS Framework] the "if" item was changed', display)
+
+      if (!fragBlock || !!fragBlock.display === !!display) {
+        return
+      }
+      fragBlock.display = !!display
+      if (display) {
+        compile(vm, target, fragBlock, meta)
+      }
+      else {
+        removeTarget(vm, fragBlock, true)
+      }
+    }
+  )
+
+  fragBlock.display = !!display
+  if (display) {
+    compile(vm, target, fragBlock, meta)
+  }
+}
+
+/**
+ * Watch calc value changes and append certain type action to differ.
+ * It is used for if or repeat data-binding generator.
+ *
+ * @param  {object}   fragBlock
+ * @param  {function} calc
+ * @param  {string}   type
+ * @param  {function} handler
+ * @return {any}      init value of calc
+ */
+function watchBlock (vm, fragBlock, calc, type, handler) {
+  const differ = vm && vm._app && vm._app.differ
+  const config = {}
+  const depth = (fragBlock.element.depth || 0) + 1
+
+  return watch(vm, calc, (value) => {
+    config.latestValue = value
+    if (differ && !config.recorded) {
+      differ.append(type, depth, fragBlock.blockId, () => {
+        const latestValue = config.latestValue
+        handler(latestValue)
+        config.recorded = false
+        config.latestValue = undefined
+      })
+    }
+    config.recorded = true
+  })
+}
+
+/**
+ * Clone a context and merge certain data.
+ *
+ * @param  {object} mergedData
+ * @return {object}
+ */
+function mergeContext (context, mergedData) {
+  const newContext = Object.create(context)
+  newContext._data = mergedData
+  initData(newContext)
+  initComputed(newContext)
+  newContext._realParent = context
+  if (context._static) {
+    newContext._static = context._static
+  }
+  return newContext
+}


[19/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/core/observer.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/core/observer.js b/html5/frameworks/legacy/core/observer.js
deleted file mode 100644
index 103dd0d..0000000
--- a/html5/frameworks/legacy/core/observer.js
+++ /dev/null
@@ -1,323 +0,0 @@
-/* eslint-disable */
-
-
-import Dep from './dep'
-import { arrayMethods } from './array'
-import {
-  def,
-  remove,
-  isObject,
-  isPlainObject,
-  hasProto,
-  hasOwn,
-  isReserved
-} from '../util/index'
-
-const arrayKeys = Object.getOwnPropertyNames(arrayMethods)
-
-/**
- * Observer class that are attached to each observed
- * object. Once attached, the observer converts target
- * object's property keys into getter/setters that
- * collect dependencies and dispatches updates.
- *
- * @param {Array|Object} value
- * @constructor
- */
-
-export function Observer (value) {
-  this.value = value
-  this.dep = new Dep()
-  def(value, '__ob__', this)
-  if (Array.isArray(value)) {
-    const augment = hasProto
-      ? protoAugment
-      : copyAugment
-    augment(value, arrayMethods, arrayKeys)
-    this.observeArray(value)
-  } else {
-    this.walk(value)
-  }
-}
-
-// Instance methods
-
-/**
- * Walk through each property and convert them into
- * getter/setters. This method should only be called when
- * value type is Object.
- *
- * @param {Object} obj
- */
-
-Observer.prototype.walk = function (obj) {
-  for (let key in obj) {
-    this.convert(key, obj[key])
-  }
-}
-
-/**
- * Observe a list of Array items.
- *
- * @param {Array} items
- */
-
-Observer.prototype.observeArray = function (items) {
-  for (let i = 0, l = items.length; i < l; i++) {
-    observe(items[i])
-  }
-}
-
-/**
- * Convert a property into getter/setter so we can emit
- * the events when the property is accessed/changed.
- *
- * @param {String} key
- * @param {*} val
- */
-
-Observer.prototype.convert = function (key, val) {
-  defineReactive(this.value, key, val)
-}
-
-/**
- * Add an owner vm, so that when $set/$delete mutations
- * happen we can notify owner vms to proxy the keys and
- * digest the watchers. This is only called when the object
- * is observed as an instance's root $data.
- *
- * @param {Vue} vm
- */
-
-Observer.prototype.addVm = function (vm) {
-  (this.vms || (this.vms = [])).push(vm)
-}
-
-/**
- * Remove an owner vm. This is called when the object is
- * swapped out as an instance's $data object.
- *
- * @param {Vue} vm
- */
-
-/* istanbul ignore next */
-Observer.prototype.removeVm = function (vm) {
-  remove(this.vms, vm)
-}
-
-// helpers
-
-/**
- * Augment an target Object or Array by intercepting
- * the prototype chain using __proto__
- *
- * @param {Object|Array} target
- * @param {Object} src
- */
-
-function protoAugment (target, src) {
-  /* eslint-disable no-proto */
-  target.__proto__ = src
-  /* eslint-enable no-proto */
-}
-
-/**
- * Augment an target Object or Array by defining
- * hidden properties.
- *
- * @param {Object|Array} target
- * @param {Object} proto
- */
-
-/* istanbul ignore next */
-function copyAugment (target, src, keys) {
-  for (let i = 0, l = keys.length; i < l; i++) {
-    const key = keys[i]
-    def(target, key, src[key])
-  }
-}
-
-/**
- * Attempt to create an observer instance for a value,
- * returns the new observer if successfully observed,
- * or the existing observer if the value already has one.
- *
- * @param {*} value
- * @param {Vue} [vm]
- * @return {Observer|undefined}
- * @static
- */
-
-export function observe (value, vm) {
-  if (!isObject(value)) {
-    return
-  }
-  let ob
-  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
-    ob = value.__ob__
-  } else if (
-    (Array.isArray(value) || isPlainObject(value)) &&
-    Object.isExtensible(value) &&
-    !value._isVue
-  ) {
-    ob = new Observer(value)
-  }
-  if (ob && vm) {
-    ob.addVm(vm)
-  }
-  return ob
-}
-
-/**
- * Define a reactive property on an Object.
- *
- * @param {Object} obj
- * @param {String} key
- * @param {*} val
- */
-
-export function defineReactive (obj, key, val) {
-  const dep = new Dep()
-
-  const property = Object.getOwnPropertyDescriptor(obj, key)
-  if (property && property.configurable === false) {
-    return
-  }
-
-  // cater for pre-defined getter/setters
-  const getter = property && property.get
-  const setter = property && property.set
-
-  let childOb = observe(val)
-  Object.defineProperty(obj, key, {
-    enumerable: true,
-    configurable: true,
-    get: function reactiveGetter () {
-      const value = getter ? getter.call(obj) : val
-      if (Dep.target) {
-        dep.depend()
-        if (childOb) {
-          childOb.dep.depend()
-        }
-        if (Array.isArray(value)) {
-          for (let e, i = 0, l = value.length; i < l; i++) {
-            e = value[i]
-            e && e.__ob__ && e.__ob__.dep.depend()
-          }
-        }
-      }
-      return value
-    },
-    set: function reactiveSetter (newVal) {
-      const value = getter ? getter.call(obj) : val
-      if (newVal === value) {
-        return
-      }
-      if (setter) {
-        setter.call(obj, newVal)
-      } else {
-        val = newVal
-      }
-      childOb = observe(newVal)
-      dep.notify()
-    }
-  })
-}
-
-/**
- * Set a property on an object. Adds the new property and
- * triggers change notification if the property doesn't
- * already exist.
- *
- * @param {Object} obj
- * @param {String} key
- * @param {*} val
- * @public
- */
-
-/* istanbul ignore next */
-export function set (obj, key, val) {
-  if (Array.isArray(obj)) {
-    return obj.splice(key, 1, val)
-  }
-  if (hasOwn(obj, key)) {
-    obj[key] = val
-    return
-  }
-  if (obj._isVue) {
-    set(obj._data, key, val)
-    return
-  }
-  const ob = obj.__ob__
-  if (!ob) {
-    obj[key] = val
-    return
-  }
-  ob.convert(key, val)
-  ob.dep.notify()
-  if (ob.vms) {
-    let i = ob.vms.length
-    while (i--) {
-      const vm = ob.vms[i]
-      proxy(vm, key)
-      // vm.$forceUpdate()
-    }
-  }
-  return val
-}
-
-/**
- * Delete a property and trigger change if necessary.
- *
- * @param {Object} obj
- * @param {String} key
- */
-
-/* istanbul ignore next */
-export function del (obj, key) {
-  if (!hasOwn(obj, key)) {
-    return
-  }
-  delete obj[key]
-  const ob = obj.__ob__
-
-  if (!ob) {
-    if (obj._isVue) {
-      delete obj._data[key]
-      // obj.$forceUpdate()
-    }
-    return
-  }
-  ob.dep.notify()
-  if (ob.vms) {
-    let i = ob.vms.length
-    while (i--) {
-      const vm = ob.vms[i]
-      unproxy(vm, key)
-      // vm.$forceUpdate()
-    }
-  }
-}
-
-const KEY_WORDS = ['$index', '$value', '$event']
-export function proxy (vm, key) {
-  if (KEY_WORDS.indexOf(key) > -1 || !isReserved(key)) {
-    Object.defineProperty(vm, key, {
-      configurable: true,
-      enumerable: true,
-      get: function proxyGetter () {
-        return vm._data[key]
-      },
-      set: function proxySetter (val) {
-        vm._data[key] = val
-      }
-    })
-  }
-}
-
-/* istanbul ignore next */
-export function unproxy (vm, key) {
-  if (!isReserved(key)) {
-    delete vm[key]
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/core/state.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/core/state.js b/html5/frameworks/legacy/core/state.js
deleted file mode 100644
index 5300dbf..0000000
--- a/html5/frameworks/legacy/core/state.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/* eslint-disable */
-
-
-import Watcher from './watcher'
-import Dep from './dep'
-import {
-  observe,
-  proxy,
-  unproxy
-} from './observer'
-import {
-  isPlainObject,
-  bind
-} from '../util/index'
-
-export function initState (vm) {
-  vm._watchers = []
-  initData(vm)
-  initComputed(vm)
-  initMethods(vm)
-}
-
-export function initData (vm) {
-  let data = vm._data
-
-  if (!isPlainObject(data)) {
-    data = {}
-  }
-  // proxy data on instance
-  const keys = Object.keys(data)
-  let i = keys.length
-  while (i--) {
-    proxy(vm, keys[i])
-  }
-  // observe data
-  observe(data, vm)
-}
-
-/* istanbul ignore next */
-function noop () {
-}
-
-export function initComputed (vm) {
-  const computed = vm._computed
-  if (computed) {
-    for (let key in computed) {
-      const userDef = computed[key]
-      const def = {
-        enumerable: true,
-        configurable: true
-      }
-      if (typeof userDef === 'function') {
-        def.get = makeComputedGetter(userDef, vm)
-        def.set = noop
-      } else {
-        def.get = userDef.get
-          ? userDef.cache !== false
-            ? makeComputedGetter(userDef.get, vm)
-            : bind(userDef.get, vm)
-          : noop
-        def.set = userDef.set
-          ? bind(userDef.set, vm)
-          : noop
-      }
-      Object.defineProperty(vm, key, def)
-    }
-  }
-}
-
-function makeComputedGetter (getter, owner) {
-  const watcher = new Watcher(owner, getter, null, {
-    lazy: true
-  })
-  return function computedGetter () {
-    if (watcher.dirty) {
-      watcher.evaluate()
-    }
-    if (Dep.target) {
-      watcher.depend()
-    }
-    return watcher.value
-  }
-}
-
-export function initMethods (vm) {
-  const methods = vm._methods
-  if (methods) {
-    for (let key in methods) {
-      vm[key] = methods[key]
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/core/watcher.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/core/watcher.js b/html5/frameworks/legacy/core/watcher.js
deleted file mode 100644
index cfd6370..0000000
--- a/html5/frameworks/legacy/core/watcher.js
+++ /dev/null
@@ -1,254 +0,0 @@
-/* eslint-disable */
-
-
-import Dep, { pushTarget, popTarget } from './dep'
-// import { pushWatcher } from './batcher'
-import {
-  remove,
-  extend,
-  isObject,
-  createNewSet
-  // _Set as Set
-} from '../util/index'
-
-let uid = 0
-
-/**
- * A watcher parses an expression, collects dependencies,
- * and fires callback when the expression value changes.
- * This is used for both the $watch() api and directives.
- *
- * @param {Vue} vm
- * @param {String|Function} expOrFn
- * @param {Function} cb
- * @param {Object} options
- *                 - {Array} filters
- *                 - {Boolean} twoWay
- *                 - {Boolean} deep
- *                 - {Boolean} user
- *                 - {Boolean} sync
- *                 - {Boolean} lazy
- *                 - {Function} [preProcess]
- *                 - {Function} [postProcess]
- * @constructor
- */
-
-export default function Watcher (vm, expOrFn, cb, options) {
-  // mix in options
-  if (options) {
-    extend(this, options)
-  }
-  const isFn = typeof expOrFn === 'function'
-  this.vm = vm
-  vm._watchers.push(this)
-  this.expression = expOrFn
-  this.cb = cb
-  this.id = ++uid // uid for batching
-  this.active = true
-  this.dirty = this.lazy // for lazy watchers
-  this.deps = []
-  this.newDeps = []
-  this.depIds = createNewSet() // new Set()
-  this.newDepIds = createNewSet() // new Set()
-  // parse expression for getter
-  if (isFn) {
-    this.getter = expOrFn
-  }
-  this.value = this.lazy
-    ? undefined
-    : this.get()
-  // state for avoiding false triggers for deep and Array
-  // watchers during vm._digest()
-  this.queued = this.shallow = false
-}
-
-/**
- * Evaluate the getter, and re-collect dependencies.
- */
-
-Watcher.prototype.get = function () {
-  pushTarget(this)
-  const value = this.getter.call(this.vm, this.vm)
-  // "touch" every property so they are all tracked as
-  // dependencies for deep watching
-  if (this.deep) {
-    traverse(value)
-  }
-  popTarget()
-  this.cleanupDeps()
-  return value
-}
-
-/**
- * Add a dependency to this directive.
- *
- * @param {Dep} dep
- */
-
-Watcher.prototype.addDep = function (dep) {
-  const id = dep.id
-  if (!this.newDepIds.has(id)) {
-    this.newDepIds.add(id)
-    this.newDeps.push(dep)
-    if (!this.depIds.has(id)) {
-      dep.addSub(this)
-    }
-  }
-}
-
-/**
- * Clean up for dependency collection.
- */
-
-Watcher.prototype.cleanupDeps = function () {
-  let i = this.deps.length
-  while (i--) {
-    const dep = this.deps[i]
-    if (!this.newDepIds.has(dep.id)) {
-      dep.removeSub(this)
-    }
-  }
-  let tmp = this.depIds
-  this.depIds = this.newDepIds
-  this.newDepIds = tmp
-  this.newDepIds.clear()
-  tmp = this.deps
-  this.deps = this.newDeps
-  this.newDeps = tmp
-  this.newDeps.length = 0
-}
-
-/**
- * Subscriber interface.
- * Will be called when a dependency changes.
- *
- * @param {Boolean} shallow
- */
-
-Watcher.prototype.update = function (shallow) {
-  if (this.lazy) {
-    this.dirty = true
-  } else {
-    this.run()
-  }
-  // } else if (this.sync) {
-  //   this.run()
-  // } else {
-  //   // if queued, only overwrite shallow with non-shallow,
-  //   // but not the other way around.
-  //   this.shallow = this.queued
-  //     ? shallow
-  //       ? this.shallow
-  //       : false
-  //     : !!shallow
-  //   this.queued = true
-  //   pushWatcher(this)
-  // }
-}
-
-/**
- * Batcher job interface.
- * Will be called by the batcher.
- */
-
-Watcher.prototype.run = function () {
-  if (this.active) {
-    const value = this.get()
-    if (
-      value !== this.value ||
-      // Deep watchers and watchers on Object/Arrays should fire even
-      // when the value is the same, because the value may
-      // have mutated; but only do so if this is a
-      // non-shallow update (caused by a vm digest).
-      ((isObject(value) || this.deep) && !this.shallow)
-    ) {
-      // set new value
-      const oldValue = this.value
-      this.value = value
-      this.cb.call(this.vm, value, oldValue)
-    }
-    this.queued = this.shallow = false
-  }
-}
-
-/**
- * Evaluate the value of the watcher.
- * This only gets called for lazy watchers.
- */
-
-Watcher.prototype.evaluate = function () {
-  this.value = this.get()
-  this.dirty = false
-}
-
-/**
- * Depend on all deps collected by this watcher.
- */
-
-Watcher.prototype.depend = function () {
-  let i = this.deps.length
-  while (i--) {
-    this.deps[i].depend()
-  }
-}
-
-/**
- * Remove self from all dependencies' subcriber list.
- */
-
-Watcher.prototype.teardown = function () {
-  if (this.active) {
-    // remove self from vm's watcher list
-    // this is a somewhat expensive operation so we skip it
-    // if the vm is being destroyed or is performing a v-for
-    // re-render (the watcher list is then filtered by v-for).
-    if (!this.vm._isBeingDestroyed && !this.vm._vForRemoving) {
-      remove(this.vm._watchers, this)
-    }
-    let i = this.deps.length
-    while (i--) {
-      this.deps[i].removeSub(this)
-    }
-    this.active = false
-    this.vm = this.cb = this.value = null
-  }
-}
-
-/**
- * Recrusively traverse an object to evoke all converted
- * getters, so that every nested property inside the object
- * is collected as a "deep" dependency.
- *
- * @param {*} val
- * @param {Set} seen
- */
-
-const seenObjects = createNewSet() // new Set()
-/* istanbul ignore next */
-function traverse (val, seen) {
-  let i, keys, isA, isO
-  if (!seen) {
-    seen = seenObjects
-    seen.clear()
-  }
-  isA = Array.isArray(val)
-  isO = isObject(val)
-  if (isA || isO) {
-    if (val.__ob__) {
-      const depId = val.__ob__.dep.id
-      if (seen.has(depId)) {
-        return
-      } else {
-        seen.add(depId)
-      }
-    }
-    if (isA) {
-      i = val.length
-      while (i--) traverse(val[i], seen)
-    } else if (isO) {
-      keys = Object.keys(val)
-      i = keys.length
-      while (i--) traverse(val[keys[i]], seen)
-    }
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/index.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/index.js b/html5/frameworks/legacy/index.js
deleted file mode 100644
index 366ecb3..0000000
--- a/html5/frameworks/legacy/index.js
+++ /dev/null
@@ -1,40 +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 Weex framework entry.
- */
-
-import * as methods from './api/methods'
-
-import Vm from './vm'
-export { createInstance } from './static/create'
-export { init, refreshInstance, destroyInstance } from './static/life'
-import { registerComponents, registerModules, registerMethods } from './static/register'
-export { receiveTasks } from './static/bridge'
-export { getRoot } from './static/misc'
-
-// register special methods for Weex framework
-registerMethods(methods)
-
-/**
- * Prevent modification of Vm and Vm.prototype
- */
-Object.freeze(Vm)
-
-export { registerComponents, registerModules, registerMethods }

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/static/bridge.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/static/bridge.js b/html5/frameworks/legacy/static/bridge.js
deleted file mode 100644
index 665b1ad..0000000
--- a/html5/frameworks/legacy/static/bridge.js
+++ /dev/null
@@ -1,56 +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 { instanceMap } from './map'
-import {
-  fireEvent,
-  callback
-} from '../app/ctrl/index'
-
-const jsHandlers = {
-  fireEvent: (id, ...args) => {
-    return fireEvent(instanceMap[id], ...args)
-  },
-  callback: (id, ...args) => {
-    return callback(instanceMap[id], ...args)
-  }
-}
-
-/**
- * Accept calls from native (event or callback).
- *
- * @param  {string} id
- * @param  {array} tasks list with `method` and `args`
- */
-export function receiveTasks (id, tasks) {
-  const instance = instanceMap[id]
-  if (instance && Array.isArray(tasks)) {
-    const results = []
-    tasks.forEach((task) => {
-      const handler = jsHandlers[task.method]
-      const args = [...task.args]
-      /* istanbul ignore else */
-      if (typeof handler === 'function') {
-        args.unshift(id)
-        results.push(handler(...args))
-      }
-    })
-    return results
-  }
-  return new Error(`invalid instance id "${id}" or tasks`)
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/static/create.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/static/create.js b/html5/frameworks/legacy/static/create.js
deleted file mode 100644
index 1c963b3..0000000
--- a/html5/frameworks/legacy/static/create.js
+++ /dev/null
@@ -1,51 +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 App from '../app/index'
-import { instanceMap } from './map'
-import { init as initApp } from '../app/ctrl/index'
-import { resetTarget } from '../core/dep'
-
-/**
- * Create a Weex instance.
- *
- * @param  {string} id
- * @param  {string} code
- * @param  {object} options
- *         option `HAS_LOG` enable print log
- * @param  {object} data
- * @param  {object} info { created, ... services }
- */
-export function createInstance (id, code, options, data, info) {
-  const { services } = info || {}
-  resetTarget()
-  let instance = instanceMap[id]
-  /* istanbul ignore else */
-  options = options || {}
-  let result
-  /* istanbul ignore else */
-  if (!instance) {
-    instance = new App(id, options)
-    instanceMap[id] = instance
-    result = initApp(instance, code, data, services)
-  }
-  else {
-    result = new Error(`invalid instance id "${id}"`)
-  }
-  return (result instanceof Error) ? result : instance
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/static/life.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/static/life.js b/html5/frameworks/legacy/static/life.js
deleted file mode 100644
index 9cb8abc..0000000
--- a/html5/frameworks/legacy/static/life.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.
- */
-import config from '../config'
-import {
-  refresh,
-  destroy
-} from '../app/ctrl/index'
-import { instanceMap } from './map'
-import { resetTarget } from '../core/dep'
-
-/**
- * Init config informations for Weex framework
- * @param  {object} cfg
- */
-export function init (cfg) {
-  config.Document = cfg.Document
-  config.Element = cfg.Element
-  config.Comment = cfg.Comment
-  config.sendTasks = cfg.sendTasks
-  config.Listener = cfg.Listener
-}
-
-/**
- * Refresh a Weex instance with data.
- *
- * @param  {string} id
- * @param  {object} data
- */
-export function refreshInstance (id, data) {
-  const instance = instanceMap[id]
-  let result
-  /* istanbul ignore else */
-  if (instance) {
-    result = refresh(instance, data)
-  }
-  else {
-    result = new Error(`invalid instance id "${id}"`)
-  }
-  return result
-}
-
-/**
- * Destroy a Weex instance.
- * @param  {string} id
- */
-export function destroyInstance (id) {
-  // Markup some global state in native side
-  if (typeof markupState === 'function') {
-    markupState()
-  }
-
-  resetTarget()
-  const instance = instanceMap[id]
-  /* istanbul ignore else */
-  if (!instance) {
-    return new Error(`invalid instance id "${id}"`)
-  }
-  destroy(instance)
-  delete instanceMap[id]
-  // notifyContextDisposed is used to tell v8 to do a full GC,
-  // but this would have a negative performance impact on weex,
-  // because all the inline cache in v8 would get cleared
-  // during a full GC.
-  // To take care of both memory and performance, just tell v8
-  // to do a full GC every eighteen times.
-  const idNum = Math.round(id)
-  const round = 18
-  if (idNum > 0) {
-    const remainder = idNum % round
-    if (!remainder && typeof notifyTrimMemory === 'function') {
-      notifyTrimMemory()
-    }
-  }
-  return instanceMap
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/static/map.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/static/map.js b/html5/frameworks/legacy/static/map.js
deleted file mode 100644
index 8c94ef4..0000000
--- a/html5/frameworks/legacy/static/map.js
+++ /dev/null
@@ -1,19 +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.
- */
-export const instanceMap = {}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/static/misc.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/static/misc.js b/html5/frameworks/legacy/static/misc.js
deleted file mode 100644
index afbc5e3..0000000
--- a/html5/frameworks/legacy/static/misc.js
+++ /dev/null
@@ -1,40 +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 { instanceMap } from './map'
-import {
-  getRootElement
-} from '../app/ctrl/index'
-
-/**
- * Get a whole element tree of an instance for debugging.
- * @param  {string} id
- * @return {object} a virtual dom tree
- */
-export function getRoot (id) {
-  const instance = instanceMap[id]
-  let result
-  /* istanbul ignore else */
-  if (instance) {
-    result = getRootElement(instance)
-  }
-  else {
-    result = new Error(`invalid instance id "${id}"`)
-  }
-  return result
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/static/register.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/static/register.js b/html5/frameworks/legacy/static/register.js
deleted file mode 100644
index f21df89..0000000
--- a/html5/frameworks/legacy/static/register.js
+++ /dev/null
@@ -1,75 +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 Vm from '../vm/index'
-import config from '../config'
-import {
-  initModules,
-  initMethods
-} from '../app/register'
-
-const {
-  nativeComponentMap
-} = config
-
-/**
- * Register the name of each native component.
- * @param  {array} components array of name
- */
-export function registerComponents (components) {
-  if (Array.isArray(components)) {
-    components.forEach(function register (name) {
-      /* istanbul ignore if */
-      if (!name) {
-        return
-      }
-      if (typeof name === 'string') {
-        nativeComponentMap[name] = true
-      }
-      /* istanbul ignore else */
-      else if (typeof name === 'object' && typeof name.type === 'string') {
-        nativeComponentMap[name.type] = name
-      }
-    })
-  }
-}
-
-/**
- * Register the name and methods of each module.
- * @param  {object} modules a object of modules
- */
-export function registerModules (modules) {
-  /* istanbul ignore else */
-  if (typeof modules === 'object') {
-    initModules(modules)
-  }
-}
-
-/**
- * Register the name and methods of each api.
- * @param  {object} apis a object of apis
- */
-export function registerMethods (methods) {
-  /* istanbul ignore else */
-  if (typeof methods === 'object') {
-    initMethods(Vm, methods)
-  }
-}
-
-// @todo: Hack for this framework only. Will be re-designed or removed later.
-global.registerMethods = registerMethods

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/util/LICENSE
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/util/LICENSE b/html5/frameworks/legacy/util/LICENSE
deleted file mode 100644
index 4645bc4..0000000
--- a/html5/frameworks/legacy/util/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2013-2015 Yuxi Evan You
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/util/index.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/util/index.js b/html5/frameworks/legacy/util/index.js
deleted file mode 100644
index 0d716f4..0000000
--- a/html5/frameworks/legacy/util/index.js
+++ /dev/null
@@ -1,156 +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.
- */
-export {
-  extend,
-  def,
-  remove,
-  hasOwn,
-  bind,
-  toArray,
-  isObject,
-  isPlainObject
-} from './shared'
-
-/**
- * Check if a string starts with $ or _
- *
- * @param {String} str
- * @return {Boolean}
- */
-
-export function isReserved (str) {
-  const c = (str + '').charCodeAt(0)
-  return c === 0x24 || c === 0x5F
-}
-
-// can we use __proto__?
-export const hasProto = '__proto__' in {}
-
-let _Set
-/* istanbul ignore next */
-if (typeof Set !== 'undefined' && Set.toString().match(/native code/)) {
-  // use native Set when available.
-  _Set = Set
-}
-else {
-  // a non-standard Set polyfill that only works with primitive keys.
-  _Set = function () {
-    this.set = Object.create(null)
-  }
-  _Set.prototype.has = function (key) {
-    return this.set[key] !== undefined
-  }
-  _Set.prototype.add = function (key) {
-    if (key == null || this.set[key]) {
-      return
-    }
-    this.set[key] = 1
-  }
-  _Set.prototype.clear = function () {
-    this.set = Object.create(null)
-  }
-}
-
-export { _Set }
-
-/**
- * Polyfill in iOS7 by native because the JavaScript polyfill has memory problem.
- * @return {object}
- */
-
-export function createNewSet () {
-  /* istanbul ignore next */
-  /* eslint-disable */
-  if (typeof nativeSet === 'object') {
-    return nativeSet.create()
-  }
-  /* eslint-enable */
-  return new _Set()
-}
-
-/**
- * Create a cached version of a pure function.
- *
- * @param {Function} fn
- * @return {Function}
- */
-
-export function cached (fn) {
-  const cache = Object.create(null)
-  return function cachedFn (str) {
-    const hit = cache[str]
-    return hit || (cache[str] = fn(str))
-  }
-}
-
-/**
- * Camelize a hyphen-delmited string.
- *
- * @param {String} str
- * @return {String}
- */
-
-const camelizeRE = /-(\w)/g
-export const camelize = cached(str => {
-  return str.replace(camelizeRE, toUpper)
-})
-
-function toUpper (_, c) {
-  return c ? c.toUpperCase() : ''
-}
-
-/**
- * Hyphenate a camelCase string.
- *
- * @param {String} str
- * @return {String}
- */
-
-const hyphenateRE = /([a-z\d])([A-Z])/g
-export const hyphenate = cached(str => {
-  return str
-    .replace(hyphenateRE, '$1-$2')
-    .toLowerCase()
-})
-
-export function typof (v) {
-  const s = Object.prototype.toString.call(v)
-  return s.substring(8, s.length - 1).toLowerCase()
-}
-
-// weex name rules
-
-const WEEX_COMPONENT_REG = /^@weex-component\//
-const WEEX_MODULE_REG = /^@weex-module\//
-const NORMAL_MODULE_REG = /^\.{1,2}\//
-const JS_SURFIX_REG = /\.js$/
-
-export const isWeexComponent = name => !!name.match(WEEX_COMPONENT_REG)
-export const isWeexModule = name => !!name.match(WEEX_MODULE_REG)
-export const isNormalModule = name => !!name.match(NORMAL_MODULE_REG)
-export const isNpmModule = name => !isWeexComponent(name) && !isWeexModule(name) && !isNormalModule(name)
-
-export function removeWeexPrefix (str) {
-  const result = str.replace(WEEX_COMPONENT_REG, '').replace(WEEX_MODULE_REG, '')
-  return result
-}
-
-export function removeJSSurfix (str) {
-  return str.replace(JS_SURFIX_REG, '')
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/util/shared.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/util/shared.js b/html5/frameworks/legacy/util/shared.js
deleted file mode 100644
index faf76e1..0000000
--- a/html5/frameworks/legacy/util/shared.js
+++ /dev/null
@@ -1,162 +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.
- */
-/**
- * Mix properties into target object.
- *
- * @param {Object} to
- * @param {Object} from
- */
-
-function extend (target, ...src) {
-  /* istanbul ignore else */
-  if (typeof Object.assign === 'function') {
-    Object.assign(target, ...src)
-  }
-  else {
-    const first = src.shift()
-    for (const key in first) {
-      target[key] = first[key]
-    }
-    if (src.length) {
-      extend(target, ...src)
-    }
-  }
-  return target
-}
-
-/**
- * Define a property.
- *
- * @param {Object} obj
- * @param {String} key
- * @param {*} val
- * @param {Boolean} [enumerable]
- */
-
-function def (obj, key, val, enumerable) {
-  Object.defineProperty(obj, key, {
-    value: val,
-    enumerable: !!enumerable,
-    writable: true,
-    configurable: true
-  })
-}
-
-/**
- * Remove an item from an array
- *
- * @param {Array} arr
- * @param {*} item
- */
-
-function remove (arr, item) {
-  if (arr.length) {
-    const index = arr.indexOf(item)
-    if (index > -1) {
-      return arr.splice(index, 1)
-    }
-  }
-}
-
-/**
- * Check whether the object has the property.
- *
- * @param {Object} obj
- * @param {String} key
- * @return {Boolean}
- */
-const hasOwnProperty = Object.prototype.hasOwnProperty
-function hasOwn (obj, key) {
-  return hasOwnProperty.call(obj, key)
-}
-
-/**
- * Simple bind, faster than native
- *
- * @param {Function} fn
- * @param {Object} ctx
- * @return {Function}
- */
-
-function bind (fn, ctx) {
-  return function (a) {
-    const l = arguments.length
-    return l
-      ? l > 1
-        ? fn.apply(ctx, arguments)
-        : fn.call(ctx, a)
-      : fn.call(ctx)
-  }
-}
-
-/**
- * Convert an Array-like object to a real Array.
- *
- * @param {Array-like} list
- * @param {Number} [start] - start index
- * @return {Array}
- */
-
-function toArray (list, start) {
-  start = start || 0
-  let i = list.length - start
-  const ret = new Array(i)
-  while (i--) {
-    ret[i] = list[i + start]
-  }
-  return ret
-}
-
-/**
- * Quick object check - this is primarily used to tell
- * Objects from primitive values when we know the value
- * is a JSON-compliant type.
- *
- * @param {*} obj
- * @return {Boolean}
- */
-
-function isObject (obj) {
-  return obj !== null && typeof obj === 'object'
-}
-
-/**
- * Strict object type check. Only returns true
- * for plain JavaScript objects.
- *
- * @param {*} obj
- * @return {Boolean}
- */
-
-const toString = Object.prototype.toString
-const OBJECT_STRING = '[object Object]'
-function isPlainObject (obj) {
-  return toString.call(obj) === OBJECT_STRING
-}
-
-export {
-  extend,
-  def,
-  remove,
-  hasOwn,
-  bind,
-  toArray,
-  isObject,
-  isPlainObject
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/vm/compiler.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/vm/compiler.js b/html5/frameworks/legacy/vm/compiler.js
deleted file mode 100644
index ffca01f..0000000
--- a/html5/frameworks/legacy/vm/compiler.js
+++ /dev/null
@@ -1,636 +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
- * ViewModel template parser & data-binding process
- */
-
-import {
-  extend,
-  isObject,
-  bind
-} from '../util/index'
-import {
-  initData,
-  initComputed
-} from '../core/state'
-import {
-  bindElement,
-  setId,
-  bindSubVm,
-  bindSubVmAfterInitialized,
-  applyNaitveComponentOptions,
-  watch
-} from './directive'
-import {
-  createBlock,
-  createBody,
-  createElement,
-  attachTarget,
-  moveTarget,
-  removeTarget
-} from './dom-helper'
-
-/**
- * build()
- *   compile(template, parentNode)
- *     if (type is content) create contentNode
- *     else if (dirs have v-for) foreach -> create context
- *       -> compile(templateWithoutFor, parentNode): diff(list) onchange
- *     else if (dirs have v-if) assert
- *       -> compile(templateWithoutIf, parentNode): toggle(shown) onchange
- *     else if (type is dynamic)
- *       -> compile(templateWithoutDynamicType, parentNode): watch(type) onchange
- *     else if (type is custom)
- *       addChildVm(vm, parentVm)
- *       build(externalDirs)
- *       foreach childNodes -> compile(childNode, template)
- *     else if (type is native)
- *       set(dirs): update(id/attr/style/class) onchange
- *       append(template, parentNode)
- *       foreach childNodes -> compile(childNode, template)
- */
-export function build (vm) {
-  const opt = vm._options || {}
-  const template = opt.template || {}
-
-  if (opt.replace) {
-    if (template.children && template.children.length === 1) {
-      compile(vm, template.children[0], vm._parentEl)
-    }
-    else {
-      compile(vm, template.children, vm._parentEl)
-    }
-  }
-  else {
-    compile(vm, template, vm._parentEl)
-  }
-
-  console.debug(`[JS Framework] "ready" lifecycle in Vm(${vm._type})`)
-  vm.$emit('hook:ready')
-  vm._ready = true
-}
-
-/**
- * Generate elements by child or children and append to parent elements.
- * Root element info would be merged if has. The first argument may be an array
- * if the root element with options.replace has not only one child.
- *
- * @param {object|array} target
- * @param {object}       dest
- * @param {object}       meta
- */
-function compile (vm, target, dest, meta) {
-  const app = vm._app || {}
-
-  if (app.lastSignal === -1) {
-    return
-  }
-
-  if (target.attr && target.attr.hasOwnProperty('static')) {
-    vm._static = true
-  }
-
-  if (targetIsFragment(target)) {
-    compileFragment(vm, target, dest, meta)
-    return
-  }
-  meta = meta || {}
-  if (targetIsContent(target)) {
-    console.debug('[JS Framework] compile "content" block by', target)
-    vm._content = createBlock(vm, dest)
-    return
-  }
-
-  if (targetNeedCheckRepeat(target, meta)) {
-    console.debug('[JS Framework] compile "repeat" logic by', target)
-    if (dest.type === 'document') {
-      console.warn('[JS Framework] The root element does\'t support `repeat` directive!')
-    }
-    else {
-      compileRepeat(vm, target, dest)
-    }
-    return
-  }
-  if (targetNeedCheckShown(target, meta)) {
-    console.debug('[JS Framework] compile "if" logic by', target)
-    if (dest.type === 'document') {
-      console.warn('[JS Framework] The root element does\'t support `if` directive!')
-    }
-    else {
-      compileShown(vm, target, dest, meta)
-    }
-    return
-  }
-  const typeGetter = meta.type || target.type
-  if (targetNeedCheckType(typeGetter, meta)) {
-    compileType(vm, target, dest, typeGetter, meta)
-    return
-  }
-  const type = typeGetter
-  const component = targetIsComposed(vm, target, type)
-  if (component) {
-    console.debug('[JS Framework] compile composed component by', target)
-    compileCustomComponent(vm, component, target, dest, type, meta)
-    return
-  }
-  console.debug('[JS Framework] compile native component by', target)
-  compileNativeComponent(vm, target, dest, type)
-}
-
-/**
- * Check if target is a fragment (an array).
- *
- * @param  {object}  target
- * @return {boolean}
- */
-function targetIsFragment (target) {
-  return Array.isArray(target)
-}
-
-/**
- * Check if target type is content/slot.
- *
- * @param  {object}  target
- * @return {boolean}
- */
-function targetIsContent (target) {
-  return target.type === 'content' || target.type === 'slot'
-}
-
-/**
- * Check if target need to compile by a list.
- *
- * @param  {object}  target
- * @param  {object}  meta
- * @return {boolean}
- */
-function targetNeedCheckRepeat (target, meta) {
-  return !meta.hasOwnProperty('repeat') && target.repeat
-}
-
-/**
- * Check if target need to compile by a boolean value.
- *
- * @param  {object}  target
- * @param  {object}  meta
- * @return {boolean}
- */
-function targetNeedCheckShown (target, meta) {
-  return !meta.hasOwnProperty('shown') && target.shown
-}
-
-/**
- * Check if target need to compile by a dynamic type.
- *
- * @param  {string|function} typeGetter
- * @param  {object}          meta
- * @return {boolean}
- */
-function targetNeedCheckType (typeGetter, meta) {
-  return (typeof typeGetter === 'function') && !meta.hasOwnProperty('type')
-}
-
-/**
- * Check if this kind of component is composed.
- *
- * @param  {string}  type
- * @return {boolean}
- */
-function targetIsComposed (vm, target, type) {
-  let component
-  if (vm._app && vm._app.customComponentMap) {
-    component = vm._app.customComponentMap[type]
-  }
-  if (vm._options && vm._options.components) {
-    component = vm._options.components[type]
-  }
-  if (target.component) {
-    component = component || {}
-  }
-  return component
-}
-
-/**
- * Compile a list of targets.
- *
- * @param {object} target
- * @param {object} dest
- * @param {object} meta
- */
-function compileFragment (vm, target, dest, meta) {
-  const fragBlock = createBlock(vm, dest)
-  target.forEach((child) => {
-    compile(vm, child, fragBlock, meta)
-  })
-}
-
-/**
- * Compile a target with repeat directive.
- *
- * @param {object} target
- * @param {object} dest
- */
-function compileRepeat (vm, target, dest) {
-  const repeat = target.repeat
-  const oldStyle = typeof repeat === 'function'
-  let getter = repeat.getter || repeat.expression || repeat
-  if (typeof getter !== 'function') {
-    getter = function () { return [] }
-  }
-  const key = repeat.key || '$index'
-  const value = repeat.value || '$value'
-  const trackBy = repeat.trackBy || target.trackBy ||
-    (target.attr && target.attr.trackBy)
-
-  const fragBlock = createBlock(vm, dest)
-  fragBlock.children = []
-  fragBlock.data = []
-  fragBlock.vms = []
-
-  bindRepeat(vm, target, fragBlock, { getter, key, value, trackBy, oldStyle })
-}
-
-/**
- * Compile a target with if directive.
- *
- * @param {object} target
- * @param {object} dest
- * @param {object} meta
- */
-function compileShown (vm, target, dest, meta) {
-  const newMeta = { shown: true }
-  const fragBlock = createBlock(vm, dest)
-
-  if (dest.element && dest.children) {
-    dest.children.push(fragBlock)
-  }
-
-  if (meta.repeat) {
-    newMeta.repeat = meta.repeat
-  }
-
-  bindShown(vm, target, fragBlock, newMeta)
-}
-
-/**
- * Compile a target with dynamic component type.
- *
- * @param {object}   target
- * @param {object}   dest
- * @param {function} typeGetter
- */
-function compileType (vm, target, dest, typeGetter, meta) {
-  const type = typeGetter.call(vm)
-  const newMeta = extend({ type }, meta)
-  const fragBlock = createBlock(vm, dest)
-
-  if (dest.element && dest.children) {
-    dest.children.push(fragBlock)
-  }
-
-  watch(vm, typeGetter, (value) => {
-    const newMeta = extend({ type: value }, meta)
-    removeTarget(vm, fragBlock, true)
-    compile(vm, target, fragBlock, newMeta)
-  })
-
-  compile(vm, target, fragBlock, newMeta)
-}
-
-/**
- * Compile a composed component.
- *
- * @param {object} target
- * @param {object} dest
- * @param {string} type
- */
-function compileCustomComponent (vm, component, target, dest, type, meta) {
-  const Ctor = vm.constructor
-  const subVm = new Ctor(type, component, vm, dest, undefined, {
-    'hook:init': function () {
-      if (vm._static) {
-        this._static = vm._static
-      }
-      setId(vm, null, target.id, this)
-      // bind template earlier because of lifecycle issues
-      this._externalBinding = {
-        parent: vm,
-        template: target
-      }
-    },
-    'hook:created': function () {
-      bindSubVm(vm, this, target, meta.repeat)
-    },
-    'hook:ready': function () {
-      if (this._content) {
-        compileChildren(vm, target, this._content)
-      }
-    }
-  })
-  bindSubVmAfterInitialized(vm, subVm, target, dest)
-}
-
-/**
- * Generate element from template and attach to the dest if needed.
- * The time to attach depends on whether the mode status is node or tree.
- *
- * @param {object} template
- * @param {object} dest
- * @param {string} type
- */
-function compileNativeComponent (vm, template, dest, type) {
-  applyNaitveComponentOptions(template)
-
-  let element
-  if (dest.ref === '_documentElement') {
-    // if its parent is documentElement then it's a body
-    console.debug(`[JS Framework] compile to create body for ${type}`)
-    element = createBody(vm, type)
-  }
-  else {
-    console.debug(`[JS Framework] compile to create element for ${type}`)
-    element = createElement(vm, type)
-  }
-
-  if (!vm._rootEl) {
-    vm._rootEl = element
-    // bind event earlier because of lifecycle issues
-    const binding = vm._externalBinding || {}
-    const target = binding.template
-    const parentVm = binding.parent
-    if (target && target.events && parentVm && element) {
-      for (const type in target.events) {
-        const handler = parentVm[target.events[type]]
-        if (handler) {
-          element.addEvent(type, bind(handler, parentVm))
-        }
-      }
-    }
-  }
-
-  bindElement(vm, element, template)
-
-  if (template.attr && template.attr.append) { // backward, append prop in attr
-    template.append = template.attr.append
-  }
-
-  if (template.append) { // give the append attribute for ios adaptation
-    element.attr = element.attr || {}
-    element.attr.append = template.append
-  }
-
-  const treeMode = template.append === 'tree'
-  const app = vm._app || {}
-  if (app.lastSignal !== -1 && !treeMode) {
-    console.debug('[JS Framework] compile to append single node for', element)
-    app.lastSignal = attachTarget(vm, element, dest)
-  }
-  if (app.lastSignal !== -1) {
-    compileChildren(vm, template, element)
-  }
-  if (app.lastSignal !== -1 && treeMode) {
-    console.debug('[JS Framework] compile to append whole tree for', element)
-    app.lastSignal = attachTarget(vm, element, dest)
-  }
-}
-
-/**
- * Set all children to a certain parent element.
- *
- * @param {object} template
- * @param {object} dest
- */
-function compileChildren (vm, template, dest) {
-  const app = vm._app || {}
-  const children = template.children
-  if (children && children.length) {
-    children.every((child) => {
-      compile(vm, child, dest)
-      return app.lastSignal !== -1
-    })
-  }
-}
-
-/**
- * Watch the list update and refresh the changes.
- *
- * @param {object} target
- * @param {object} fragBlock {vms, data, children}
- * @param {object} info      {getter, key, value, trackBy, oldStyle}
- */
-function bindRepeat (vm, target, fragBlock, info) {
-  const vms = fragBlock.vms
-  const children = fragBlock.children
-  const { getter, trackBy, oldStyle } = info
-  const keyName = info.key
-  const valueName = info.value
-
-  function compileItem (item, index, context) {
-    let mergedData
-    if (oldStyle) {
-      mergedData = item
-      if (isObject(item)) {
-        mergedData[keyName] = index
-        if (!mergedData.hasOwnProperty('INDEX')) {
-          Object.defineProperty(mergedData, 'INDEX', {
-            value: () => {
-              console.warn('[JS Framework] "INDEX" in repeat is deprecated, ' +
-                'please use "$index" instead')
-            }
-          })
-        }
-      }
-      else {
-        console.warn('[JS Framework] Each list item must be an object in old-style repeat, '
-          + 'please use `repeat={{v in list}}` instead.')
-        mergedData = {}
-        mergedData[keyName] = index
-        mergedData[valueName] = item
-      }
-    }
-    else {
-      mergedData = {}
-      mergedData[keyName] = index
-      mergedData[valueName] = item
-    }
-    const newContext = mergeContext(context, mergedData)
-    vms.push(newContext)
-    compile(newContext, target, fragBlock, { repeat: item })
-  }
-
-  const list = watchBlock(vm, fragBlock, getter, 'repeat',
-    (data) => {
-      console.debug('[JS Framework] the "repeat" item has changed', data)
-      if (!fragBlock || !data) {
-        return
-      }
-
-      const oldChildren = children.slice()
-      const oldVms = vms.slice()
-      const oldData = fragBlock.data.slice()
-      // 1. collect all new refs track by
-      const trackMap = {}
-      const reusedMap = {}
-      data.forEach((item, index) => {
-        const key = trackBy ? item[trackBy] : (oldStyle ? item[keyName] : index)
-        /* istanbul ignore if */
-        if (key == null || key === '') {
-          return
-        }
-        trackMap[key] = item
-      })
-
-      // 2. remove unused element foreach old item
-      const reusedList = []
-      oldData.forEach((item, index) => {
-        const key = trackBy ? item[trackBy] : (oldStyle ? item[keyName] : index)
-        if (trackMap.hasOwnProperty(key)) {
-          reusedMap[key] = {
-            item, index, key,
-            target: oldChildren[index],
-            vm: oldVms[index]
-          }
-          reusedList.push(item)
-        }
-        else {
-          removeTarget(vm, oldChildren[index])
-        }
-      })
-
-      // 3. create new element foreach new item
-      children.length = 0
-      vms.length = 0
-      fragBlock.data = data.slice()
-      fragBlock.updateMark = fragBlock.start
-
-      data.forEach((item, index) => {
-        const key = trackBy ? item[trackBy] : (oldStyle ? item[keyName] : index)
-        const reused = reusedMap[key]
-        if (reused) {
-          if (reused.item === reusedList[0]) {
-            reusedList.shift()
-          }
-          else {
-            reusedList.$remove(reused.item)
-            moveTarget(vm, reused.target, fragBlock.updateMark, true)
-          }
-          children.push(reused.target)
-          vms.push(reused.vm)
-          if (oldStyle) {
-            reused.vm = item
-          }
-          else {
-            reused.vm[valueName] = item
-          }
-          reused.vm[keyName] = index
-          fragBlock.updateMark = reused.target
-        }
-        else {
-          compileItem(item, index, vm)
-        }
-      })
-
-      delete fragBlock.updateMark
-    }
-  )
-
-  fragBlock.data = list.slice(0)
-  list.forEach((item, index) => {
-    compileItem(item, index, vm)
-  })
-}
-
-/**
- * Watch the display update and add/remove the element.
- *
- * @param  {object} target
- * @param  {object} fragBlock
- * @param  {object} context
- */
-function bindShown (vm, target, fragBlock, meta) {
-  const display = watchBlock(vm, fragBlock, target.shown, 'shown',
-    (display) => {
-      console.debug('[JS Framework] the "if" item was changed', display)
-
-      if (!fragBlock || !!fragBlock.display === !!display) {
-        return
-      }
-      fragBlock.display = !!display
-      if (display) {
-        compile(vm, target, fragBlock, meta)
-      }
-      else {
-        removeTarget(vm, fragBlock, true)
-      }
-    }
-  )
-
-  fragBlock.display = !!display
-  if (display) {
-    compile(vm, target, fragBlock, meta)
-  }
-}
-
-/**
- * Watch calc value changes and append certain type action to differ.
- * It is used for if or repeat data-binding generator.
- *
- * @param  {object}   fragBlock
- * @param  {function} calc
- * @param  {string}   type
- * @param  {function} handler
- * @return {any}      init value of calc
- */
-function watchBlock (vm, fragBlock, calc, type, handler) {
-  const differ = vm && vm._app && vm._app.differ
-  const config = {}
-  const depth = (fragBlock.element.depth || 0) + 1
-
-  return watch(vm, calc, (value) => {
-    config.latestValue = value
-    if (differ && !config.recorded) {
-      differ.append(type, depth, fragBlock.blockId, () => {
-        const latestValue = config.latestValue
-        handler(latestValue)
-        config.recorded = false
-        config.latestValue = undefined
-      })
-    }
-    config.recorded = true
-  })
-}
-
-/**
- * Clone a context and merge certain data.
- *
- * @param  {object} mergedData
- * @return {object}
- */
-function mergeContext (context, mergedData) {
-  const newContext = Object.create(context)
-  newContext._data = mergedData
-  initData(newContext)
-  initComputed(newContext)
-  newContext._realParent = context
-  if (context._static) {
-    newContext._static = context._static
-  }
-  return newContext
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/vm/directive.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/vm/directive.js b/html5/frameworks/legacy/vm/directive.js
deleted file mode 100644
index 77ccd9a..0000000
--- a/html5/frameworks/legacy/vm/directive.js
+++ /dev/null
@@ -1,377 +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
- * Directive Parser
- */
-
-import { bind, typof } from '../util/index'
-import Watcher from '../core/watcher'
-import config from '../config'
-
-const { nativeComponentMap } = config
-
-const SETTERS = {
-  attr: 'setAttr',
-  style: 'setStyle',
-  event: 'addEvent'
-}
-
-/**
- * apply the native component's options(specified by template.type)
- * to the template
- */
-export function applyNaitveComponentOptions (template) {
-  const { type } = template
-  const options = nativeComponentMap[type]
-
-  if (typeof options === 'object') {
-    for (const key in options) {
-      if (template[key] == null) {
-        template[key] = options[key]
-      }
-      else if (typof(template[key]) === 'object' &&
-        typof(options[key]) === 'object') {
-        for (const subkey in options[key]) {
-          if (template[key][subkey] == null) {
-            template[key][subkey] = options[key][subkey]
-          }
-        }
-      }
-    }
-  }
-}
-
-/**
- * bind all id, attr, classnames, style, events to an element
- */
-export function bindElement (vm, el, template) {
-  setId(vm, el, template.id, vm)
-  setAttr(vm, el, template.attr)
-  setClass(vm, el, template.classList)
-  setStyle(vm, el, template.style)
-  bindEvents(vm, el, template.events)
-}
-
-/**
- * bind all props to sub vm and bind all style, events to the root element
- * of the sub vm if it doesn't have a replaced multi-node fragment
- */
-export function bindSubVm (vm, subVm, template, repeatItem) {
-  subVm = subVm || {}
-  template = template || {}
-
-  const options = subVm._options || {}
-
-  // bind props
-  let props = options.props
-
-  if (Array.isArray(props)) {
-    props = props.reduce((result, value) => {
-      result[value] = true
-      return result
-    }, {})
-  }
-
-  mergeProps(repeatItem, props, vm, subVm)
-  mergeProps(template.attr, props, vm, subVm)
-}
-
-/**
- * merge class and styles from vm to sub vm.
- */
-export function bindSubVmAfterInitialized (vm, subVm, template, target = {}) {
-  mergeClassStyle(template.classList, vm, subVm)
-  mergeStyle(template.style, vm, subVm)
-
-  // bind subVm to the target element
-  if (target.children) {
-    target.children[target.children.length - 1]._vm = subVm
-  }
-  else {
-    target._vm = subVm
-  }
-}
-
-/**
- * Bind props from vm to sub vm and watch their updates.
- */
-function mergeProps (target, props, vm, subVm) {
-  if (!target) {
-    return
-  }
-  for (const key in target) {
-    if (!props || props[key]) {
-      const value = target[key]
-      if (typeof value === 'function') {
-        const returnValue = watch(vm, value, function (v) {
-          subVm[key] = v
-        })
-        subVm[key] = returnValue
-      }
-      else {
-        subVm[key] = value
-      }
-    }
-  }
-}
-
-/**
- * Bind style from vm to sub vm and watch their updates.
- */
-function mergeStyle (target, vm, subVm) {
-  for (const key in target) {
-    const value = target[key]
-    if (typeof value === 'function') {
-      const returnValue = watch(vm, value, function (v) {
-        if (subVm._rootEl) {
-          subVm._rootEl.setStyle(key, v)
-        }
-      })
-      subVm._rootEl.setStyle(key, returnValue)
-    }
-    else {
-      if (subVm._rootEl) {
-        subVm._rootEl.setStyle(key, value)
-      }
-    }
-  }
-}
-
-/**
- * Bind class & style from vm to sub vm and watch their updates.
- */
-function mergeClassStyle (target, vm, subVm) {
-  const css = vm._options && vm._options.style || {}
-
-  /* istanbul ignore if */
-  if (!subVm._rootEl) {
-    return
-  }
-
-  const className = '@originalRootEl'
-  css[className] = subVm._rootEl.classStyle
-
-  function addClassName (list, name) {
-    if (typof(list) === 'array') {
-      list.unshift(name)
-    }
-  }
-
-  if (typeof target === 'function') {
-    const value = watch(vm, target, v => {
-      addClassName(v, className)
-      setClassStyle(subVm._rootEl, css, v)
-    })
-    addClassName(value, className)
-    setClassStyle(subVm._rootEl, css, value)
-  }
-  else if (target != null) {
-    addClassName(target, className)
-    setClassStyle(subVm._rootEl, css, target)
-  }
-}
-
-/**
- * bind id to an element
- * each id is unique in a whole vm
- */
-export function setId (vm, el, id, target) {
-  const map = Object.create(null)
-
-  Object.defineProperties(map, {
-    vm: {
-      value: target,
-      writable: false,
-      configurable: false
-    },
-    el: {
-      get: () => el || target._rootEl,
-      configurable: false
-    }
-  })
-
-  if (typeof id === 'function') {
-    const handler = id
-    id = handler.call(vm)
-    if (id || id === 0) {
-      vm._ids[id] = map
-    }
-    watch(vm, handler, (newId) => {
-      if (newId) {
-        vm._ids[newId] = map
-      }
-    })
-  }
-  else if (id && typeof id === 'string') {
-    vm._ids[id] = map
-  }
-}
-
-/**
- * bind attr to an element
- */
-function setAttr (vm, el, attr) {
-  bindDir(vm, el, 'attr', attr)
-}
-
-function setClassStyle (el, css, classList) {
-  if (typeof classList === 'string') {
-    classList = classList.split(/\s+/)
-  }
-  classList.forEach((name, i) => {
-    classList.splice(i, 1, ...name.split(/\s+/))
-  })
-  const classStyle = {}
-  const length = classList.length
-
-  for (let i = 0; i < length; i++) {
-    const style = css[classList[i]]
-    if (style) {
-      Object.keys(style).forEach((key) => {
-        classStyle[key] = style[key]
-      })
-    }
-  }
-  el.setClassStyle(classStyle)
-}
-
-/**
- * bind classnames to an element
- */
-function setClass (vm, el, classList) {
-  if (typeof classList !== 'function' && !Array.isArray(classList)) {
-    return
-  }
-  if (Array.isArray(classList) && !classList.length) {
-    el.setClassStyle({})
-    return
-  }
-
-  const style = vm._options && vm._options.style || {}
-  if (typeof classList === 'function') {
-    const value = watch(vm, classList, v => {
-      setClassStyle(el, style, v)
-    })
-    setClassStyle(el, style, value)
-  }
-  else {
-    setClassStyle(el, style, classList)
-  }
-}
-
-/**
- * bind style to an element
- */
-function setStyle (vm, el, style) {
-  bindDir(vm, el, 'style', style)
-}
-
-/**
- * add an event type and handler to an element and generate a dom update
- */
-function setEvent (vm, el, type, handler) {
-  el.addEvent(type, bind(handler, vm))
-}
-
-/**
- * add all events of an element
- */
-function bindEvents (vm, el, events) {
-  if (!events) {
-    return
-  }
-  const keys = Object.keys(events)
-  let i = keys.length
-  while (i--) {
-    const key = keys[i]
-    let handler = events[key]
-    if (typeof handler === 'string') {
-      handler = vm[handler]
-      /* istanbul ignore if */
-      if (!handler) {
-        console.warn(`[JS Framework] The event handler "${handler}" is not defined.`)
-      }
-    }
-    setEvent(vm, el, key, handler)
-  }
-}
-
-/**
- * set a series of members as a kind of an element
- * for example: style, attr, ...
- * if the value is a function then bind the data changes
- */
-function bindDir (vm, el, name, data) {
-  if (!data) {
-    return
-  }
-  const keys = Object.keys(data)
-  let i = keys.length
-  while (i--) {
-    const key = keys[i]
-    const value = data[key]
-    if (typeof value === 'function') {
-      bindKey(vm, el, name, key, value)
-    }
-    else {
-      el[SETTERS[name]](key, value)
-    }
-  }
-}
-
-/**
- * bind data changes to a certain key to a name series in an element
- */
-function bindKey (vm, el, name, key, calc) {
-  const methodName = SETTERS[name]
-  // watch the calc, and returns a value by calc.call()
-  const value = watch(vm, calc, (value) => {
-    function handler () {
-      el[methodName](key, value)
-    }
-    const differ = vm && vm._app && vm._app.differ
-    if (differ) {
-      differ.append('element', el.depth || 0, el.ref, handler)
-    }
-    else {
-      handler()
-    }
-  })
-
-  el[methodName](key, value)
-}
-
-/**
- * watch a calc function and callback if the calc value changes
- */
-export function watch (vm, calc, callback) {
-  if (vm._static) {
-    return calc.call(vm, vm)
-  }
-  const watcher = new Watcher(vm, calc, function (value, oldValue) {
-    /* istanbul ignore if */
-    if (typeof value !== 'object' && value === oldValue) {
-      return
-    }
-    callback(value)
-  })
-
-  return watcher.value
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/vm/dom-helper.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/vm/dom-helper.js b/html5/frameworks/legacy/vm/dom-helper.js
deleted file mode 100644
index 6e5a800..0000000
--- a/html5/frameworks/legacy/vm/dom-helper.js
+++ /dev/null
@@ -1,258 +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 Document & Element Helpers.
- *
- * required:
- * Document#: createElement, createComment, getRef
- * Element#: appendChild, insertBefore, removeChild, nextSibling
- */
-
-/**
- * Create a body by type
- * Using this._app.doc
- *
- * @param  {string} type
- */
-export function createBody (vm, type) {
-  const doc = vm._app.doc
-  return doc.createBody(type)
-}
-
-/**
- * Create an element by type
- * Using this._app.doc
- *
- * @param  {string} type
- */
-export function createElement (vm, type) {
-  const doc = vm._app.doc
-  return doc.createElement(type)
-}
-
-/**
- * Create and return a frag block for an element.
- * The frag block has a starter, ender and the element itself.
- *
- * @param  {object} element
- */
-export function createBlock (vm, element) {
-  const start = createBlockStart(vm)
-  const end = createBlockEnd(vm)
-  const blockId = lastestBlockId++
-  if (element.element) {
-    let updateMark = element.updateMark
-    if (updateMark) {
-      if (updateMark.element) {
-        updateMark = updateMark.end
-      }
-      element.element.insertAfter(end, updateMark)
-      element.element.insertAfter(start, updateMark)
-      element.updateMark = end
-    }
-    else {
-      element.element.insertBefore(start, element.end)
-      element.element.insertBefore(end, element.end)
-    }
-    element = element.element
-  }
-  else {
-    element.appendChild(start)
-    element.appendChild(end)
-  }
-  return { start, end, element, blockId }
-}
-
-let lastestBlockId = 1
-
-/**
- * Create and return a block starter.
- * Using this._app.doc
- */
-function createBlockStart (vm) {
-  const doc = vm._app.doc
-  const anchor = doc.createComment('start')
-  return anchor
-}
-
-/**
- * Create and return a block ender.
- * Using this._app.doc
- */
-function createBlockEnd (vm) {
-  const doc = vm._app.doc
-  const anchor = doc.createComment('end')
-  return anchor
-}
-
-/**
- * Attach target to a certain dest using appendChild by default.
- * If the dest is a frag block then insert before the ender.
- * If the target is a frag block then attach the starter and ender in order.
- *
- * @param  {object} target
- * @param  {object} dest
- */
-export function attachTarget (vm, target, dest) {
-  if (dest.element) {
-    const before = dest.end
-    const after = dest.updateMark
-    // push new target for watch list update later
-    if (dest.children) {
-      dest.children.push(target)
-    }
-    // for check repeat case
-    if (after) {
-      const signal = moveTarget(vm, target, after)
-      dest.updateMark = target.element ? target.end : target
-      return signal
-    }
-    else if (target.element) {
-      dest.element.insertBefore(target.start, before)
-      dest.element.insertBefore(target.end, before)
-    }
-    else {
-      return dest.element.insertBefore(target, before)
-    }
-  }
-  else {
-    if (target.element) {
-      dest.appendChild(target.start)
-      dest.appendChild(target.end)
-    }
-    else {
-      return dest.appendChild(target)
-    }
-  }
-}
-
-/**
- * Move target before a certain element. The target maybe block or element.
- *
- * @param  {object} target
- * @param  {object} before
- */
-export function moveTarget (vm, target, after) {
-  if (target.element) {
-    return moveBlock(target, after)
-  }
-  return moveElement(target, after)
-}
-
-/**
- * Move element before a certain element.
- *
- * @param  {object} element
- * @param  {object} before
- */
-function moveElement (element, after) {
-  const parent = after.parentNode
-  if (parent) {
-    return parent.insertAfter(element, after)
-  }
-}
-
-/**
- * Move all elements of the block before a certain element.
- *
- * @param  {object} fragBlock
- * @param  {object} before
- */
-function moveBlock (fragBlock, after) {
-  const parent = after.parentNode
-
-  if (parent) {
-    let el = fragBlock.start
-    let signal
-    const group = [el]
-
-    while (el && el !== fragBlock.end) {
-      el = el.nextSibling
-      group.push(el)
-    }
-
-    let temp = after
-    group.every((el) => {
-      signal = parent.insertAfter(el, temp)
-      temp = el
-      return signal !== -1
-    })
-
-    return signal
-  }
-}
-
-/**
- * Remove target from DOM tree.
- * If the target is a frag block then call _removeBlock
- *
- * @param  {object} target
- */
-export function removeTarget (vm, target, preserveBlock = false) {
-  if (target.element) {
-    removeBlock(target, preserveBlock)
-  }
-  else {
-    removeElement(target)
-  }
-  if (target._vm) {
-    target._vm.$emit('hook:destroyed')
-  }
-}
-
-/**
- * Remove a certain element.
- * Using this._app.doc
- *
- * @param  {object} target
- */
-function removeElement (target) {
-  const parent = target.parentNode
-
-  if (parent) {
-    parent.removeChild(target)
-  }
-}
-
-/**
- * Remove a frag block.
- * The second param decides whether the block self should be removed too.
- *
- * @param  {object}  fragBlock
- * @param  {Boolean} preserveBlock=false
- */
-function removeBlock (fragBlock, preserveBlock = false) {
-  const result = []
-  let el = fragBlock.start.nextSibling
-
-  while (el && el !== fragBlock.end) {
-    result.push(el)
-    el = el.nextSibling
-  }
-
-  if (!preserveBlock) {
-    removeElement(fragBlock.start)
-  }
-  result.forEach((el) => {
-    removeElement(el)
-  })
-  if (!preserveBlock) {
-    removeElement(fragBlock.end)
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/vm/events.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/vm/events.js b/html5/frameworks/legacy/vm/events.js
deleted file mode 100644
index 5691472..0000000
--- a/html5/frameworks/legacy/vm/events.js
+++ /dev/null
@@ -1,179 +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
- * Everything about component event which includes event object, event listener,
- * event emitter and lifecycle hooks.
- */
-
-/**
- * Event object definition. An event object has `type`, `timestamp` and
- * `detail` from which a component emit. The event object could be dispatched to
- * parents or broadcasted to children except `this.stop()` is called.
- * @param {string} type
- * @param {any}    detail
- */
-function Evt (type, detail) {
-  if (detail instanceof Evt) {
-    return detail
-  }
-
-  this.timestamp = Date.now()
-  this.detail = detail
-  this.type = type
-
-  let shouldStop = false
-
-  /**
-   * stop dispatch and broadcast
-   */
-  this.stop = function () {
-    shouldStop = true
-  }
-
-  /**
-   * check if it can't be dispatched or broadcasted
-   */
-  this.hasStopped = function () {
-    return shouldStop
-  }
-}
-
-/**
- * Emit an event but not broadcast down or dispatch up.
- * @param  {string} type
- * @param  {any}    detail
- */
-export function $emit (type, detail) {
-  const events = this._vmEvents
-  const handlerList = events[type]
-  if (handlerList) {
-    const evt = new Evt(type, detail)
-    handlerList.forEach((handler) => {
-      handler.call(this, evt)
-    })
-  }
-}
-
-/**
- * Emit an event and dispatch it up.
- * @param  {string} type
- * @param  {any}    detail
- */
-export function $dispatch (type, detail) {
-  const evt = new Evt(type, detail)
-  this.$emit(type, evt)
-
-  if (!evt.hasStopped() && this._parent && this._parent.$dispatch) {
-    this._parent.$dispatch(type, evt)
-  }
-}
-
-/**
- * Emit an event and broadcast it down.
- * @param  {string} type
- * @param  {any}    detail
- */
-export function $broadcast (type, detail) {
-  const evt = new Evt(type, detail)
-  this.$emit(type, evt)
-
-  if (!evt.hasStopped() && this._childrenVms) {
-    this._childrenVms.forEach((subVm) => {
-      subVm.$broadcast(type, evt)
-    })
-  }
-}
-
-/**
- * Add event listener.
- * @param  {string}   type
- * @param  {function} handler
- */
-export function $on (type, handler) {
-  if (!type || typeof handler !== 'function') {
-    return
-  }
-  const events = this._vmEvents
-  const handlerList = events[type] || []
-  handlerList.push(handler)
-  events[type] = handlerList
-
-  // fixed old version lifecycle design
-  /* istanbul ignore if */
-  if (type === 'hook:ready' && this._ready) {
-    this.$emit('hook:ready')
-  }
-}
-
-/**
- * Remove event listener.
- * @param  {string}   type
- * @param  {function} handler
- */
-export function $off (type, handler) {
-  if (!type) {
-    return
-  }
-  const events = this._vmEvents
-  if (!handler) {
-    delete events[type]
-    return
-  }
-  const handlerList = events[type]
-  if (!handlerList) {
-    return
-  }
-  handlerList.$remove(handler)
-}
-
-const LIFE_CYCLE_TYPES = ['init', 'created', 'ready', 'destroyed']
-
-/**
- * Init events:
- * 1. listen `events` in component options & `externalEvents`.
- * 2. bind lifecycle hooks.
- * @param  {Vm}     vm
- * @param  {object} externalEvents
- */
-export function initEvents (vm, externalEvents) {
-  const options = vm._options || {}
-  const events = options.events || {}
-  for (const type1 in events) {
-    vm.$on(type1, events[type1])
-  }
-  for (const type2 in externalEvents) {
-    vm.$on(type2, externalEvents[type2])
-  }
-  LIFE_CYCLE_TYPES.forEach((type) => {
-    vm.$on(`hook:${type}`, options[type])
-  })
-}
-
-/**
- * Bind event related methods to ViewModel instance.
- * @param  {Vm} vm
- */
-export function mixinEvents (vm) {
-  vm.$emit = $emit
-  vm.$dispatch = $dispatch
-  vm.$broadcast = $broadcast
-  vm.$on = $on
-  vm.$off = $off
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/legacy/vm/index.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/legacy/vm/index.js b/html5/frameworks/legacy/vm/index.js
deleted file mode 100644
index 45a33af..0000000
--- a/html5/frameworks/legacy/vm/index.js
+++ /dev/null
@@ -1,131 +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
- * ViewModel Constructor & definition
- */
-
-import { extend } from '../util/index'
-import {
-  initState
-} from '../core/state'
-import {
-  build
-} from './compiler'
-import {
-  set,
-  del
-} from '../core/observer'
-import {
-  watch
-} from './directive'
-import {
-  initEvents,
-  mixinEvents
-} from './events'
-
-/**
- * ViewModel constructor
- *
- * @param {string} type
- * @param {object} options    component options
- * @param {object} parentVm   which contains _app
- * @param {object} parentEl   root element or frag block
- * @param {object} mergedData external data
- * @param {object} externalEvents external events
- */
-export default function Vm (
-  type,
-  options,
-  parentVm,
-  parentEl,
-  mergedData,
-  externalEvents
-) {
-  parentVm = parentVm || {}
-  this._parent = parentVm._realParent ? parentVm._realParent : parentVm
-  this._app = parentVm._app || {}
-  parentVm._childrenVms && parentVm._childrenVms.push(this)
-
-  if (!options && this._app.customComponentMap) {
-    options = this._app.customComponentMap[type]
-  }
-  options = options || {}
-
-  const data = options.data || {}
-
-  this._options = options
-  this._methods = options.methods || {}
-  this._computed = options.computed || {}
-  this._css = options.style || {}
-  this._ids = {}
-  this._vmEvents = {}
-  this._childrenVms = []
-  this._type = type
-
-  // bind events and lifecycles
-  initEvents(this, externalEvents)
-
-  console.debug(`[JS Framework] "init" lifecycle in Vm(${this._type})`)
-  this.$emit('hook:init')
-  this._inited = true
-
-  // proxy data and methods
-  // observe data and add this to vms
-  this._data = typeof data === 'function' ? data() : data
-  if (mergedData) {
-    extend(this._data, mergedData)
-  }
-  initState(this)
-
-  console.debug(`[JS Framework] "created" lifecycle in Vm(${this._type})`)
-  this.$emit('hook:created')
-  this._created = true
-
-  // backward old ready entry
-  if (options.methods && options.methods.ready) {
-    console.warn('"exports.methods.ready" is deprecated, ' +
-      'please use "exports.created" instead')
-    options.methods.ready.call(this)
-  }
-
-  if (!this._app.doc) {
-    return
-  }
-
-  // if no parentElement then specify the documentElement
-  this._parentEl = parentEl || this._app.doc.documentElement
-  build(this)
-}
-
-mixinEvents(Vm.prototype)
-
-/**
- * Watch an function and bind all the data appeared in it. When the related
- * data changes, the callback will be called with new value as 1st param.
- *
- * @param {Function} fn
- * @param {Function} callback
- */
-Vm.prototype.$watch = function (fn, callback) {
-  watch(this, fn, callback)
-}
-
-Vm.set = set
-Vm.delete = del

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/frameworks/vanilla/index.js
----------------------------------------------------------------------
diff --git a/html5/frameworks/vanilla/index.js b/html5/frameworks/vanilla/index.js
deleted file mode 100644
index f3b4bc2..0000000
--- a/html5/frameworks/vanilla/index.js
+++ /dev/null
@@ -1,110 +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.
- */
-const config = {}
-
-const instanceMap = {}
-
-export function init (cfg) {
-  config.Document = cfg.Document
-  config.Element = cfg.Element
-  config.Comment = cfg.Comment
-  config.sendTasks = cfg.sendTasks
-}
-
-export function createInstance (id, code, options = {}, data = {}, serviceObjects = {}) {
-  const document = new config.Document(id, options.bundleUrl)
-  const callbacks = {}
-  const instance = { id, data, document, callbacks }
-
-  let lastCallbackId = 0
-  document.addCallback = func => {
-    lastCallbackId++
-    callbacks[lastCallbackId] = func
-    return lastCallbackId
-  }
-  document.handleCallback = (funcId, data, ifLast) => {
-    const callback = callbacks[funcId]
-    if (ifLast) {
-      delete callbacks[funcId]
-    }
-    return callback(data)
-  }
-  instanceMap[id] = instance
-
-  const globalObjects = Object.assign({
-    Document: config.Document,
-    Element: config.Element,
-    Comment: config.Comment,
-    sendTasks: tasks => config.sendTasks(id, tasks, -1),
-    options,
-    document
-  }, serviceObjects)
-
-  const globalKeys = []
-  const globalValues = []
-  for (const key in globalObjects) {
-    globalKeys.push(key)
-    globalValues.push(globalObjects[key])
-  }
-  globalKeys.push(code)
-
-  const result = new Function(...globalKeys)
-  result(...globalValues)
-
-  config.sendTasks(id, [{ module: 'dom', method: 'createFinish', args: [] }], -1)
-
-  return instance
-}
-
-export function destroyInstance (id) {
-  delete instanceMap[id]
-}
-
-export function getRoot (id) {
-  return instanceMap[id].document.body.toJSON()
-}
-
-export function receiveTasks (id, tasks) {
-  const jsHandlers = {
-    fireEvent: (id, ref, type, data, domChanges) => {
-      const { document } = instanceMap[id]
-      const el = document.getRef(ref)
-      return document.fireEvent(el, type, data, domChanges)
-    },
-
-    callback: (id, funcId, data, ifLast) => {
-      const { document } = instanceMap[id]
-      return document.handleCallback(funcId, data, ifLast)
-    }
-  }
-
-  const { document } = instanceMap[id] || {}
-  if (document && Array.isArray(tasks)) {
-    const results = []
-    tasks.forEach((task) => {
-      const handler = jsHandlers[task.method]
-      const args = [...task.args]
-      if (typeof handler === 'function') {
-        args.unshift(id)
-        results.push(handler(...args))
-      }
-    })
-    return results
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/render/native/index.js
----------------------------------------------------------------------
diff --git a/html5/render/native/index.js b/html5/render/native/index.js
deleted file mode 100644
index c692995..0000000
--- a/html5/render/native/index.js
+++ /dev/null
@@ -1,22 +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 setup from './setup'
-import frameworks from '../../frameworks/index'
-
-setup(frameworks)



[17/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/runtime/vdom/operation.js
----------------------------------------------------------------------
diff --git a/html5/runtime/vdom/operation.js b/html5/runtime/vdom/operation.js
deleted file mode 100644
index 69a64e1..0000000
--- a/html5/runtime/vdom/operation.js
+++ /dev/null
@@ -1,272 +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.
- */
-
-const docMap = {}
-
-/**
- * Add a document object into docMap.
- * @param {string} id
- * @param {object} document
- */
-export function addDoc (id, doc) {
-  if (id) {
-    docMap[id] = doc
-  }
-}
-
-/**
- * Get the document object by id.
- * @param {string} id
- */
-export function getDoc (id) {
-  return docMap[id]
-}
-
-/**
- * Remove the document from docMap by id.
- * @param {string} id
- */
-export function removeDoc (id) {
-  delete docMap[id]
-}
-
-/**
- * @deprecated
- * Get listener by document id.
- * @param {string} id
- * @return {object} listener
- */
-export function getListener (id) {
-  const doc = docMap[id]
-  if (doc && doc.listener) {
-    return doc.listener
-  }
-  return null
-}
-
-/**
- * Get TaskCenter instance by id.
- * @param {string} id
- * @return {object} TaskCenter
- */
-export function getTaskCenter (id) {
-  const doc = docMap[id]
-  if (doc && doc.taskCenter) {
-    return doc.taskCenter
-  }
-  return null
-}
-
-/**
- * Append body node to documentElement.
- * @param {object} document
- * @param {object} node
- * @param {object} before
- */
-export function appendBody (doc, node, before) {
-  const { documentElement } = doc
-
-  if (documentElement.pureChildren.length > 0 || node.parentNode) {
-    return
-  }
-  const children = documentElement.children
-  const beforeIndex = children.indexOf(before)
-  if (beforeIndex < 0) {
-    children.push(node)
-  }
-  else {
-    children.splice(beforeIndex, 0, node)
-  }
-
-  if (node.nodeType === 1) {
-    if (node.role === 'body') {
-      node.docId = doc.id
-      node.ownerDocument = doc
-      node.parentNode = documentElement
-      linkParent(node, documentElement)
-    }
-    else {
-      node.children.forEach(child => {
-        child.parentNode = node
-      })
-      setBody(doc, node)
-      node.docId = doc.id
-      node.ownerDocument = doc
-      linkParent(node, documentElement)
-      delete doc.nodeMap[node.nodeId]
-    }
-    documentElement.pureChildren.push(node)
-    sendBody(doc, node)
-  }
-  else {
-    node.parentNode = documentElement
-    doc.nodeMap[node.ref] = node
-  }
-}
-
-function sendBody (doc, node) {
-  const body = node.toJSON()
-  if (doc && doc.taskCenter && typeof doc.taskCenter.send === 'function') {
-    doc.taskCenter.send('dom', { action: 'createBody' }, [body])
-  }
-}
-
-/**
- * Set up body node.
- * @param {object} document
- * @param {object} element
- */
-export function setBody (doc, el) {
-  el.role = 'body'
-  el.depth = 1
-  delete doc.nodeMap[el.nodeId]
-  el.ref = '_root'
-  doc.nodeMap._root = el
-  doc.body = el
-}
-
-/**
- * Establish the connection between parent and child node.
- * @param {object} child node
- * @param {object} parent node
- */
-export function linkParent (node, parent) {
-  node.parentNode = parent
-  if (parent.docId) {
-    node.docId = parent.docId
-    node.ownerDocument = parent.ownerDocument
-    node.ownerDocument.nodeMap[node.nodeId] = node
-    node.depth = parent.depth + 1
-  }
-  node.children.forEach(child => {
-    linkParent(child, node)
-  })
-}
-
-/**
- * Get the next sibling element.
- * @param {object} node
- */
-export function nextElement (node) {
-  while (node) {
-    if (node.nodeType === 1) {
-      return node
-    }
-    node = node.nextSibling
-  }
-}
-
-/**
- * Get the previous sibling element.
- * @param {object} node
- */
-export function previousElement (node) {
-  while (node) {
-    if (node.nodeType === 1) {
-      return node
-    }
-    node = node.previousSibling
-  }
-}
-
-/**
- * Insert a node into list at the specified index.
- * @param {object} target node
- * @param {array} list
- * @param {number} newIndex
- * @param {boolean} changeSibling
- * @return {number} newIndex
- */
-export function insertIndex (target, list, newIndex, changeSibling) {
-  /* istanbul ignore next */
-  if (newIndex < 0) {
-    newIndex = 0
-  }
-  const before = list[newIndex - 1]
-  const after = list[newIndex]
-  list.splice(newIndex, 0, target)
-  if (changeSibling) {
-    before && (before.nextSibling = target)
-    target.previousSibling = before
-    target.nextSibling = after
-    after && (after.previousSibling = target)
-  }
-  return newIndex
-}
-
-/**
- * Move the node to a new index in list.
- * @param {object} target node
- * @param {array} list
- * @param {number} newIndex
- * @param {boolean} changeSibling
- * @return {number} newIndex
- */
-export function moveIndex (target, list, newIndex, changeSibling) {
-  const index = list.indexOf(target)
-  /* istanbul ignore next */
-  if (index < 0) {
-    return -1
-  }
-  if (changeSibling) {
-    const before = list[index - 1]
-    const after = list[index + 1]
-    before && (before.nextSibling = after)
-    after && (after.previousSibling = before)
-  }
-  list.splice(index, 1)
-  let newIndexAfter = newIndex
-  if (index <= newIndex) {
-    newIndexAfter = newIndex - 1
-  }
-  const beforeNew = list[newIndexAfter - 1]
-  const afterNew = list[newIndexAfter]
-  list.splice(newIndexAfter, 0, target)
-  if (changeSibling) {
-    beforeNew && (beforeNew.nextSibling = target)
-    target.previousSibling = beforeNew
-    target.nextSibling = afterNew
-    afterNew && (afterNew.previousSibling = target)
-  }
-  if (index === newIndexAfter) {
-    return -1
-  }
-  return newIndex
-}
-
-/**
- * Remove the node from list.
- * @param {object} target node
- * @param {array} list
- * @param {boolean} changeSibling
- */
-export function removeIndex (target, list, changeSibling) {
-  const index = list.indexOf(target)
-  /* istanbul ignore next */
-  if (index < 0) {
-    return
-  }
-  if (changeSibling) {
-    const before = list[index - 1]
-    const after = list[index + 1]
-    before && (before.nextSibling = after)
-    after && (after.previousSibling = before)
-  }
-  list.splice(index, 1)
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/services/amd/index.js
----------------------------------------------------------------------
diff --git a/html5/services/amd/index.js b/html5/services/amd/index.js
deleted file mode 100644
index 3a7a491..0000000
--- a/html5/services/amd/index.js
+++ /dev/null
@@ -1,105 +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
- * A simple implementation of AMD for weex.
- */
-
-/**
- * amd modules (<id, module> pair)
- *  - id : instance id.
- *  - module: {
- *        name: module name
- *        , factory: module's factory function
- *        , cached: cached module,
- *        , deps: module's dependencies, always be [] under most circumstances.
- *      }
- */
-const modules = {}
-
-const amdService = {
-
-  // create a amd service.
-  create: (id, env, config) => {
-    if (!env.framework.match(/Vue/i)) {
-      return
-    }
-
-    const mod = {}
-    modules[id] = mod
-    const amdObject = {
-
-      /**
-       * define a module.
-       * @param  {String} name: module name.
-       * @param  {Array} deps: dependencies. Always be empty array.
-       * @param  {function} factory: factory function.
-       */
-      define (name, deps, factory) {
-        if (mod[name]) {
-          console.warn(`[amdService] already defined module: '${name}'`)
-        }
-        if (typeof deps === 'function') {
-          factory = deps
-          deps = []
-        }
-        mod[name] = { name, factory, cached: false, deps }
-      },
-
-      /**
-       * require a module.
-       * @param  {string} name - module name.
-       */
-      require (name) {
-        const servMod = mod[name]
-        if (!servMod) {
-          return console.warn(`[amdService] module '${name}' is not defined.`)
-        }
-        if (servMod.cached) {
-          return servMod.cached
-        }
-        const exports = {}
-        const module = { exports }
-        const { deps } = servMod
-        let ret
-        if (deps && deps.length >= 1) {
-          /**
-           * to support:
-           *   define(name, ['foo', 'bar'], function (foo, bar) { ... })
-           */
-          const args = deps.map(depName => require(depName))
-          ret = servMod.factory(...args)
-        }
-        else {
-          ret = servMod.factory(amdObject.require, exports, module)
-        }
-        servMod.cached = ret || module.exports
-        return servMod.cached
-      }
-    }
-    return { instance: amdObject }
-  },
-
-  destroy: (id, env) => {
-    delete modules[id]
-  }
-}
-
-export default amdService

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/services/broadcast-channel/index.js
----------------------------------------------------------------------
diff --git a/html5/services/broadcast-channel/index.js b/html5/services/broadcast-channel/index.js
deleted file mode 100644
index cc7524e..0000000
--- a/html5/services/broadcast-channel/index.js
+++ /dev/null
@@ -1,125 +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
- * The polyfill of BroadcastChannel API.
- * This api can be used to achieve inter-instance communications.
- *
- * https://html.spec.whatwg.org/multipage/comms.html#broadcasting-to-other-browsing-contexts
- */
-
-import { MessageEvent } from './message-event'
-
-const channels = {}
-const instances = {}
-
-/**
- * An empty constructor for BroadcastChannel polyfill.
- * The real constructor will be defined when a Weex instance created because
- * we need to track the channel by Weex instance id.
- */
-function BroadcastChannel () {}
-
-/**
- * Sends the given message to other BroadcastChannel objects set up for this channel.
- * @param {any} message
- */
-BroadcastChannel.prototype.postMessage = function (message) {
-  if (this._closed) {
-    throw new Error(`BroadcastChannel "${this.name}" is closed.`)
-  }
-
-  const subscribers = channels[this.name]
-  if (subscribers && subscribers.length) {
-    for (let i = 0; i < subscribers.length; ++i) {
-      const member = subscribers[i]
-
-      if (member._closed || member === this) continue
-
-      if (typeof member.onmessage === 'function') {
-        member.onmessage(new MessageEvent('message', { data: message }))
-      }
-    }
-  }
-}
-
-/**
- * Closes the BroadcastChannel object, opening it up to garbage collection.
- */
-BroadcastChannel.prototype.close = function () {
-  if (this._closed) {
-    return
-  }
-
-  this._closed = true
-
-  // remove itself from channels.
-  if (channels[this.name]) {
-    const subscribers = channels[this.name].filter(x => x !== this)
-    if (subscribers.length) {
-      channels[this.name] = subscribers
-    }
-    else {
-      delete channels[this.name]
-    }
-  }
-}
-
-export default {
-  create: (id, env, config) => {
-    instances[id] = []
-    if (typeof global.BroadcastChannel === 'function') {
-      return {}
-    }
-    const serviceObject = {
-      /**
-       * Returns a new BroadcastChannel object via which messages for the given
-       * channel name can be sent and received.
-       * @param {string} name
-       */
-      BroadcastChannel: function (name) {
-        // the name property is readonly
-        Object.defineProperty(this, 'name', {
-          configurable: false,
-          enumerable: true,
-          writable: false,
-          value: String(name)
-        })
-
-        this._closed = false
-        this.onmessage = null
-
-        if (!channels[this.name]) {
-          channels[this.name] = []
-        }
-        channels[this.name].push(this)
-        instances[id].push(this)
-      }
-    }
-    serviceObject.BroadcastChannel.prototype = BroadcastChannel.prototype
-    return {
-      instance: serviceObject
-    }
-  },
-  destroy: (id, env) => {
-    instances[id].forEach(channel => channel.close())
-    delete instances[id]
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/services/broadcast-channel/message-event.js
----------------------------------------------------------------------
diff --git a/html5/services/broadcast-channel/message-event.js b/html5/services/broadcast-channel/message-event.js
deleted file mode 100644
index 00fa36a..0000000
--- a/html5/services/broadcast-channel/message-event.js
+++ /dev/null
@@ -1,40 +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.
- */
-
-/**
- * Mock MessageEvent type
- * @param {string} type
- * @param {object} dict { data, origin, source, ports }
- *
- * This type has been simplified.
- * https://html.spec.whatwg.org/multipage/comms.html#messageevent
- * https://dom.spec.whatwg.org/#interface-event
- */
-export function MessageEvent (type, dict = {}) {
-  this.type = type || 'message'
-
-  this.data = dict.data || null
-  this.origin = dict.origin || ''
-  this.source = dict.source || null
-  this.ports = dict.ports || []
-
-  // inherit properties
-  this.target = null
-  this.timeStamp = Date.now()
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/services/index.js
----------------------------------------------------------------------
diff --git a/html5/services/index.js b/html5/services/index.js
deleted file mode 100644
index 52cd97d..0000000
--- a/html5/services/index.js
+++ /dev/null
@@ -1,23 +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 BroadcastChannel from './broadcast-channel/index'
-
-export default {
-  BroadcastChannel
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/shared/arrayFrom.js
----------------------------------------------------------------------
diff --git a/html5/shared/arrayFrom.js b/html5/shared/arrayFrom.js
deleted file mode 100644
index af5e78c..0000000
--- a/html5/shared/arrayFrom.js
+++ /dev/null
@@ -1,105 +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.
- */
-
-/* eslint-disable */
-
-// Production steps of ECMA-262, Edition 6, 22.1.2.1
-// Reference: https://people.mozilla.org/~jorendorff/es6-draft.html#sec-array.from
-
-/* istanbul ignore if */
-if (!Array.from) {
-  Array.from = (function() {
-    var toStr = Object.prototype.toString;
-    var isCallable = function(fn) {
-      return typeof fn === 'function' || toStr.call(fn) === '[object Function]';
-    };
-    var toInteger = function(value) {
-      var number = Number(value);
-      if (isNaN(number)) {
-        return 0;
-      }
-      if (number === 0 || !isFinite(number)) {
-        return number;
-      }
-      return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
-    };
-    var maxSafeInteger = Math.pow(2, 53) - 1;
-    var toLength = function(value) {
-      var len = toInteger(value);
-      return Math.min(Math.max(len, 0), maxSafeInteger);
-    };
-
-    // The length property of the from method is 1.
-    return function from(arrayLike/*, mapFn, thisArg */) {
-      // 1. Let C be the this value.
-      var C = this;
-
-      // 2. Let items be ToObject(arrayLike).
-      var items = Object(arrayLike);
-
-      // 3. ReturnIfAbrupt(items).
-      if (arrayLike == null) {
-        throw new TypeError('Array.from requires an array-like object - not null or undefined');
-      }
-
-      // 4. If mapfn is undefined, then let mapping be false.
-      var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
-      var T;
-      if (typeof mapFn !== 'undefined') {
-        // 5. else
-        // 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
-        if (!isCallable(mapFn)) {
-          throw new TypeError('Array.from: when provided, the second argument must be a function');
-        }
-
-        // 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
-        if (arguments.length > 2) {
-          T = arguments[2];
-        }
-      }
-
-      // 10. Let lenValue be Get(items, "length").
-      // 11. Let len be ToLength(lenValue).
-      var len = toLength(items.length);
-
-      // 13. If IsConstructor(C) is true, then
-      // 13. a. Let A be the result of calling the [[Construct]] internal method of C with an argument list containing the single item len.
-      // 14. a. Else, Let A be ArrayCreate(len).
-      var A = isCallable(C) ? Object(new C(len)) : new Array(len);
-
-      // 16. Let k be 0.
-      var k = 0;
-      // 17. Repeat, while k < len… (also steps a - h)
-      var kValue;
-      while (k < len) {
-        kValue = items[k];
-        if (mapFn) {
-          A[k] = typeof T === 'undefined' ? mapFn(kValue, k) : mapFn.call(T, kValue, k);
-        } else {
-          A[k] = kValue;
-        }
-        k += 1;
-      }
-      // 18. Let putStatus be Put(A, "length", len, true).
-      A.length = len;
-      // 20. Return A.
-      return A;
-    };
-  }());
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/shared/console.js
----------------------------------------------------------------------
diff --git a/html5/shared/console.js b/html5/shared/console.js
deleted file mode 100644
index eb54815..0000000
--- a/html5/shared/console.js
+++ /dev/null
@@ -1,137 +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
- * This file will hack `console` methods by `WXEnvironment.logLevel`.
- * So we can control how many and which messages will be sent by change the log level.
- * Additionally in native platform the message content must be primitive values and
- * using `nativeLog(...args, logLevelMark)` so we create a new `console` object in
- * global add a format process for its methods.
- */
-
-const LEVELS = ['off', 'error', 'warn', 'info', 'log', 'debug']
-let levelMap = {}
-
-const originalConsole = global.console
-
-/**
- * Hack console for native environment.
- */
-export function setNativeConsole () {
-  generateLevelMap()
-
-  /* istanbul ignore next */
-  // mock console in native environment
-  if (global.WXEnvironment && global.WXEnvironment.platform !== 'Web') {
-    global.console = {
-      debug: (...args) => {
-        if (checkLevel('debug')) { global.nativeLog(...format(args), '__DEBUG') }
-      },
-      log: (...args) => {
-        if (checkLevel('log')) { global.nativeLog(...format(args), '__LOG') }
-      },
-      info: (...args) => {
-        if (checkLevel('info')) { global.nativeLog(...format(args), '__INFO') }
-      },
-      warn: (...args) => {
-        if (checkLevel('warn')) { global.nativeLog(...format(args), '__WARN') }
-      },
-      error: (...args) => {
-        if (checkLevel('error')) { global.nativeLog(...format(args), '__ERROR') }
-      }
-    }
-  }
-
-  // Web or Node
-  else {
-    const { debug, log, info, warn, error } = console
-    console.__ori__ = { debug, log, info, warn, error }
-    console.debug = (...args) => {
-      if (checkLevel('debug')) { console.__ori__.debug.apply(console, args) }
-    }
-    console.log = (...args) => {
-      if (checkLevel('log')) { console.__ori__.log.apply(console, args) }
-    }
-    console.info = (...args) => {
-      if (checkLevel('info')) { console.__ori__.info.apply(console, args) }
-    }
-    console.warn = (...args) => {
-      if (checkLevel('warn')) { console.__ori__.warn.apply(console, args) }
-    }
-    console.error = (...args) => {
-      if (checkLevel('error')) { console.__ori__.error.apply(console, args) }
-    }
-  }
-}
-
-/**
- * Reset hacked console to original.
- */
-/* istanbul ignore next */
-export function resetNativeConsole () {
-  levelMap = {}
-  global.console = originalConsole
-}
-
-/**
- * Generate map for which types of message will be sent in a certain message level
- * as the order of LEVELS.
- */
-function generateLevelMap () {
-  LEVELS.forEach(level => {
-    const levelIndex = LEVELS.indexOf(level)
-    levelMap[level] = {}
-    LEVELS.forEach(type => {
-      const typeIndex = LEVELS.indexOf(type)
-      if (typeIndex <= levelIndex) {
-        levelMap[level][type] = true
-      }
-    })
-  })
-}
-
-/**
- * Check if a certain type of message will be sent in current log level of env.
- * @param  {string} type
- * @return {boolean}
- */
-function checkLevel (type) {
-  const logLevel = (global.WXEnvironment && global.WXEnvironment.logLevel) || 'log'
-  return levelMap[logLevel] && levelMap[logLevel][type]
-}
-
-/**
- * Convert all log arguments into primitive values.
- * @param  {array} args
- * @return {array}
- */
-/* istanbul ignore next */
-function format (args) {
-  return args.map((v) => {
-    const type = Object.prototype.toString.call(v)
-    if (type.toLowerCase() === '[object object]') {
-      v = JSON.stringify(v)
-    }
-    else {
-      v = String(v)
-    }
-    return v
-  })
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/shared/freeze.js
----------------------------------------------------------------------
diff --git a/html5/shared/freeze.js b/html5/shared/freeze.js
deleted file mode 100644
index f997a5f..0000000
--- a/html5/shared/freeze.js
+++ /dev/null
@@ -1,90 +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.
- */
-/**
- * Freeze the prototype of javascript build-in objects.
- */
-/* istanbul ignore next */
-export function freezePrototype () {
-  Object.freeze(Object)
-  Object.freeze(Array)
-
-  // Object.freeze(Object.prototype)
-  freezeObjectProto()
-  Object.freeze(Array.prototype)
-  Object.freeze(String.prototype)
-  Object.freeze(Number.prototype)
-  Object.freeze(Boolean.prototype)
-
-  // Object.freeze(Error.prototype)
-  freezeErrorProto()
-  Object.freeze(Date.prototype)
-  Object.freeze(RegExp.prototype)
-}
-
-function freezeObjectProto () {
-  const proto = Object.prototype
-  const protoName = 'Object.prototype'
-  freezeProtoProperty(proto, '__defineGetter__', protoName)
-  freezeProtoProperty(proto, '__defineSetter__', protoName)
-  freezeProtoProperty(proto, '__lookupGetter__', protoName)
-  freezeProtoProperty(proto, '__lookupSetter__', protoName)
-  freezeProtoProperty(proto, 'constructor', protoName)
-  freezeProtoProperty(proto, 'hasOwnProperty', protoName)
-  freezeProtoProperty(proto, 'isPrototypeOf', protoName)
-  freezeProtoProperty(proto, 'propertyIsEnumerable', protoName)
-  freezeProtoProperty(proto, 'toLocaleString', protoName)
-  freezeProtoProperty(proto, 'toString', protoName)
-  freezeProtoProperty(proto, 'valueOf', protoName)
-  Object.seal(proto)
-}
-
-function freezeErrorProto () {
-  const proto = Error.prototype
-  const protoName = 'Error.prototype'
-  freezeProtoProperty(proto, 'name', protoName)
-  freezeProtoProperty(proto, 'message', protoName)
-  freezeProtoProperty(proto, 'toString', protoName)
-  freezeProtoProperty(proto, 'constructor', protoName)
-  Object.seal(proto)
-}
-
-function freezeProtoProperty (proto, propertyName, protoName) {
-  if (!proto.hasOwnProperty(propertyName)) {
-    return
-  }
-
-  const origin = proto[propertyName]
-  Object.defineProperty(proto, propertyName, {
-    get: function () {
-      return origin
-    },
-    set: function (value) {
-      if (this === proto) {
-        throw Error(`Cannot assign to read only property ${propertyName} of ${protoName}`)
-      }
-
-      Object.defineProperty(this, propertyName, {
-        value: value,
-        writable: true
-      })
-
-      return value
-    }
-  })
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/shared/index.js
----------------------------------------------------------------------
diff --git a/html5/shared/index.js b/html5/shared/index.js
deleted file mode 100644
index 27b95c3..0000000
--- a/html5/shared/index.js
+++ /dev/null
@@ -1,32 +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 './arrayFrom'
-import './objectAssign'
-import './objectSetPrototypeOf'
-
-// import promise hack and polyfills
-import './promise'
-import 'core-js/modules/es6.object.to-string'
-import 'core-js/modules/es6.string.iterator'
-import 'core-js/modules/web.dom.iterable'
-import 'core-js/modules/es6.promise'
-
-export * from './console'
-export * from './setTimeout'
-export * from './freeze'

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/shared/objectAssign.js
----------------------------------------------------------------------
diff --git a/html5/shared/objectAssign.js b/html5/shared/objectAssign.js
deleted file mode 100644
index 7aa5993..0000000
--- a/html5/shared/objectAssign.js
+++ /dev/null
@@ -1,19 +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 'core-js/fn/object/assign'

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/shared/objectSetPrototypeOf.js
----------------------------------------------------------------------
diff --git a/html5/shared/objectSetPrototypeOf.js b/html5/shared/objectSetPrototypeOf.js
deleted file mode 100644
index 6364ce4..0000000
--- a/html5/shared/objectSetPrototypeOf.js
+++ /dev/null
@@ -1,74 +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.
- */
-
-/* eslint-disable */
-
-// https://gist.github.com/WebReflection/5593554
-
-/* istanbul ignore if */
-if (!Object.setPrototypeOf) {
-  Object.setPrototypeOf = (function(Object, magic) {
-    var set;
-    function setPrototypeOf(O, proto) {
-      set.call(O, proto);
-      return O;
-    }
-    try {
-      // this works already in Firefox and Safari
-      set = Object.getOwnPropertyDescriptor(Object.prototype, magic).set;
-      set.call({}, null);
-    } catch (e) {
-      if (
-        // IE < 11 cannot be shimmed
-        Object.prototype !== {}[magic] ||
-        // neither can any browser that actually
-        // implemented __proto__ correctly
-        // (all but old V8 will return here)
-        {__proto__: null}.__proto__ === void 0
-        // this case means null objects cannot be passed
-        // through setPrototypeOf in a reliable way
-        // which means here a **Sham** is needed instead
-      ) {
-        return;
-      }
-      // nodejs 0.8 and 0.10 are (buggy and..) fine here
-      // probably Chrome or some old Mobile stock browser
-      set = function(proto) {
-        this[magic] = proto;
-      };
-      // please note that this will **not** work
-      // in those browsers that do not inherit
-      // __proto__ by mistake from Object.prototype
-      // in these cases we should probably throw an error
-      // or at least be informed about the issue
-      setPrototypeOf.polyfill = setPrototypeOf(
-        setPrototypeOf({}, null),
-        Object.prototype
-      ) instanceof Object;
-      // setPrototypeOf.polyfill === true means it works as meant
-      // setPrototypeOf.polyfill === false means it's not 100% reliable
-      // setPrototypeOf.polyfill === undefined
-      // or
-      // setPrototypeOf.polyfill ==  null means it's not a polyfill
-      // which means it works as expected
-      // we can even delete Object.prototype.__proto__;
-    }
-    return setPrototypeOf;
-  }(Object, '__proto__'));
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/shared/promise.js
----------------------------------------------------------------------
diff --git a/html5/shared/promise.js b/html5/shared/promise.js
deleted file mode 100644
index 39fe816..0000000
--- a/html5/shared/promise.js
+++ /dev/null
@@ -1,28 +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.
- */
-
-// fix Promise Problem on JSContext of iOS7~8
-// @see https://bugs.webkit.org/show_bug.cgi?id=135866
-
-const { WXEnvironment } = global
-
-/* istanbul ignore next */
-if (WXEnvironment && WXEnvironment.platform === 'iOS') {
-  global.Promise = undefined
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/shared/setTimeout.js
----------------------------------------------------------------------
diff --git a/html5/shared/setTimeout.js b/html5/shared/setTimeout.js
deleted file mode 100644
index d545d6d..0000000
--- a/html5/shared/setTimeout.js
+++ /dev/null
@@ -1,63 +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
- * Polyfill `setTimeout` on Android V8 using native method
- * `setTimeoutNative(callbackId, time)` and JS method
- * `setTimeoutCallback(callbackId)`.
- * This polyfill is only used in virtual-DOM diff & flush agorithm. Not
- * accessed by JS Bundle code (The timer APIs polyfill for JS Bundle is in
- * `html5/default/app/ctrl.js`).
- */
-
-const originalSetTimeout = global.setTimeout
-const setTimeoutNative = global.setTimeoutNative
-
-/**
- * Set up native timer
- */
-/* istanbul ignore next */
-export function setNativeTimer () {
-  if (typeof setTimeout === 'undefined' &&
-  typeof setTimeoutNative === 'function') {
-    const timeoutMap = {}
-    let timeoutId = 0
-
-    global.setTimeout = (cb, time) => {
-      timeoutMap[++timeoutId] = cb
-      setTimeoutNative(timeoutId.toString(), time)
-    }
-
-    global.setTimeoutCallback = (id) => {
-      if (typeof timeoutMap[id] === 'function') {
-        timeoutMap[id]()
-        delete timeoutMap[id]
-      }
-    }
-  }
-}
-
-/* istanbul ignore next */
-export function resetNativeTimer () {
-  global.setTimeout = originalSetTimeout
-  global.setTimeoutCallback = null
-}
-
-setNativeTimer()

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/.eslintrc
----------------------------------------------------------------------
diff --git a/html5/test/case/.eslintrc b/html5/test/case/.eslintrc
deleted file mode 100644
index c892a87..0000000
--- a/html5/test/case/.eslintrc
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  "globals": {
-    "callNativeHandler": true,
-  },
-
-  "rules": {
-    "no-eval": "off"
-  }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/append-root.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/append-root.output.js b/html5/test/case/basic/append-root.output.js
deleted file mode 100644
index 3c4a5a4..0000000
--- a/html5/test/case/basic/append-root.output.js
+++ /dev/null
@@ -1,36 +0,0 @@
-{
-  type: 'container',
-  style: {
-    flexDirection: 'column'
-  },
-  attr: {
-    append: "tree"
-  },
-  children: [{
-    type: 'container',
-    children: [{
-      type: 'image',
-      attr: {
-        src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
-      }
-    }, {
-      type: 'text',
-      attr: {
-        value: 'title1'
-      }
-    }]
-  }, {
-    type: 'container',
-    children: [{
-      type: 'image',
-      attr: {
-        src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
-      }
-    }, {
-      type: 'text',
-      attr: {
-        value: 'title2'
-      }
-    }]
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/append-root.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/append-root.source.js b/html5/test/case/basic/append-root.source.js
deleted file mode 100644
index dfe60d7..0000000
--- a/html5/test/case/basic/append-root.source.js
+++ /dev/null
@@ -1,75 +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.
- */
-define('@weex-component/append', function(require, exports, module) {;
-  module.exports = {
-    data: function() {
-      return {
-        itemList: [{
-          itemId: '520421163634',
-          title: 'title1',
-          pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
-        }, {
-          itemId: '522076777462',
-          title: 'title2',
-          pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
-        }]
-      }
-    }
-  }
-
-
-  ;
-  module.exports.style = {}
-
-  ;
-  module.exports.template = {
-    "type": "container",
-    "style": {
-      "flexDirection": "column"
-    },
-    "append": "tree",
-    "children": [{
-      "type": "container",
-      "children": [{
-        "type": "image",
-        "attr": {
-          "src": function() {
-            return this.pictureUrl
-          }
-        }
-      }, {
-        "type": "text",
-        "attr": {
-          "value": function() {
-            return this.title
-          }
-        }
-      }],
-      "repeat": function() {
-        return this.itemList
-      }
-    }]
-  }
-
-  ;
-})
-
-// require module
-
-bootstrap('@weex-component/append')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/append.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/append.output.js b/html5/test/case/basic/append.output.js
deleted file mode 100644
index 5259fb6..0000000
--- a/html5/test/case/basic/append.output.js
+++ /dev/null
@@ -1,79 +0,0 @@
-{
-  type: 'container',
-  style: {
-    flexDirection: 'column'
-  },
-  children: [{
-    type: 'container',
-    event: ['click'],
-    attr: {
-      append: 'tree'
-    },
-    children: [{
-      type: 'container',
-      children: [{
-        type: 'text',
-        attr: {
-          value: 'any text'
-        }
-      }]
-    }, {
-      type: 'image',
-      attr: {
-        src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
-      },
-      style: {
-        width: 200,
-        height: 200
-      }
-    }, {
-      type: 'text',
-      attr: {
-        value: 'title1'
-      },
-      style: {
-        flex: 1,
-        color: '#ff0000',
-        fontSize: 48,
-        fontWeight: 'bold',
-        backgroundColor: '#eeeeee'
-      }
-    }]
-  }, {
-    type: 'container',
-    event: ['click'],
-    attr: {
-      append: 'tree'
-    },
-    children: [{
-      type: 'container',
-      children: [{
-        type: 'text',
-        attr: {
-          value: 'any text'
-        }
-      }]
-    }, {
-      type: 'image',
-      attr: {
-        src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
-      },
-      style: {
-        width: 200,
-        height: 200
-      }
-    }, {
-      type: 'text',
-      attr: {
-        value: 'title2'
-      },
-      style: {
-        flex: 1,
-        color: '#ff0000',
-        fontSize: 48,
-        fontWeight: 'bold',
-        backgroundColor: '#eeeeee'
-      }
-    }]
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/append.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/append.source.js b/html5/test/case/basic/append.source.js
deleted file mode 100644
index c216578..0000000
--- a/html5/test/case/basic/append.source.js
+++ /dev/null
@@ -1,123 +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.
- */
-define('@weex-component/taobao-item', function (require, exports, module) {
-
-;
-    module.exports = {
-      data: function () {return {
-        itemId: '',
-        title: '',
-        pictureUrl: ''
-      }},
-      methods: {
-        gotoDetail: function () {
-          this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId)
-        }
-      }
-    }
-  
-
-;module.exports.style = {
-  "thumb": {
-    "width": 200,
-    "height": 200
-  },
-  "title": {
-    "flex": 1,
-    "color": "#ff0000",
-    "fontSize": 48,
-    "fontWeight": "bold",
-    "backgroundColor": "#eeeeee"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "events": {
-    "click": "gotoDetail"
-  },
-  "append": "tree",
-  "children": [
-    {
-      "type": "container",
-      "children": [{
-        "type": "text",
-        "attr": {
-          "value": "any text"
-        }
-      }]
-    },
-    {
-      "type": "image",
-      "classList": [
-        "thumb"
-      ],
-      "attr": {
-        "src": function () {return this.pictureUrl}
-      }
-    },
-    {
-      "type": "text",
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": function () {return this.title}
-      }
-    }
-  ]
-}
-
-;})
-
-// module
-
-define('@weex-component/append', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      itemList: [
-        {itemId: '520421163634', title: 'title1', pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'},
-        {itemId: '522076777462', title: 'title2', pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'}
-      ]
-    }}
-  }
-
-
-;module.exports.style = {}
-
-;module.exports.template = {
-  "type": "container",
-  "style": {
-    "flexDirection": "column"
-  },
-  "children": [
-    {
-      "type": "taobao-item",
-      "repeat": function () {return this.itemList}
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/append')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/backward1.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/backward1.output.js b/html5/test/case/basic/backward1.output.js
deleted file mode 100644
index 8f6d086..0000000
--- a/html5/test/case/basic/backward1.output.js
+++ /dev/null
@@ -1,57 +0,0 @@
-{
-  type: 'container',
-  style: {
-    flexDirection: 'column'
-  },
-  children: [{
-    type: 'container',
-    event: ['click'],
-    children: [{
-      type: 'image',
-      attr: {
-        src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
-      },
-      style: {
-        width: 200,
-        height: 200
-      }
-    }, {
-      type: 'text',
-      attr: {
-        value: 'title1'
-      },
-      style: {
-        flex: 1,
-        color: '#ff0000',
-        fontSize: 48,
-        fontWeight: 'bold',
-        backgroundColor: '#eeeeee'
-      }
-    }]
-  }, {
-    type: 'container',
-    event: ['click'],
-    children: [{
-      type: 'image',
-      attr: {
-        src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
-      },
-      style: {
-        width: 200,
-        height: 200
-      }
-    }, {
-      type: 'text',
-      attr: {
-        value: 'title2'
-      },
-      style: {
-        flex: 1,
-        color: '#ff0000',
-        fontSize: 48,
-        fontWeight: 'bold',
-        backgroundColor: '#eeeeee'
-      }
-    }]
-  }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/backward1.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/backward1.source.js b/html5/test/case/basic/backward1.source.js
deleted file mode 100644
index 1bf6d74..0000000
--- a/html5/test/case/basic/backward1.source.js
+++ /dev/null
@@ -1,113 +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.
- */
-register('taobao-item', {
-
-    data: function() {
-        return {
-            itemId: '',
-            title: '',
-            pictureUrl: ''
-        };
-    },
-    methods: {
-        gotoDetail: function() {
-            this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId);
-        }
-    }
-
-    ,
-    style: {
-        "thumb": {
-            "width": 200,
-            "height": 200
-        },
-        "title": {
-            "flex": 1,
-            "color": "#ff0000",
-            "fontSize": 48,
-            "fontWeight": "bold",
-            "backgroundColor": "#eeeeee"
-        }
-    },
-    template: {
-        "type": "container",
-        "events": {
-            "click": "gotoDetail"
-        },
-        "children": [{
-            "type": "image",
-            "classList": [
-                "thumb"
-            ],
-            "attr": {
-                "src": function() {
-                    return this.pictureUrl
-                }
-            }
-        }, {
-            "type": "text",
-            "classList": [
-                "title"
-            ],
-            "attr": {
-                "value": function() {
-                    return this.title
-                }
-            }
-        }]
-    }
-})
-
-// module
-
-register('backward1', {
-
-    data: function() {
-        return {
-            itemList: [{
-                itemId: '520421163634',
-                title: 'title1',
-                pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
-            }, {
-                itemId: '522076777462',
-                title: 'title2',
-                pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
-            }]
-        };
-    }
-
-    ,
-    style: {},
-    template: {
-        "type": "container",
-        "style": {
-            "flexDirection": "column"
-        },
-        "children": [{
-            "type": "taobao-item",
-            "repeat": function() {
-                return this.itemList
-            }
-        }]
-    }
-})
-
-// require module
-
-render('backward1', {})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/backward2.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/backward2.output.js b/html5/test/case/basic/backward2.output.js
deleted file mode 100644
index 8f6d086..0000000
--- a/html5/test/case/basic/backward2.output.js
+++ /dev/null
@@ -1,57 +0,0 @@
-{
-  type: 'container',
-  style: {
-    flexDirection: 'column'
-  },
-  children: [{
-    type: 'container',
-    event: ['click'],
-    children: [{
-      type: 'image',
-      attr: {
-        src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
-      },
-      style: {
-        width: 200,
-        height: 200
-      }
-    }, {
-      type: 'text',
-      attr: {
-        value: 'title1'
-      },
-      style: {
-        flex: 1,
-        color: '#ff0000',
-        fontSize: 48,
-        fontWeight: 'bold',
-        backgroundColor: '#eeeeee'
-      }
-    }]
-  }, {
-    type: 'container',
-    event: ['click'],
-    children: [{
-      type: 'image',
-      attr: {
-        src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
-      },
-      style: {
-        width: 200,
-        height: 200
-      }
-    }, {
-      type: 'text',
-      attr: {
-        value: 'title2'
-      },
-      style: {
-        flex: 1,
-        color: '#ff0000',
-        fontSize: 48,
-        fontWeight: 'bold',
-        backgroundColor: '#eeeeee'
-      }
-    }]
-  }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/backward2.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/backward2.source.js b/html5/test/case/basic/backward2.source.js
deleted file mode 100644
index cf4885a..0000000
--- a/html5/test/case/basic/backward2.source.js
+++ /dev/null
@@ -1,113 +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.
- */
-define('taobao-item', function (require, exports, module) {
-
-;
-    module.exports = {
-      data: function () {return {
-        itemId: '',
-        title: '',
-        pictureUrl: ''
-      }},
-      methods: {
-        gotoDetail: function () {
-          this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId)
-        }
-      }
-    }
-  
-
-;module.exports.style = {
-  "thumb": {
-    "width": 200,
-    "height": 200
-  },
-  "title": {
-    "flex": 1,
-    "color": "#ff0000",
-    "fontSize": 48,
-    "fontWeight": "bold",
-    "backgroundColor": "#eeeeee"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "events": {
-    "click": "gotoDetail"
-  },
-  "children": [
-    {
-      "type": "image",
-      "classList": [
-        "thumb"
-      ],
-      "attr": {
-        "src": function () {return this.pictureUrl}
-      }
-    },
-    {
-      "type": "text",
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": function () {return this.title}
-      }
-    }
-  ]
-}
-
-;})
-
-// module
-
-define('backward2', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      itemList: [
-        {itemId: '520421163634', title: 'title1', pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'},
-        {itemId: '522076777462', title: 'title2', pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'}
-      ]
-    }}
-  }
-
-
-;module.exports.style = {}
-
-;module.exports.template = {
-  "type": "container",
-  "style": {
-    "flexDirection": "column"
-  },
-  "children": [
-    {
-      "type": "taobao-item",
-      "repeat": function () {return this.itemList}
-    }
-  ]
-}
-
-;})
-
-// require module
-
-require('backward2')()

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/components.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/components.output.js b/html5/test/case/basic/components.output.js
deleted file mode 100644
index b7acaa4..0000000
--- a/html5/test/case/basic/components.output.js
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-    "type": "container",
-    "children": [{
-        "type": "container",
-        "style": {
-            "marginTop": 10
-        },
-        "children": [{
-            "type": "text",
-            "attr": {
-                "value": "aa"
-            }
-        },{
-            "type": "text",
-            "attr": {
-                "value": "bb"
-            }
-        }]
-    }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/components.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/components.source.js b/html5/test/case/basic/components.source.js
deleted file mode 100644
index f095486..0000000
--- a/html5/test/case/basic/components.source.js
+++ /dev/null
@@ -1,101 +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.
- */
-function otherNameFunction () {
-  var module = {}
-  var exports = {}
-  module.exports = exports
-
-  ;module.exports.style = {}
-
-  ;module.exports.template = {
-    "type": "container",
-    "children": [
-      {
-        "type": "text",
-        "attr": {
-          "value": function () {return this.outerData.a}
-        }
-      },
-      {
-        "type": "text",
-        "attr": {
-          "value": function () {return this.outerData.b}
-        }
-      }
-    ]
-  }
-
-  ;module.exports.created = function() {
-    this.outerData.a = 'aa'
-  }
-
-  ;module.exports.ready = function() {
-    this.outerData.b = 'bb'
-  }
-
-  return module.exports
-}
-
-// module
-
-define('@weex-component/subvm', function (require, exports, module) {
-
-;
-  module.exports = {
-    "components": {
-      innertpl: otherNameFunction()
-    },
-    data: function () {return {
-      item: {a: 'a', b: 'b'},
-      className: 'fromOuter',
-      marginTop: 10
-    }}
-  }
-
-
-;module.exports.style = {}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "innertpl",
-      "classList": function() {
-        return this.className
-      },
-      "style": {
-        marginTop: function() {
-          return this.marginTop
-        }
-      },
-      "events": {
-          "click": "clicked"
-      },
-      "attr": {
-        "outerData": function () {return this.item}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/subvm')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/computed-in-repeat.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/computed-in-repeat.output.js b/html5/test/case/basic/computed-in-repeat.output.js
deleted file mode 100644
index 14a8d23..0000000
--- a/html5/test/case/basic/computed-in-repeat.output.js
+++ /dev/null
@@ -1,19 +0,0 @@
-{
-    "type": "div",
-    "children": [{
-        "type": "text",
-        "attr": {
-            "value": "Hello Evan You"
-        }
-    }, {
-        "type": "text",
-        "attr": {
-            "value": "Hello Terry King"
-        }
-    }, {
-        "type": "text",
-        "attr": {
-            "value": "Jack Ma"
-        }
-    }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/computed-in-repeat.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/computed-in-repeat.source.js b/html5/test/case/basic/computed-in-repeat.source.js
deleted file mode 100644
index fc203a9..0000000
--- a/html5/test/case/basic/computed-in-repeat.source.js
+++ /dev/null
@@ -1,82 +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.
- */
-define('@weex-component/e9b6b411765a9cc9e9d1a0469ae0a84c', function(require, exports, module) {
-
-    ;
-    module.exports = {
-        data: function() {
-            return {
-                firstName: 'Jack',
-                lastName: 'Ma',
-                peoples: [{
-                    firstName: 'Evan',
-                    lastName: 'You'
-                }, {
-                    firstName: 'Terry',
-                    lastName: 'King'
-                }]
-            }
-        },
-        computed: {
-            fullName: function() {
-                return this.firstName + ' ' + this.lastName
-            }
-        }
-    }
-
-
-    ;
-    module.exports.style = {}
-
-    ;
-    module.exports.template = {
-        "type": "div",
-        "classList": [
-            "bg"
-        ],
-        "children": [{
-            "type": "text",
-            "repeat": function() {
-                return this.peoples
-            },
-            "classList": [
-                "text"
-            ],
-            "attr": {
-                "value": function() {
-                    return 'Hello ' + (this.fullName)
-                }
-            }
-        }, {
-            "type": "text",
-            "attr": {
-                "value": function() {
-                    return this.fullName
-                }
-            }
-        }]
-    }
-
-    ;
-})
-
-// require module
-bootstrap('@weex-component/e9b6b411765a9cc9e9d1a0469ae0a84c', {
-    "transformerVersion": "0.3.1"
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/created.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/created.output.js b/html5/test/case/basic/created.output.js
deleted file mode 100644
index 39c15f3..0000000
--- a/html5/test/case/basic/created.output.js
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "children": [
-    {
-      "attr": {
-        "value": "1"
-      },
-      "type": "text"
-    }
-  ],
-  "type": "container"
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/created.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/created.source.js b/html5/test/case/basic/created.source.js
deleted file mode 100644
index dd2ee5b..0000000
--- a/html5/test/case/basic/created.source.js
+++ /dev/null
@@ -1,56 +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.
- */
-define('@weex-component/created', function(require, exports, module) {
-
-    ;
-    module.exports = {
-        data: function() {
-            return {
-                text: '',
-            }
-        },
-        created: function() {
-            this.text = '1'
-        }
-    }
-
-
-    ;
-    module.exports.style = {}
-
-    ;
-    module.exports.template = {
-        "type": "container",
-        "children": [{
-            "type": "text",
-            "attr": {
-                "value": function() {
-                    return this.text
-                }
-            }
-        }]
-    }
-
-    ;
-})
-
-// require module
-bootstrap('@weex-component/created', {
-    "transformerVersion": "0.3.1"
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/dynamic-id.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/dynamic-id.output.js b/html5/test/case/basic/dynamic-id.output.js
deleted file mode 100644
index 5029a30..0000000
--- a/html5/test/case/basic/dynamic-id.output.js
+++ /dev/null
@@ -1,44 +0,0 @@
-{
-  type: 'div',
-  children: [
-    {
-      type: 'text',
-      attr: {
-        value: 'Hello',
-        a: 1
-      }
-    },
-    {
-      type: 'text',
-      attr: {
-        value: 'Hello',
-        a: 1,
-        b: 1
-      }
-    },
-    {
-      type: 'text',
-      attr: {
-        value: 'Hello'
-      }
-    },
-    {
-      type: 'text',
-      attr: {
-        value: 'Hello'
-      }
-    },
-    {
-      type: 'text',
-      attr: {
-        value: 'Hello'
-      }
-    },
-    {
-      type: 'text',
-      attr: {
-        value: 'Hello'
-      }
-    }
-  ]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/dynamic-id.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/dynamic-id.source.js b/html5/test/case/basic/dynamic-id.source.js
deleted file mode 100644
index 6616cec..0000000
--- a/html5/test/case/basic/dynamic-id.source.js
+++ /dev/null
@@ -1,94 +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.
- */
-define('@weex-component/id', function (require, exports, module) {
-
-;
-  module.exports = {
-    ready: function () {
-      if (this.$el('x')) {
-        this.$el('x').setAttr('a', 1)
-      }
-      if (this.$el('y')) {
-        this.$el('y').setAttr('a', 1)
-      }
-      if (this.$el('')) {
-        this.$el('').setAttr('a', 1)
-      }
-      if (this.$el('0')) {
-        this.$el('0').setAttr('a', 1)
-      }
-      if (this.$el(0)) {
-        this.$el(0).setAttr('b', 1)
-      }
-    }
-  }
-
-;module.exports.template = {
-  "type": "div",
-  "children": [
-    {
-      "type": "text",
-      "id": function () { return "x" },
-      "attr": {
-        "value": "Hello"
-      }
-    },
-    {
-      "type": "text",
-      "id": function () { return 0 },
-      "attr": {
-        "value": "Hello"
-      }
-    },
-    {
-      "type": "text",
-      "id": function () { return '' },
-      "attr": {
-        "value": "Hello"
-      }
-    },
-    {
-      "type": "text",
-      "id": function () { return null },
-      "attr": {
-        "value": "Hello"
-      }
-    },
-    {
-      "type": "text",
-      "id": function () { return NaN },
-      "attr": {
-        "value": "Hello"
-      }
-    },
-    {
-      "type": "text",
-      "id": function () { return },
-      "attr": {
-        "value": "Hello"
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/id')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/dynamic-property.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/dynamic-property.output.js b/html5/test/case/basic/dynamic-property.output.js
deleted file mode 100644
index 8ccc9d5..0000000
--- a/html5/test/case/basic/dynamic-property.output.js
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  "type": "div",
-  "children": [{
-    "type": "text",
-    "attr": {
-      "value": "A: undefined"
-    }
-  }, {
-    "type": "text",
-    "attr": {
-      "value": "B: created"
-    }
-  }, {
-    "type": "text",
-    "attr": {
-      "value": "C: ready"
-    }
-  }, {
-    "type": "text",
-    "attr": {
-      "value": "D: initial"
-    }
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/dynamic-property.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/dynamic-property.source.js b/html5/test/case/basic/dynamic-property.source.js
deleted file mode 100644
index 880d468..0000000
--- a/html5/test/case/basic/dynamic-property.source.js
+++ /dev/null
@@ -1,79 +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.
- */
-define('@weex-component/dynamic-property', function (require, exports, module) {
-
-  ;module.exports.style = {}
-
-  ;module.exports = {
-    data: function () {return {
-      A: {},
-      B: {},
-      C: {
-        message: 'initial'
-      },
-      D: {
-        message: 'initial'
-      }
-    }},
-
-    created: function() {
-      this.B.message = 'created'
-      this.C.message = 'created'
-    },
-
-    ready: function() {
-      this.A.message = 'ready'
-      this.B.message = 'ready'
-      this.C.message = 'ready'
-    }
-  }
-
-  ;module.exports.template = {
-    "type": "div",
-	  "children": [
-	    {
-	      "type": "text",
-	      "attr": {
-	        "value": function () {return 'A: ' + (this.A.message)}
-	      }
-	    },
-	    {
-	      "type": "text",
-	      "attr": {
-	        "value": function () {return 'B: ' + (this.B.message)}
-	      }
-	    },
-	    {
-	      "type": "text",
-	      "attr": {
-	        "value": function () {return 'C: ' + (this.C.message)}
-	      }
-	    },
-	    {
-	      "type": "text",
-	      "attr": {
-	        "value": function () {return 'D: ' + (this.D.message)}
-	      }
-	    }
-	  ]
-  }
-
-;})
-
-bootstrap('@weex-component/dynamic-property')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/dynamic-type.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/dynamic-type.output.js b/html5/test/case/basic/dynamic-type.output.js
deleted file mode 100644
index d6881a4..0000000
--- a/html5/test/case/basic/dynamic-type.output.js
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'text',
-    style: {
-      width: 200,
-      height: 200
-    }
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/dynamic-type.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/dynamic-type.source.js b/html5/test/case/basic/dynamic-type.source.js
deleted file mode 100644
index 62b8f72..0000000
--- a/html5/test/case/basic/dynamic-type.source.js
+++ /dev/null
@@ -1,45 +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.
- */
-define('@weex-component/dynamic-type', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      type: 'text'
-    }}
-  }
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": function () {return this.type},
-      "style": {
-        "width": 200,
-        "height": 200
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/dynamic-type')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo.output.js b/html5/test/case/basic/foo.output.js
deleted file mode 100644
index 1372f91..0000000
--- a/html5/test/case/basic/foo.output.js
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    type: 'container'
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo.source.js b/html5/test/case/basic/foo.source.js
deleted file mode 100644
index 027eee1..0000000
--- a/html5/test/case/basic/foo.source.js
+++ /dev/null
@@ -1,44 +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.
- */
-define('@weex-component/foo', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {
-      return {
-        x: 1
-      }
-    }
-  }
-
-
-;module.exports.style = {
-  "a": {
-    "fontSize": 12
-  }
-}
-
-;module.exports.template = {
-  "type": "container"
-}
-
-;})
-
-
-bootstrap('@weex-component/foo')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo2.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo2.output.js b/html5/test/case/basic/foo2.output.js
deleted file mode 100644
index d7cc321..0000000
--- a/html5/test/case/basic/foo2.output.js
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-    type: 'container',
-    attr: {
-        a: '1',
-        b: 1
-    },
-    style: {
-        fontSize: 12
-    }
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo2.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo2.source.js b/html5/test/case/basic/foo2.source.js
deleted file mode 100644
index cabf559..0000000
--- a/html5/test/case/basic/foo2.source.js
+++ /dev/null
@@ -1,52 +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.
- */
-define('@weex-component/foo2', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {
-      return {
-        x: 1
-      }
-    }
-  }
-
-
-;module.exports.style = {
-  "a": {
-    "fontSize": 12
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "attr": {
-    "a": "1",
-    "b": function () {return this.x}
-  },
-  "classList": [
-    "a"
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/foo2')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo3.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo3.output.js b/html5/test/case/basic/foo3.output.js
deleted file mode 100644
index 51c886e..0000000
--- a/html5/test/case/basic/foo3.output.js
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-    type: 'container',
-    children: [{
-        type: 'text',
-        attr: {
-            value: 'Hello World'
-        }
-    }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo3.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo3.source.js b/html5/test/case/basic/foo3.source.js
deleted file mode 100644
index 7fda995..0000000
--- a/html5/test/case/basic/foo3.source.js
+++ /dev/null
@@ -1,41 +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.
- */
-define('@weex-component/foo3', function (require, exports, module) {
-
-;
-
-;module.exports.style = {}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "value": "Hello World"
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/foo3')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo4.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo4.output.js b/html5/test/case/basic/foo4.output.js
deleted file mode 100644
index bef3668..0000000
--- a/html5/test/case/basic/foo4.output.js
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'text',
-    attr: {
-      value: 'Hello World'
-    },
-    style: {
-      color: '#ff0000'
-    }
-  }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo4.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo4.source.js b/html5/test/case/basic/foo4.source.js
deleted file mode 100644
index f842abf..0000000
--- a/html5/test/case/basic/foo4.source.js
+++ /dev/null
@@ -1,44 +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.
- */
-define('@weex-component/foo4', function (require, exports, module) {
-
-;
-
-;module.exports.style = {}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "style": {
-        "color": "#ff0000"
-      },
-      "attr": {
-        "value": "Hello World"
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/foo4')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo5.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo5.output.js b/html5/test/case/basic/foo5.output.js
deleted file mode 100644
index 840cefb..0000000
--- a/html5/test/case/basic/foo5.output.js
+++ /dev/null
@@ -1,14 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'text',
-    attr: {
-      value: 'Hello World'
-    },
-    style: {
-      color: '#ff0000',
-      fontSize: 48,
-      fontWeight: 'bold'
-    }
-  }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo5.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo5.source.js b/html5/test/case/basic/foo5.source.js
deleted file mode 100644
index b09fc03..0000000
--- a/html5/test/case/basic/foo5.source.js
+++ /dev/null
@@ -1,50 +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.
- */
-define('@weex-component/foo5', function (require, exports, module) {
-
-;
-
-;module.exports.style = {
-  "title": {
-    "color": "#ff0000",
-    "fontSize": 48,
-    "fontWeight": "bold"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": "Hello World"
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/foo5')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo6.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo6.output.js b/html5/test/case/basic/foo6.output.js
deleted file mode 100644
index 1a8fd90..0000000
--- a/html5/test/case/basic/foo6.output.js
+++ /dev/null
@@ -1,28 +0,0 @@
-{
-  type: 'container',
-  style: {
-    flexDirection: 'row'
-  },
-  children: [{
-    type: 'image',
-    attr: {
-      src: 'http://gw.alicdn.com/tfscom/TB1OMfAJFXXXXbfXXXXqVMCNVXX-400-400.jpg'
-    },
-    style: {
-      width: 200,
-      height: 200
-    }
-  }, {
-    type: 'text',
-    attr: {
-      value: '一个超赞的宝贝标题'
-    },
-    style: {
-      flex: 1,
-      color: '#ff0000',
-      fontSize: 48,
-      fontWeight: 'bold',
-      backgroundColor: '#eeeeee'
-    }
-  }]
-}
\ No newline at end of file



[07/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/vdom/index.js
----------------------------------------------------------------------
diff --git a/runtime/vdom/index.js b/runtime/vdom/index.js
new file mode 100644
index 0000000..2947f5e
--- /dev/null
+++ b/runtime/vdom/index.js
@@ -0,0 +1,37 @@
+/*
+ * 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 Node from './Node'
+import Element from './Element'
+import Comment from './Comment'
+import Document from './Document'
+
+export {
+  registerElement,
+  unregisterElement,
+  isWeexElement,
+  clearWeexElements
+} from './WeexElement'
+
+export {
+  Document,
+  Node,
+  Element,
+  Comment
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/vdom/operation.js
----------------------------------------------------------------------
diff --git a/runtime/vdom/operation.js b/runtime/vdom/operation.js
new file mode 100644
index 0000000..69a64e1
--- /dev/null
+++ b/runtime/vdom/operation.js
@@ -0,0 +1,272 @@
+/*
+ * 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.
+ */
+
+const docMap = {}
+
+/**
+ * Add a document object into docMap.
+ * @param {string} id
+ * @param {object} document
+ */
+export function addDoc (id, doc) {
+  if (id) {
+    docMap[id] = doc
+  }
+}
+
+/**
+ * Get the document object by id.
+ * @param {string} id
+ */
+export function getDoc (id) {
+  return docMap[id]
+}
+
+/**
+ * Remove the document from docMap by id.
+ * @param {string} id
+ */
+export function removeDoc (id) {
+  delete docMap[id]
+}
+
+/**
+ * @deprecated
+ * Get listener by document id.
+ * @param {string} id
+ * @return {object} listener
+ */
+export function getListener (id) {
+  const doc = docMap[id]
+  if (doc && doc.listener) {
+    return doc.listener
+  }
+  return null
+}
+
+/**
+ * Get TaskCenter instance by id.
+ * @param {string} id
+ * @return {object} TaskCenter
+ */
+export function getTaskCenter (id) {
+  const doc = docMap[id]
+  if (doc && doc.taskCenter) {
+    return doc.taskCenter
+  }
+  return null
+}
+
+/**
+ * Append body node to documentElement.
+ * @param {object} document
+ * @param {object} node
+ * @param {object} before
+ */
+export function appendBody (doc, node, before) {
+  const { documentElement } = doc
+
+  if (documentElement.pureChildren.length > 0 || node.parentNode) {
+    return
+  }
+  const children = documentElement.children
+  const beforeIndex = children.indexOf(before)
+  if (beforeIndex < 0) {
+    children.push(node)
+  }
+  else {
+    children.splice(beforeIndex, 0, node)
+  }
+
+  if (node.nodeType === 1) {
+    if (node.role === 'body') {
+      node.docId = doc.id
+      node.ownerDocument = doc
+      node.parentNode = documentElement
+      linkParent(node, documentElement)
+    }
+    else {
+      node.children.forEach(child => {
+        child.parentNode = node
+      })
+      setBody(doc, node)
+      node.docId = doc.id
+      node.ownerDocument = doc
+      linkParent(node, documentElement)
+      delete doc.nodeMap[node.nodeId]
+    }
+    documentElement.pureChildren.push(node)
+    sendBody(doc, node)
+  }
+  else {
+    node.parentNode = documentElement
+    doc.nodeMap[node.ref] = node
+  }
+}
+
+function sendBody (doc, node) {
+  const body = node.toJSON()
+  if (doc && doc.taskCenter && typeof doc.taskCenter.send === 'function') {
+    doc.taskCenter.send('dom', { action: 'createBody' }, [body])
+  }
+}
+
+/**
+ * Set up body node.
+ * @param {object} document
+ * @param {object} element
+ */
+export function setBody (doc, el) {
+  el.role = 'body'
+  el.depth = 1
+  delete doc.nodeMap[el.nodeId]
+  el.ref = '_root'
+  doc.nodeMap._root = el
+  doc.body = el
+}
+
+/**
+ * Establish the connection between parent and child node.
+ * @param {object} child node
+ * @param {object} parent node
+ */
+export function linkParent (node, parent) {
+  node.parentNode = parent
+  if (parent.docId) {
+    node.docId = parent.docId
+    node.ownerDocument = parent.ownerDocument
+    node.ownerDocument.nodeMap[node.nodeId] = node
+    node.depth = parent.depth + 1
+  }
+  node.children.forEach(child => {
+    linkParent(child, node)
+  })
+}
+
+/**
+ * Get the next sibling element.
+ * @param {object} node
+ */
+export function nextElement (node) {
+  while (node) {
+    if (node.nodeType === 1) {
+      return node
+    }
+    node = node.nextSibling
+  }
+}
+
+/**
+ * Get the previous sibling element.
+ * @param {object} node
+ */
+export function previousElement (node) {
+  while (node) {
+    if (node.nodeType === 1) {
+      return node
+    }
+    node = node.previousSibling
+  }
+}
+
+/**
+ * Insert a node into list at the specified index.
+ * @param {object} target node
+ * @param {array} list
+ * @param {number} newIndex
+ * @param {boolean} changeSibling
+ * @return {number} newIndex
+ */
+export function insertIndex (target, list, newIndex, changeSibling) {
+  /* istanbul ignore next */
+  if (newIndex < 0) {
+    newIndex = 0
+  }
+  const before = list[newIndex - 1]
+  const after = list[newIndex]
+  list.splice(newIndex, 0, target)
+  if (changeSibling) {
+    before && (before.nextSibling = target)
+    target.previousSibling = before
+    target.nextSibling = after
+    after && (after.previousSibling = target)
+  }
+  return newIndex
+}
+
+/**
+ * Move the node to a new index in list.
+ * @param {object} target node
+ * @param {array} list
+ * @param {number} newIndex
+ * @param {boolean} changeSibling
+ * @return {number} newIndex
+ */
+export function moveIndex (target, list, newIndex, changeSibling) {
+  const index = list.indexOf(target)
+  /* istanbul ignore next */
+  if (index < 0) {
+    return -1
+  }
+  if (changeSibling) {
+    const before = list[index - 1]
+    const after = list[index + 1]
+    before && (before.nextSibling = after)
+    after && (after.previousSibling = before)
+  }
+  list.splice(index, 1)
+  let newIndexAfter = newIndex
+  if (index <= newIndex) {
+    newIndexAfter = newIndex - 1
+  }
+  const beforeNew = list[newIndexAfter - 1]
+  const afterNew = list[newIndexAfter]
+  list.splice(newIndexAfter, 0, target)
+  if (changeSibling) {
+    beforeNew && (beforeNew.nextSibling = target)
+    target.previousSibling = beforeNew
+    target.nextSibling = afterNew
+    afterNew && (afterNew.previousSibling = target)
+  }
+  if (index === newIndexAfter) {
+    return -1
+  }
+  return newIndex
+}
+
+/**
+ * Remove the node from list.
+ * @param {object} target node
+ * @param {array} list
+ * @param {boolean} changeSibling
+ */
+export function removeIndex (target, list, changeSibling) {
+  const index = list.indexOf(target)
+  /* istanbul ignore next */
+  if (index < 0) {
+    return
+  }
+  if (changeSibling) {
+    const before = list[index - 1]
+    const after = list[index + 1]
+    before && (before.nextSibling = after)
+    after && (after.previousSibling = before)
+  }
+  list.splice(index, 1)
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/.eslintrc
----------------------------------------------------------------------
diff --git a/test/js-framework/case/.eslintrc b/test/js-framework/case/.eslintrc
new file mode 100644
index 0000000..c892a87
--- /dev/null
+++ b/test/js-framework/case/.eslintrc
@@ -0,0 +1,9 @@
+{
+  "globals": {
+    "callNativeHandler": true,
+  },
+
+  "rules": {
+    "no-eval": "off"
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/append-root.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/append-root.output.js b/test/js-framework/case/basic/append-root.output.js
new file mode 100644
index 0000000..3c4a5a4
--- /dev/null
+++ b/test/js-framework/case/basic/append-root.output.js
@@ -0,0 +1,36 @@
+{
+  type: 'container',
+  style: {
+    flexDirection: 'column'
+  },
+  attr: {
+    append: "tree"
+  },
+  children: [{
+    type: 'container',
+    children: [{
+      type: 'image',
+      attr: {
+        src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
+      }
+    }, {
+      type: 'text',
+      attr: {
+        value: 'title1'
+      }
+    }]
+  }, {
+    type: 'container',
+    children: [{
+      type: 'image',
+      attr: {
+        src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
+      }
+    }, {
+      type: 'text',
+      attr: {
+        value: 'title2'
+      }
+    }]
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/append-root.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/append-root.source.js b/test/js-framework/case/basic/append-root.source.js
new file mode 100644
index 0000000..dfe60d7
--- /dev/null
+++ b/test/js-framework/case/basic/append-root.source.js
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+define('@weex-component/append', function(require, exports, module) {;
+  module.exports = {
+    data: function() {
+      return {
+        itemList: [{
+          itemId: '520421163634',
+          title: 'title1',
+          pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
+        }, {
+          itemId: '522076777462',
+          title: 'title2',
+          pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
+        }]
+      }
+    }
+  }
+
+
+  ;
+  module.exports.style = {}
+
+  ;
+  module.exports.template = {
+    "type": "container",
+    "style": {
+      "flexDirection": "column"
+    },
+    "append": "tree",
+    "children": [{
+      "type": "container",
+      "children": [{
+        "type": "image",
+        "attr": {
+          "src": function() {
+            return this.pictureUrl
+          }
+        }
+      }, {
+        "type": "text",
+        "attr": {
+          "value": function() {
+            return this.title
+          }
+        }
+      }],
+      "repeat": function() {
+        return this.itemList
+      }
+    }]
+  }
+
+  ;
+})
+
+// require module
+
+bootstrap('@weex-component/append')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/append.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/append.output.js b/test/js-framework/case/basic/append.output.js
new file mode 100644
index 0000000..5259fb6
--- /dev/null
+++ b/test/js-framework/case/basic/append.output.js
@@ -0,0 +1,79 @@
+{
+  type: 'container',
+  style: {
+    flexDirection: 'column'
+  },
+  children: [{
+    type: 'container',
+    event: ['click'],
+    attr: {
+      append: 'tree'
+    },
+    children: [{
+      type: 'container',
+      children: [{
+        type: 'text',
+        attr: {
+          value: 'any text'
+        }
+      }]
+    }, {
+      type: 'image',
+      attr: {
+        src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
+      },
+      style: {
+        width: 200,
+        height: 200
+      }
+    }, {
+      type: 'text',
+      attr: {
+        value: 'title1'
+      },
+      style: {
+        flex: 1,
+        color: '#ff0000',
+        fontSize: 48,
+        fontWeight: 'bold',
+        backgroundColor: '#eeeeee'
+      }
+    }]
+  }, {
+    type: 'container',
+    event: ['click'],
+    attr: {
+      append: 'tree'
+    },
+    children: [{
+      type: 'container',
+      children: [{
+        type: 'text',
+        attr: {
+          value: 'any text'
+        }
+      }]
+    }, {
+      type: 'image',
+      attr: {
+        src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
+      },
+      style: {
+        width: 200,
+        height: 200
+      }
+    }, {
+      type: 'text',
+      attr: {
+        value: 'title2'
+      },
+      style: {
+        flex: 1,
+        color: '#ff0000',
+        fontSize: 48,
+        fontWeight: 'bold',
+        backgroundColor: '#eeeeee'
+      }
+    }]
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/append.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/append.source.js b/test/js-framework/case/basic/append.source.js
new file mode 100644
index 0000000..c216578
--- /dev/null
+++ b/test/js-framework/case/basic/append.source.js
@@ -0,0 +1,123 @@
+/*
+ * 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.
+ */
+define('@weex-component/taobao-item', function (require, exports, module) {
+
+;
+    module.exports = {
+      data: function () {return {
+        itemId: '',
+        title: '',
+        pictureUrl: ''
+      }},
+      methods: {
+        gotoDetail: function () {
+          this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId)
+        }
+      }
+    }
+  
+
+;module.exports.style = {
+  "thumb": {
+    "width": 200,
+    "height": 200
+  },
+  "title": {
+    "flex": 1,
+    "color": "#ff0000",
+    "fontSize": 48,
+    "fontWeight": "bold",
+    "backgroundColor": "#eeeeee"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "events": {
+    "click": "gotoDetail"
+  },
+  "append": "tree",
+  "children": [
+    {
+      "type": "container",
+      "children": [{
+        "type": "text",
+        "attr": {
+          "value": "any text"
+        }
+      }]
+    },
+    {
+      "type": "image",
+      "classList": [
+        "thumb"
+      ],
+      "attr": {
+        "src": function () {return this.pictureUrl}
+      }
+    },
+    {
+      "type": "text",
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": function () {return this.title}
+      }
+    }
+  ]
+}
+
+;})
+
+// module
+
+define('@weex-component/append', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      itemList: [
+        {itemId: '520421163634', title: 'title1', pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'},
+        {itemId: '522076777462', title: 'title2', pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'}
+      ]
+    }}
+  }
+
+
+;module.exports.style = {}
+
+;module.exports.template = {
+  "type": "container",
+  "style": {
+    "flexDirection": "column"
+  },
+  "children": [
+    {
+      "type": "taobao-item",
+      "repeat": function () {return this.itemList}
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/append')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/backward1.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/backward1.output.js b/test/js-framework/case/basic/backward1.output.js
new file mode 100644
index 0000000..8f6d086
--- /dev/null
+++ b/test/js-framework/case/basic/backward1.output.js
@@ -0,0 +1,57 @@
+{
+  type: 'container',
+  style: {
+    flexDirection: 'column'
+  },
+  children: [{
+    type: 'container',
+    event: ['click'],
+    children: [{
+      type: 'image',
+      attr: {
+        src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
+      },
+      style: {
+        width: 200,
+        height: 200
+      }
+    }, {
+      type: 'text',
+      attr: {
+        value: 'title1'
+      },
+      style: {
+        flex: 1,
+        color: '#ff0000',
+        fontSize: 48,
+        fontWeight: 'bold',
+        backgroundColor: '#eeeeee'
+      }
+    }]
+  }, {
+    type: 'container',
+    event: ['click'],
+    children: [{
+      type: 'image',
+      attr: {
+        src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
+      },
+      style: {
+        width: 200,
+        height: 200
+      }
+    }, {
+      type: 'text',
+      attr: {
+        value: 'title2'
+      },
+      style: {
+        flex: 1,
+        color: '#ff0000',
+        fontSize: 48,
+        fontWeight: 'bold',
+        backgroundColor: '#eeeeee'
+      }
+    }]
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/backward1.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/backward1.source.js b/test/js-framework/case/basic/backward1.source.js
new file mode 100644
index 0000000..1bf6d74
--- /dev/null
+++ b/test/js-framework/case/basic/backward1.source.js
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+register('taobao-item', {
+
+    data: function() {
+        return {
+            itemId: '',
+            title: '',
+            pictureUrl: ''
+        };
+    },
+    methods: {
+        gotoDetail: function() {
+            this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId);
+        }
+    }
+
+    ,
+    style: {
+        "thumb": {
+            "width": 200,
+            "height": 200
+        },
+        "title": {
+            "flex": 1,
+            "color": "#ff0000",
+            "fontSize": 48,
+            "fontWeight": "bold",
+            "backgroundColor": "#eeeeee"
+        }
+    },
+    template: {
+        "type": "container",
+        "events": {
+            "click": "gotoDetail"
+        },
+        "children": [{
+            "type": "image",
+            "classList": [
+                "thumb"
+            ],
+            "attr": {
+                "src": function() {
+                    return this.pictureUrl
+                }
+            }
+        }, {
+            "type": "text",
+            "classList": [
+                "title"
+            ],
+            "attr": {
+                "value": function() {
+                    return this.title
+                }
+            }
+        }]
+    }
+})
+
+// module
+
+register('backward1', {
+
+    data: function() {
+        return {
+            itemList: [{
+                itemId: '520421163634',
+                title: 'title1',
+                pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
+            }, {
+                itemId: '522076777462',
+                title: 'title2',
+                pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
+            }]
+        };
+    }
+
+    ,
+    style: {},
+    template: {
+        "type": "container",
+        "style": {
+            "flexDirection": "column"
+        },
+        "children": [{
+            "type": "taobao-item",
+            "repeat": function() {
+                return this.itemList
+            }
+        }]
+    }
+})
+
+// require module
+
+render('backward1', {})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/backward2.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/backward2.output.js b/test/js-framework/case/basic/backward2.output.js
new file mode 100644
index 0000000..8f6d086
--- /dev/null
+++ b/test/js-framework/case/basic/backward2.output.js
@@ -0,0 +1,57 @@
+{
+  type: 'container',
+  style: {
+    flexDirection: 'column'
+  },
+  children: [{
+    type: 'container',
+    event: ['click'],
+    children: [{
+      type: 'image',
+      attr: {
+        src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
+      },
+      style: {
+        width: 200,
+        height: 200
+      }
+    }, {
+      type: 'text',
+      attr: {
+        value: 'title1'
+      },
+      style: {
+        flex: 1,
+        color: '#ff0000',
+        fontSize: 48,
+        fontWeight: 'bold',
+        backgroundColor: '#eeeeee'
+      }
+    }]
+  }, {
+    type: 'container',
+    event: ['click'],
+    children: [{
+      type: 'image',
+      attr: {
+        src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
+      },
+      style: {
+        width: 200,
+        height: 200
+      }
+    }, {
+      type: 'text',
+      attr: {
+        value: 'title2'
+      },
+      style: {
+        flex: 1,
+        color: '#ff0000',
+        fontSize: 48,
+        fontWeight: 'bold',
+        backgroundColor: '#eeeeee'
+      }
+    }]
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/backward2.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/backward2.source.js b/test/js-framework/case/basic/backward2.source.js
new file mode 100644
index 0000000..cf4885a
--- /dev/null
+++ b/test/js-framework/case/basic/backward2.source.js
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+define('taobao-item', function (require, exports, module) {
+
+;
+    module.exports = {
+      data: function () {return {
+        itemId: '',
+        title: '',
+        pictureUrl: ''
+      }},
+      methods: {
+        gotoDetail: function () {
+          this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId)
+        }
+      }
+    }
+  
+
+;module.exports.style = {
+  "thumb": {
+    "width": 200,
+    "height": 200
+  },
+  "title": {
+    "flex": 1,
+    "color": "#ff0000",
+    "fontSize": 48,
+    "fontWeight": "bold",
+    "backgroundColor": "#eeeeee"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "events": {
+    "click": "gotoDetail"
+  },
+  "children": [
+    {
+      "type": "image",
+      "classList": [
+        "thumb"
+      ],
+      "attr": {
+        "src": function () {return this.pictureUrl}
+      }
+    },
+    {
+      "type": "text",
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": function () {return this.title}
+      }
+    }
+  ]
+}
+
+;})
+
+// module
+
+define('backward2', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      itemList: [
+        {itemId: '520421163634', title: 'title1', pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'},
+        {itemId: '522076777462', title: 'title2', pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'}
+      ]
+    }}
+  }
+
+
+;module.exports.style = {}
+
+;module.exports.template = {
+  "type": "container",
+  "style": {
+    "flexDirection": "column"
+  },
+  "children": [
+    {
+      "type": "taobao-item",
+      "repeat": function () {return this.itemList}
+    }
+  ]
+}
+
+;})
+
+// require module
+
+require('backward2')()

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/components.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/components.output.js b/test/js-framework/case/basic/components.output.js
new file mode 100644
index 0000000..b7acaa4
--- /dev/null
+++ b/test/js-framework/case/basic/components.output.js
@@ -0,0 +1,20 @@
+{
+    "type": "container",
+    "children": [{
+        "type": "container",
+        "style": {
+            "marginTop": 10
+        },
+        "children": [{
+            "type": "text",
+            "attr": {
+                "value": "aa"
+            }
+        },{
+            "type": "text",
+            "attr": {
+                "value": "bb"
+            }
+        }]
+    }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/components.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/components.source.js b/test/js-framework/case/basic/components.source.js
new file mode 100644
index 0000000..f095486
--- /dev/null
+++ b/test/js-framework/case/basic/components.source.js
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+function otherNameFunction () {
+  var module = {}
+  var exports = {}
+  module.exports = exports
+
+  ;module.exports.style = {}
+
+  ;module.exports.template = {
+    "type": "container",
+    "children": [
+      {
+        "type": "text",
+        "attr": {
+          "value": function () {return this.outerData.a}
+        }
+      },
+      {
+        "type": "text",
+        "attr": {
+          "value": function () {return this.outerData.b}
+        }
+      }
+    ]
+  }
+
+  ;module.exports.created = function() {
+    this.outerData.a = 'aa'
+  }
+
+  ;module.exports.ready = function() {
+    this.outerData.b = 'bb'
+  }
+
+  return module.exports
+}
+
+// module
+
+define('@weex-component/subvm', function (require, exports, module) {
+
+;
+  module.exports = {
+    "components": {
+      innertpl: otherNameFunction()
+    },
+    data: function () {return {
+      item: {a: 'a', b: 'b'},
+      className: 'fromOuter',
+      marginTop: 10
+    }}
+  }
+
+
+;module.exports.style = {}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "innertpl",
+      "classList": function() {
+        return this.className
+      },
+      "style": {
+        marginTop: function() {
+          return this.marginTop
+        }
+      },
+      "events": {
+          "click": "clicked"
+      },
+      "attr": {
+        "outerData": function () {return this.item}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/subvm')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/computed-in-repeat.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/computed-in-repeat.output.js b/test/js-framework/case/basic/computed-in-repeat.output.js
new file mode 100644
index 0000000..14a8d23
--- /dev/null
+++ b/test/js-framework/case/basic/computed-in-repeat.output.js
@@ -0,0 +1,19 @@
+{
+    "type": "div",
+    "children": [{
+        "type": "text",
+        "attr": {
+            "value": "Hello Evan You"
+        }
+    }, {
+        "type": "text",
+        "attr": {
+            "value": "Hello Terry King"
+        }
+    }, {
+        "type": "text",
+        "attr": {
+            "value": "Jack Ma"
+        }
+    }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/computed-in-repeat.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/computed-in-repeat.source.js b/test/js-framework/case/basic/computed-in-repeat.source.js
new file mode 100644
index 0000000..fc203a9
--- /dev/null
+++ b/test/js-framework/case/basic/computed-in-repeat.source.js
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+define('@weex-component/e9b6b411765a9cc9e9d1a0469ae0a84c', function(require, exports, module) {
+
+    ;
+    module.exports = {
+        data: function() {
+            return {
+                firstName: 'Jack',
+                lastName: 'Ma',
+                peoples: [{
+                    firstName: 'Evan',
+                    lastName: 'You'
+                }, {
+                    firstName: 'Terry',
+                    lastName: 'King'
+                }]
+            }
+        },
+        computed: {
+            fullName: function() {
+                return this.firstName + ' ' + this.lastName
+            }
+        }
+    }
+
+
+    ;
+    module.exports.style = {}
+
+    ;
+    module.exports.template = {
+        "type": "div",
+        "classList": [
+            "bg"
+        ],
+        "children": [{
+            "type": "text",
+            "repeat": function() {
+                return this.peoples
+            },
+            "classList": [
+                "text"
+            ],
+            "attr": {
+                "value": function() {
+                    return 'Hello ' + (this.fullName)
+                }
+            }
+        }, {
+            "type": "text",
+            "attr": {
+                "value": function() {
+                    return this.fullName
+                }
+            }
+        }]
+    }
+
+    ;
+})
+
+// require module
+bootstrap('@weex-component/e9b6b411765a9cc9e9d1a0469ae0a84c', {
+    "transformerVersion": "0.3.1"
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/created.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/created.output.js b/test/js-framework/case/basic/created.output.js
new file mode 100644
index 0000000..39c15f3
--- /dev/null
+++ b/test/js-framework/case/basic/created.output.js
@@ -0,0 +1,11 @@
+{
+  "children": [
+    {
+      "attr": {
+        "value": "1"
+      },
+      "type": "text"
+    }
+  ],
+  "type": "container"
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/created.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/created.source.js b/test/js-framework/case/basic/created.source.js
new file mode 100644
index 0000000..dd2ee5b
--- /dev/null
+++ b/test/js-framework/case/basic/created.source.js
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+define('@weex-component/created', function(require, exports, module) {
+
+    ;
+    module.exports = {
+        data: function() {
+            return {
+                text: '',
+            }
+        },
+        created: function() {
+            this.text = '1'
+        }
+    }
+
+
+    ;
+    module.exports.style = {}
+
+    ;
+    module.exports.template = {
+        "type": "container",
+        "children": [{
+            "type": "text",
+            "attr": {
+                "value": function() {
+                    return this.text
+                }
+            }
+        }]
+    }
+
+    ;
+})
+
+// require module
+bootstrap('@weex-component/created', {
+    "transformerVersion": "0.3.1"
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/dynamic-id.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/dynamic-id.output.js b/test/js-framework/case/basic/dynamic-id.output.js
new file mode 100644
index 0000000..5029a30
--- /dev/null
+++ b/test/js-framework/case/basic/dynamic-id.output.js
@@ -0,0 +1,44 @@
+{
+  type: 'div',
+  children: [
+    {
+      type: 'text',
+      attr: {
+        value: 'Hello',
+        a: 1
+      }
+    },
+    {
+      type: 'text',
+      attr: {
+        value: 'Hello',
+        a: 1,
+        b: 1
+      }
+    },
+    {
+      type: 'text',
+      attr: {
+        value: 'Hello'
+      }
+    },
+    {
+      type: 'text',
+      attr: {
+        value: 'Hello'
+      }
+    },
+    {
+      type: 'text',
+      attr: {
+        value: 'Hello'
+      }
+    },
+    {
+      type: 'text',
+      attr: {
+        value: 'Hello'
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/dynamic-id.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/dynamic-id.source.js b/test/js-framework/case/basic/dynamic-id.source.js
new file mode 100644
index 0000000..6616cec
--- /dev/null
+++ b/test/js-framework/case/basic/dynamic-id.source.js
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+define('@weex-component/id', function (require, exports, module) {
+
+;
+  module.exports = {
+    ready: function () {
+      if (this.$el('x')) {
+        this.$el('x').setAttr('a', 1)
+      }
+      if (this.$el('y')) {
+        this.$el('y').setAttr('a', 1)
+      }
+      if (this.$el('')) {
+        this.$el('').setAttr('a', 1)
+      }
+      if (this.$el('0')) {
+        this.$el('0').setAttr('a', 1)
+      }
+      if (this.$el(0)) {
+        this.$el(0).setAttr('b', 1)
+      }
+    }
+  }
+
+;module.exports.template = {
+  "type": "div",
+  "children": [
+    {
+      "type": "text",
+      "id": function () { return "x" },
+      "attr": {
+        "value": "Hello"
+      }
+    },
+    {
+      "type": "text",
+      "id": function () { return 0 },
+      "attr": {
+        "value": "Hello"
+      }
+    },
+    {
+      "type": "text",
+      "id": function () { return '' },
+      "attr": {
+        "value": "Hello"
+      }
+    },
+    {
+      "type": "text",
+      "id": function () { return null },
+      "attr": {
+        "value": "Hello"
+      }
+    },
+    {
+      "type": "text",
+      "id": function () { return NaN },
+      "attr": {
+        "value": "Hello"
+      }
+    },
+    {
+      "type": "text",
+      "id": function () { return },
+      "attr": {
+        "value": "Hello"
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/id')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/dynamic-property.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/dynamic-property.output.js b/test/js-framework/case/basic/dynamic-property.output.js
new file mode 100644
index 0000000..8ccc9d5
--- /dev/null
+++ b/test/js-framework/case/basic/dynamic-property.output.js
@@ -0,0 +1,24 @@
+{
+  "type": "div",
+  "children": [{
+    "type": "text",
+    "attr": {
+      "value": "A: undefined"
+    }
+  }, {
+    "type": "text",
+    "attr": {
+      "value": "B: created"
+    }
+  }, {
+    "type": "text",
+    "attr": {
+      "value": "C: ready"
+    }
+  }, {
+    "type": "text",
+    "attr": {
+      "value": "D: initial"
+    }
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/dynamic-property.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/dynamic-property.source.js b/test/js-framework/case/basic/dynamic-property.source.js
new file mode 100644
index 0000000..880d468
--- /dev/null
+++ b/test/js-framework/case/basic/dynamic-property.source.js
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+define('@weex-component/dynamic-property', function (require, exports, module) {
+
+  ;module.exports.style = {}
+
+  ;module.exports = {
+    data: function () {return {
+      A: {},
+      B: {},
+      C: {
+        message: 'initial'
+      },
+      D: {
+        message: 'initial'
+      }
+    }},
+
+    created: function() {
+      this.B.message = 'created'
+      this.C.message = 'created'
+    },
+
+    ready: function() {
+      this.A.message = 'ready'
+      this.B.message = 'ready'
+      this.C.message = 'ready'
+    }
+  }
+
+  ;module.exports.template = {
+    "type": "div",
+	  "children": [
+	    {
+	      "type": "text",
+	      "attr": {
+	        "value": function () {return 'A: ' + (this.A.message)}
+	      }
+	    },
+	    {
+	      "type": "text",
+	      "attr": {
+	        "value": function () {return 'B: ' + (this.B.message)}
+	      }
+	    },
+	    {
+	      "type": "text",
+	      "attr": {
+	        "value": function () {return 'C: ' + (this.C.message)}
+	      }
+	    },
+	    {
+	      "type": "text",
+	      "attr": {
+	        "value": function () {return 'D: ' + (this.D.message)}
+	      }
+	    }
+	  ]
+  }
+
+;})
+
+bootstrap('@weex-component/dynamic-property')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/dynamic-type.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/dynamic-type.output.js b/test/js-framework/case/basic/dynamic-type.output.js
new file mode 100644
index 0000000..d6881a4
--- /dev/null
+++ b/test/js-framework/case/basic/dynamic-type.output.js
@@ -0,0 +1,10 @@
+{
+  type: 'container',
+  children: [{
+    type: 'text',
+    style: {
+      width: 200,
+      height: 200
+    }
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/dynamic-type.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/dynamic-type.source.js b/test/js-framework/case/basic/dynamic-type.source.js
new file mode 100644
index 0000000..62b8f72
--- /dev/null
+++ b/test/js-framework/case/basic/dynamic-type.source.js
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+define('@weex-component/dynamic-type', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      type: 'text'
+    }}
+  }
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": function () {return this.type},
+      "style": {
+        "width": 200,
+        "height": 200
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/dynamic-type')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo.output.js b/test/js-framework/case/basic/foo.output.js
new file mode 100644
index 0000000..1372f91
--- /dev/null
+++ b/test/js-framework/case/basic/foo.output.js
@@ -0,0 +1,3 @@
+{
+    type: 'container'
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo.source.js b/test/js-framework/case/basic/foo.source.js
new file mode 100644
index 0000000..027eee1
--- /dev/null
+++ b/test/js-framework/case/basic/foo.source.js
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+define('@weex-component/foo', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {
+      return {
+        x: 1
+      }
+    }
+  }
+
+
+;module.exports.style = {
+  "a": {
+    "fontSize": 12
+  }
+}
+
+;module.exports.template = {
+  "type": "container"
+}
+
+;})
+
+
+bootstrap('@weex-component/foo')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo2.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo2.output.js b/test/js-framework/case/basic/foo2.output.js
new file mode 100644
index 0000000..d7cc321
--- /dev/null
+++ b/test/js-framework/case/basic/foo2.output.js
@@ -0,0 +1,10 @@
+{
+    type: 'container',
+    attr: {
+        a: '1',
+        b: 1
+    },
+    style: {
+        fontSize: 12
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo2.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo2.source.js b/test/js-framework/case/basic/foo2.source.js
new file mode 100644
index 0000000..cabf559
--- /dev/null
+++ b/test/js-framework/case/basic/foo2.source.js
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+define('@weex-component/foo2', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {
+      return {
+        x: 1
+      }
+    }
+  }
+
+
+;module.exports.style = {
+  "a": {
+    "fontSize": 12
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "attr": {
+    "a": "1",
+    "b": function () {return this.x}
+  },
+  "classList": [
+    "a"
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/foo2')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo3.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo3.output.js b/test/js-framework/case/basic/foo3.output.js
new file mode 100644
index 0000000..51c886e
--- /dev/null
+++ b/test/js-framework/case/basic/foo3.output.js
@@ -0,0 +1,9 @@
+{
+    type: 'container',
+    children: [{
+        type: 'text',
+        attr: {
+            value: 'Hello World'
+        }
+    }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo3.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo3.source.js b/test/js-framework/case/basic/foo3.source.js
new file mode 100644
index 0000000..7fda995
--- /dev/null
+++ b/test/js-framework/case/basic/foo3.source.js
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+define('@weex-component/foo3', function (require, exports, module) {
+
+;
+
+;module.exports.style = {}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "value": "Hello World"
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/foo3')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo4.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo4.output.js b/test/js-framework/case/basic/foo4.output.js
new file mode 100644
index 0000000..bef3668
--- /dev/null
+++ b/test/js-framework/case/basic/foo4.output.js
@@ -0,0 +1,12 @@
+{
+  type: 'container',
+  children: [{
+    type: 'text',
+    attr: {
+      value: 'Hello World'
+    },
+    style: {
+      color: '#ff0000'
+    }
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo4.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo4.source.js b/test/js-framework/case/basic/foo4.source.js
new file mode 100644
index 0000000..f842abf
--- /dev/null
+++ b/test/js-framework/case/basic/foo4.source.js
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+define('@weex-component/foo4', function (require, exports, module) {
+
+;
+
+;module.exports.style = {}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "style": {
+        "color": "#ff0000"
+      },
+      "attr": {
+        "value": "Hello World"
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/foo4')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo5.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo5.output.js b/test/js-framework/case/basic/foo5.output.js
new file mode 100644
index 0000000..840cefb
--- /dev/null
+++ b/test/js-framework/case/basic/foo5.output.js
@@ -0,0 +1,14 @@
+{
+  type: 'container',
+  children: [{
+    type: 'text',
+    attr: {
+      value: 'Hello World'
+    },
+    style: {
+      color: '#ff0000',
+      fontSize: 48,
+      fontWeight: 'bold'
+    }
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo5.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo5.source.js b/test/js-framework/case/basic/foo5.source.js
new file mode 100644
index 0000000..b09fc03
--- /dev/null
+++ b/test/js-framework/case/basic/foo5.source.js
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+define('@weex-component/foo5', function (require, exports, module) {
+
+;
+
+;module.exports.style = {
+  "title": {
+    "color": "#ff0000",
+    "fontSize": 48,
+    "fontWeight": "bold"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": "Hello World"
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/foo5')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo6.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo6.output.js b/test/js-framework/case/basic/foo6.output.js
new file mode 100644
index 0000000..1a8fd90
--- /dev/null
+++ b/test/js-framework/case/basic/foo6.output.js
@@ -0,0 +1,28 @@
+{
+  type: 'container',
+  style: {
+    flexDirection: 'row'
+  },
+  children: [{
+    type: 'image',
+    attr: {
+      src: 'http://gw.alicdn.com/tfscom/TB1OMfAJFXXXXbfXXXXqVMCNVXX-400-400.jpg'
+    },
+    style: {
+      width: 200,
+      height: 200
+    }
+  }, {
+    type: 'text',
+    attr: {
+      value: '一个超赞的宝贝标题'
+    },
+    style: {
+      flex: 1,
+      color: '#ff0000',
+      fontSize: 48,
+      fontWeight: 'bold',
+      backgroundColor: '#eeeeee'
+    }
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo6.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo6.source.js b/test/js-framework/case/basic/foo6.source.js
new file mode 100644
index 0000000..4301a50
--- /dev/null
+++ b/test/js-framework/case/basic/foo6.source.js
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+define('@weex-component/foo6', function (require, exports, module) {
+
+;
+
+;module.exports.style = {
+  "container": {
+    "flexDirection": "row"
+  },
+  "thumb": {
+    "width": 200,
+    "height": 200
+  },
+  "title": {
+    "flex": 1,
+    "color": "#ff0000",
+    "fontSize": 48,
+    "fontWeight": "bold",
+    "backgroundColor": "#eeeeee"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "classList": [
+    "container"
+  ],
+  "children": [
+    {
+      "type": "image",
+      "classList": [
+        "thumb"
+      ],
+      "attr": {
+        "src": "http://gw.alicdn.com/tfscom/TB1OMfAJFXXXXbfXXXXqVMCNVXX-400-400.jpg"
+      }
+    },
+    {
+      "type": "text",
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": "一个超赞的宝贝标题"
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/foo6')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo7.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo7.output.js b/test/js-framework/case/basic/foo7.output.js
new file mode 100644
index 0000000..d19c92f
--- /dev/null
+++ b/test/js-framework/case/basic/foo7.output.js
@@ -0,0 +1,26 @@
+{
+  type: 'container',
+  event: ['click'],
+  children: [{
+    type: 'image',
+    attr: {
+      src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
+    },
+    style: {
+      width: 200,
+      height: 200
+    }
+  }, {
+    type: 'text',
+    attr: {
+      value: '一个超赞的宝贝标题'
+    },
+    style: {
+      flex: 1,
+      color: '#ff0000',
+      fontSize: 48,
+      fontWeight: 'bold',
+      backgroundColor: '#eeeeee'
+    }
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo7.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo7.source.js b/test/js-framework/case/basic/foo7.source.js
new file mode 100644
index 0000000..2d9b43f
--- /dev/null
+++ b/test/js-framework/case/basic/foo7.source.js
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+define('@weex-component/foo7', function (require, exports, module) {
+
+;
+  module.exports = {
+    methods: {
+      gotoDetail: function () {
+        this.$openURL('https://item.taobao.com/item.htm?id=520421163634')
+      }
+    }
+  }
+
+
+;module.exports.style = {
+  "thumb": {
+    "width": 200,
+    "height": 200
+  },
+  "title": {
+    "flex": 1,
+    "color": "#ff0000",
+    "fontSize": 48,
+    "fontWeight": "bold",
+    "backgroundColor": "#eeeeee"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "events": {
+    "click": "gotoDetail"
+  },
+  "children": [
+    {
+      "type": "image",
+      "classList": [
+        "thumb"
+      ],
+      "attr": {
+        "src": "https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg"
+      }
+    },
+    {
+      "type": "text",
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": "一个超赞的宝贝标题"
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/foo7')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo8.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo8.output.js b/test/js-framework/case/basic/foo8.output.js
new file mode 100644
index 0000000..8ed4bb1
--- /dev/null
+++ b/test/js-framework/case/basic/foo8.output.js
@@ -0,0 +1,60 @@
+{
+  type: 'container',
+  children: [{
+    type: 'container',
+    style: {
+      flexDirection: 'row'
+    },
+    event: ['click'],
+    children: [{
+      type: 'image',
+      attr: {
+        src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
+      },
+      style: {
+        width: 200,
+        height: 200
+      }
+    }, {
+      type: 'text',
+      attr: {
+        value: 'title1'
+      },
+      style: {
+        flex: 1,
+        color: '#ff0000',
+        fontSize: 48,
+        fontWeight: 'bold',
+        backgroundColor: '#eeeeee'
+      }
+    }]
+  }, {
+    type: 'container',
+    style: {
+      flexDirection: 'row'
+    },
+    event: ['click'],
+    children: [{
+      type: 'image',
+      attr: {
+        src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
+      },
+      style: {
+        width: 200,
+        height: 200
+      }
+    }, {
+      type: 'text',
+      attr: {
+        value: 'title2'
+      },
+      style: {
+        flex: 1,
+        color: '#ff0000',
+        fontSize: 48,
+        fontWeight: 'bold',
+        backgroundColor: '#eeeeee'
+      }
+    }]
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo8.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo8.source.js b/test/js-framework/case/basic/foo8.source.js
new file mode 100644
index 0000000..d2512f2
--- /dev/null
+++ b/test/js-framework/case/basic/foo8.source.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.
+ */
+define('@weex-component/foo8', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      itemList: [
+        {itemId: '520421163634', title: 'title1', pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'},
+        {itemId: '522076777462', title: 'title2', pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'}
+      ]
+    }},
+    methods: {
+      gotoDetail: function () {
+        this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId)
+      }
+    }
+  }
+
+
+;module.exports.style = {
+  "thumb": {
+    "width": 200,
+    "height": 200
+  },
+  "title": {
+    "flex": 1,
+    "color": "#ff0000",
+    "fontSize": 48,
+    "fontWeight": "bold",
+    "backgroundColor": "#eeeeee"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "container",
+      "repeat": function () {return this.itemList},
+      "events": {
+        "click": "gotoDetail"
+      },
+      "style": {
+        "flexDirection": "row"
+      },
+      "children": [
+        {
+          "type": "image",
+          "classList": [
+            "thumb"
+          ],
+          "attr": {
+            "src": function () {return this.pictureUrl}
+          }
+        },
+        {
+          "type": "text",
+          "classList": [
+            "title"
+          ],
+          "attr": {
+            "value": function () {return this.title}
+          }
+        }
+      ]
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/foo8')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo9.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo9.output.js b/test/js-framework/case/basic/foo9.output.js
new file mode 100644
index 0000000..8f6d086
--- /dev/null
+++ b/test/js-framework/case/basic/foo9.output.js
@@ -0,0 +1,57 @@
+{
+  type: 'container',
+  style: {
+    flexDirection: 'column'
+  },
+  children: [{
+    type: 'container',
+    event: ['click'],
+    children: [{
+      type: 'image',
+      attr: {
+        src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
+      },
+      style: {
+        width: 200,
+        height: 200
+      }
+    }, {
+      type: 'text',
+      attr: {
+        value: 'title1'
+      },
+      style: {
+        flex: 1,
+        color: '#ff0000',
+        fontSize: 48,
+        fontWeight: 'bold',
+        backgroundColor: '#eeeeee'
+      }
+    }]
+  }, {
+    type: 'container',
+    event: ['click'],
+    children: [{
+      type: 'image',
+      attr: {
+        src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
+      },
+      style: {
+        width: 200,
+        height: 200
+      }
+    }, {
+      type: 'text',
+      attr: {
+        value: 'title2'
+      },
+      style: {
+        flex: 1,
+        color: '#ff0000',
+        fontSize: 48,
+        fontWeight: 'bold',
+        backgroundColor: '#eeeeee'
+      }
+    }]
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/foo9.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/foo9.source.js b/test/js-framework/case/basic/foo9.source.js
new file mode 100644
index 0000000..da534e9
--- /dev/null
+++ b/test/js-framework/case/basic/foo9.source.js
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+define('@weex-component/taobao-item', function (require, exports, module) {
+
+;
+    module.exports = {
+      data: function () {return {
+        itemId: '',
+        title: '',
+        pictureUrl: ''
+      }},
+      methods: {
+        gotoDetail: function () {
+          this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId)
+        }
+      }
+    }
+  
+
+;module.exports.style = {
+  "thumb": {
+    "width": 200,
+    "height": 200
+  },
+  "title": {
+    "flex": 1,
+    "color": "#ff0000",
+    "fontSize": 48,
+    "fontWeight": "bold",
+    "backgroundColor": "#eeeeee"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "events": {
+    "click": "gotoDetail"
+  },
+  "children": [
+    {
+      "type": "image",
+      "classList": [
+        "thumb"
+      ],
+      "attr": {
+        "src": function () {return this.pictureUrl}
+      }
+    },
+    {
+      "type": "text",
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": function () {return this.title}
+      }
+    }
+  ]
+}
+
+;})
+
+// module
+
+define('@weex-component/foo9', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      itemList: [
+        {itemId: '520421163634', title: 'title1', pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'},
+        {itemId: '522076777462', title: 'title2', pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'}
+      ]
+    }}
+  }
+
+
+;module.exports.style = {}
+
+;module.exports.template = {
+  "type": "container",
+  "style": {
+    "flexDirection": "column"
+  },
+  "children": [
+    {
+      "type": "taobao-item",
+      "repeat": function () {return this.itemList}
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/foo9')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/global-weex-object.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/global-weex-object.output.js b/test/js-framework/case/basic/global-weex-object.output.js
new file mode 100644
index 0000000..3e1fa19
--- /dev/null
+++ b/test/js-framework/case/basic/global-weex-object.output.js
@@ -0,0 +1,6 @@
+{
+    type: 'div',
+    attr: {
+        a: 'WeexDemo'
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/global-weex-object.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/global-weex-object.source.js b/test/js-framework/case/basic/global-weex-object.source.js
new file mode 100644
index 0000000..252af7e
--- /dev/null
+++ b/test/js-framework/case/basic/global-weex-object.source.js
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+weex.define('@weex-component/foo', function (require, exports, module) {
+  module.exports = {
+    data: function () {
+      return {
+        x: weex.config.env.appName
+      }
+    },
+    template: {
+      "type": "div",
+      "attr": {
+        "a": function () {
+          return this.x
+        }
+      }
+    }
+  }
+})
+
+weex.bootstrap('@weex-component/foo')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/id.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/id.output.js b/test/js-framework/case/basic/id.output.js
new file mode 100644
index 0000000..7ce073c
--- /dev/null
+++ b/test/js-framework/case/basic/id.output.js
@@ -0,0 +1,32 @@
+{
+  type: 'div',
+  children: [
+    {
+      type: 'text',
+      attr: {
+        value: 'Hello',
+        a: 1
+      }
+    },
+    {
+      type: 'text',
+      attr: {
+        value: 'Hello',
+        a: 1,
+        b: 1
+      }
+    },
+    {
+      type: 'text',
+      attr: {
+        value: 'Hello'
+      }
+    },
+    {
+      type: 'text',
+      attr: {
+        value: 'Hello'
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/id.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/id.source.js b/test/js-framework/case/basic/id.source.js
new file mode 100644
index 0000000..3c85886
--- /dev/null
+++ b/test/js-framework/case/basic/id.source.js
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+define('@weex-component/id', function (require, exports, module) {
+
+;
+  module.exports = {
+    ready: function () {
+      if (this.$el('x')) {
+        this.$el('x').setAttr('a', 1)
+      }
+      if (this.$el('y')) {
+        this.$el('y').setAttr('a', 1)
+      }
+      if (this.$el('')) {
+        this.$el('').setAttr('a', 1)
+      }
+      if (this.$el('0')) {
+        this.$el('0').setAttr('a', 1)
+      }
+      if (this.$el(0)) {
+        this.$el(0).setAttr('b', 1)
+      }
+    }
+  }
+
+;module.exports.template = {
+  "type": "div",
+  "children": [
+    {
+      "type": "text",
+      "id": "x",
+      "attr": {
+        "value": "Hello"
+      }
+    },
+    {
+      "type": "text",
+      "id": "0",
+      "attr": {
+        "value": "Hello"
+      }
+    },
+    {
+      "type": "text",
+      "id": '',
+      "attr": {
+        "value": "Hello"
+      }
+    },
+    {
+      "type": "text",
+      "id": null,
+      "attr": {
+        "value": "Hello"
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/id')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/if-repeat.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/if-repeat.output.js b/test/js-framework/case/basic/if-repeat.output.js
new file mode 100644
index 0000000..5265727
--- /dev/null
+++ b/test/js-framework/case/basic/if-repeat.output.js
@@ -0,0 +1,13 @@
+{
+  type: 'container',
+  children: [{
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: 'Hello World1'
+    }
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/if-repeat.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/if-repeat.source.js b/test/js-framework/case/basic/if-repeat.source.js
new file mode 100644
index 0000000..aa62931
--- /dev/null
+++ b/test/js-framework/case/basic/if-repeat.source.js
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+define('@weex-component/if-repeat', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      titlelist: [
+        {showTitle: true, title: 'Hello World1'},
+        {showTitle: false, title: 'Hello World2'}
+      ]
+    }}
+  }
+
+
+;module.exports.style = {
+  "title": {
+    "fontSize": 26,
+    "color": "#FF0000"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "repeat": function () {return this.titlelist},
+      "shown": function () {return this.showTitle},
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": function () {return this.title}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/if-repeat')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/if-root.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/if-root.output.js b/test/js-framework/case/basic/if-root.output.js
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/test/js-framework/case/basic/if-root.output.js
@@ -0,0 +1 @@
+{}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/if-root.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/if-root.source.js b/test/js-framework/case/basic/if-root.source.js
new file mode 100644
index 0000000..ca94316
--- /dev/null
+++ b/test/js-framework/case/basic/if-root.source.js
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+define('@weex-component/if-root', function (require, exports, module) {
+
+;module.exports = {
+  data: function () {return {
+	  visible: true,
+  }}
+}
+
+;module.exports.template = {
+  "type": "container",
+  "shown": function () {return this.visible},
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "value": "container"
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/if-root')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/if.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/if.output.js b/test/js-framework/case/basic/if.output.js
new file mode 100644
index 0000000..03dbeae
--- /dev/null
+++ b/test/js-framework/case/basic/if.output.js
@@ -0,0 +1,13 @@
+{
+  type: 'container',
+  children: [{
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: 'Hello World'
+    }
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/if.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/if.source.js b/test/js-framework/case/basic/if.source.js
new file mode 100644
index 0000000..3ebb80c
--- /dev/null
+++ b/test/js-framework/case/basic/if.source.js
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+define('@weex-component/if', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      showTitle: true,
+      title: 'Hello World'
+    }}
+  }
+
+
+;module.exports.style = {
+  "title": {
+    "fontSize": 26,
+    "color": "#FF0000"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "shown": function () {return this.showTitle},
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": function () {return this.title}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/if')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/promise.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/promise.output.js b/test/js-framework/case/basic/promise.output.js
new file mode 100644
index 0000000..07b17c9
--- /dev/null
+++ b/test/js-framework/case/basic/promise.output.js
@@ -0,0 +1,3 @@
+{
+    type: 'container'
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/promise.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/promise.source.js b/test/js-framework/case/basic/promise.source.js
new file mode 100644
index 0000000..67554ba
--- /dev/null
+++ b/test/js-framework/case/basic/promise.source.js
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+define('@weex-component/foo', function (require, exports, module) {
+new Promise(function (fulfill, reject){
+  fulfill(1)
+})
+Promise.all([Promise.resolve(1)])
+;
+  module.exports = {
+    data: function () {
+      return {
+        x: 1
+      }
+    }
+  }
+
+
+;module.exports.style = {
+  "a": {
+    "fontSize": 12
+  }
+}
+
+;module.exports.template = {
+  "type": "container"
+}
+
+;})
+
+
+bootstrap('@weex-component/foo')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/ready.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/ready.output.js b/test/js-framework/case/basic/ready.output.js
new file mode 100644
index 0000000..39c15f3
--- /dev/null
+++ b/test/js-framework/case/basic/ready.output.js
@@ -0,0 +1,11 @@
+{
+  "children": [
+    {
+      "attr": {
+        "value": "1"
+      },
+      "type": "text"
+    }
+  ],
+  "type": "container"
+}
\ No newline at end of file



[10/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/api/component.js
----------------------------------------------------------------------
diff --git a/runtime/api/component.js b/runtime/api/component.js
new file mode 100644
index 0000000..8a56961
--- /dev/null
+++ b/runtime/api/component.js
@@ -0,0 +1,51 @@
+/*
+ * 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 { registerElement } from '../vdom/WeexElement'
+
+const weexComponents = {}
+
+/**
+ * Register native components information.
+ * @param {array} newComponents
+ */
+export function registerComponents (newComponents) {
+  if (Array.isArray(newComponents)) {
+    newComponents.forEach(component => {
+      if (!component) {
+        return
+      }
+      if (typeof component === 'string') {
+        weexComponents[component] = true
+      }
+      else if (typeof component === 'object' && typeof component.type === 'string') {
+        weexComponents[component.type] = component
+        registerElement(component.type, component.methods)
+      }
+    })
+  }
+}
+
+/**
+ * Check whether the component has been registered.
+ * @param {String} component name
+ */
+export function isRegisteredComponent (name) {
+  return !!weexComponents[name]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/api/config.js
----------------------------------------------------------------------
diff --git a/runtime/api/config.js b/runtime/api/config.js
new file mode 100644
index 0000000..d212f3d
--- /dev/null
+++ b/runtime/api/config.js
@@ -0,0 +1,37 @@
+/*
+ * 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/51dd277e/runtime/api/index.js
----------------------------------------------------------------------
diff --git a/runtime/api/index.js b/runtime/api/index.js
new file mode 100644
index 0000000..a024db7
--- /dev/null
+++ b/runtime/api/index.js
@@ -0,0 +1,40 @@
+/*
+ * 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 from './init'
+import config from './config'
+import { register, unregister, has } from './service'
+
+/* istanbul ignore next */
+function freezePrototype () {
+  // Object.freeze(config.Element)
+  Object.freeze(config.Comment)
+  Object.freeze(config.Listener)
+  Object.freeze(config.Document.prototype)
+  // Object.freeze(config.Element.prototype)
+  Object.freeze(config.Comment.prototype)
+  Object.freeze(config.Listener.prototype)
+}
+
+export default {
+  service: { register, unregister, has },
+  freezePrototype,
+  init,
+  config
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/api/init.js
----------------------------------------------------------------------
diff --git a/runtime/api/init.js b/runtime/api/init.js
new file mode 100644
index 0000000..187504a
--- /dev/null
+++ b/runtime/api/init.js
@@ -0,0 +1,304 @@
+/*
+ * 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 { receiveTasks } from '../bridge/receiver'
+import { registerModules } from './module'
+import { registerComponents } from './component'
+import { services, register, unregister } from './service'
+import { track } from '../bridge/debug'
+import WeexInstance from './WeexInstance'
+import { getDoc } from '../vdom/operation'
+
+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 getBundleType (code) {
+  const result = versionRegExp.exec(code)
+  if (result) {
+    try {
+      const info = JSON.parse(result[1])
+      return info.framework
+    }
+    catch (e) {}
+  }
+
+  // default bundle type
+  return 'Weex'
+}
+
+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 instanceTypeMap = {}
+function getFrameworkType (id) {
+  return instanceTypeMap[id]
+}
+
+function createInstanceContext (id, options = {}, data) {
+  const weex = new WeexInstance(id, options)
+  Object.freeze(weex)
+
+  const bundleType = options.bundleType || 'Vue'
+  instanceTypeMap[id] = bundleType
+  const framework = runtimeConfig.frameworks[bundleType]
+  if (!framework) {
+    return new Error(`[JS Framework] Invalid bundle type "${bundleType}".`)
+  }
+  track(id, 'bundleType', bundleType)
+
+  // prepare js service
+  const services = createServices(id, { weex, bundleType }, runtimeConfig)
+  Object.freeze(services)
+
+  // prepare runtime context
+  const runtimeContext = Object.create(null)
+  Object.assign(runtimeContext, services, {
+    weex,
+    services // Temporary compatible with some legacy APIs in Rax
+  })
+  Object.freeze(runtimeContext)
+
+  // prepare instance context
+  const instanceContext = Object.create(runtimeContext)
+  if (typeof framework.createInstanceContext === 'function') {
+    Object.assign(instanceContext, framework.createInstanceContext(id, runtimeContext, data))
+  }
+  Object.freeze(instanceContext)
+  return instanceContext
+}
+
+/**
+ * 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) {
+  if (instanceTypeMap[id]) {
+    return new Error(`The instance id "${id}" has already been used!`)
+  }
+
+  // Init instance info.
+  const bundleType = getBundleType(code)
+  instanceTypeMap[id] = bundleType
+
+  // Init instance config.
+  config = JSON.parse(JSON.stringify(config || {}))
+  config.env = JSON.parse(JSON.stringify(global.WXEnvironment || {}))
+  config.bundleType = bundleType
+
+  const framework = runtimeConfig.frameworks[bundleType]
+  if (!framework) {
+    return new Error(`[JS Framework] Invalid bundle type "${bundleType}".`)
+  }
+  if (bundleType === 'Weex') {
+    console.error(`[JS Framework] COMPATIBILITY WARNING: `
+      + `Weex DSL 1.0 (.we) framework is no longer supported! `
+      + `It will be removed in the next version of WeexSDK, `
+      + `your page would be crash if you still using the ".we" framework. `
+      + `Please upgrade it to Vue.js or Rax.`)
+  }
+
+  const instanceContext = createInstanceContext(id, config, data)
+  if (typeof framework.createInstance === 'function') {
+    // Temporary compatible with some legacy APIs in Rax,
+    // some Rax page is using the legacy ".we" framework.
+    if (bundleType === 'Rax' || bundleType === 'Weex') {
+      const raxInstanceContext = Object.assign({
+        config,
+        created: Date.now(),
+        framework: bundleType
+      }, instanceContext)
+      return framework.createInstance(id, code, config, data, raxInstanceContext)
+    }
+    return framework.createInstance(id, code, config, data, instanceContext)
+  }
+  // console.error(`[JS Framework] Can't find available "createInstance" method in ${bundleType}!`)
+  runInContext(code, instanceContext)
+}
+
+/**
+ * Run js code in a specific context.
+ * @param {string} code
+ * @param {object} context
+ */
+function runInContext (code, context) {
+  const keys = []
+  const args = []
+  for (const key in context) {
+    keys.push(key)
+    args.push(context[key])
+  }
+
+  const bundle = `
+    (function (global) {
+      ${code}
+    })(Object.create(this))
+  `
+
+  return (new Function(...keys, bundle))(...args)
+}
+
+/**
+ * Get the JSON object of the root element.
+ * @param {string} instanceId
+ */
+function getRoot (instanceId) {
+  const document = getDoc(instanceId)
+  try {
+    if (document && document.body) {
+      return document.body.toJSON()
+    }
+  }
+  catch (e) {
+    console.error(`[JS Framework] Failed to get the virtual dom tree.`)
+    return
+  }
+}
+
+const methods = {
+  createInstance,
+  createInstanceContext,
+  getRoot,
+  getDocument: getDoc,
+  registerService: register,
+  unregisterService: unregister,
+  callJS (id, tasks) {
+    const framework = frameworks[getFrameworkType(id)]
+    if (framework && typeof framework.receiveTasks === 'function') {
+      return framework.receiveTasks(id, tasks)
+    }
+    return receiveTasks(id, tasks)
+  }
+}
+
+/**
+ * Register methods which will be called for each instance.
+ * @param {string} methodName
+ */
+function genInstance (methodName) {
+  methods[methodName] = function (...args) {
+    const id = args[0]
+    const type = getFrameworkType(id)
+    if (type && frameworks[type]) {
+      const result = frameworks[type][methodName](...args)
+      const info = { framework: type }
+
+      // 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 instanceTypeMap[id]
+      }
+
+      return result
+    }
+    return new Error(`[JS Framework] Using invalid instance id `
+      + `"${id}" when calling ${methodName}.`)
+  }
+}
+
+/**
+ * Register methods which init each frameworks.
+ * @param {string} methodName
+ * @param {function} sharedMethod
+ */
+function adaptMethod (methodName, sharedMethod) {
+  methods[methodName] = function (...args) {
+    if (typeof sharedMethod === 'function') {
+      sharedMethod(...args)
+    }
+
+    // TODO: deprecated
+    for (const name in runtimeConfig.frameworks) {
+      const framework = runtimeConfig.frameworks[name]
+      if (framework && framework[methodName]) {
+        framework[methodName](...args)
+      }
+    }
+  }
+}
+
+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]
+    if (typeof framework.init === 'function') {
+      try {
+        framework.init(config)
+      }
+      catch (e) {}
+    }
+  }
+
+  adaptMethod('registerComponents', registerComponents)
+  adaptMethod('registerModules', registerModules)
+  adaptMethod('registerMethods')
+
+  ; ['destroyInstance', 'refreshInstance'].forEach(genInstance)
+
+  return methods
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/api/module.js
----------------------------------------------------------------------
diff --git a/runtime/api/module.js b/runtime/api/module.js
new file mode 100644
index 0000000..df26b92
--- /dev/null
+++ b/runtime/api/module.js
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+const weexModules = {}
+
+/**
+ * Register native modules information.
+ * @param {object} newModules
+ */
+export function registerModules (newModules) {
+  for (const name in newModules) {
+    if (!weexModules[name]) {
+      weexModules[name] = {}
+    }
+    newModules[name].forEach(method => {
+      if (typeof method === 'string') {
+        weexModules[name][method] = true
+      }
+      else {
+        weexModules[name][method.name] = method.args
+      }
+    })
+  }
+}
+
+/**
+ * Check whether the module or the method has been registered.
+ * @param {String} module name
+ * @param {String} method name (optional)
+ */
+export function isRegisteredModule (name, method) {
+  if (typeof method === 'string') {
+    return !!(weexModules[name] && weexModules[name][method])
+  }
+  return !!weexModules[name]
+}
+
+export function getModuleDescription (name) {
+  return weexModules[name]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/api/service.js
----------------------------------------------------------------------
diff --git a/runtime/api/service.js b/runtime/api/service.js
new file mode 100644
index 0000000..6f2e72e
--- /dev/null
+++ b/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/51dd277e/runtime/bridge/CallbackManager.js
----------------------------------------------------------------------
diff --git a/runtime/bridge/CallbackManager.js b/runtime/bridge/CallbackManager.js
new file mode 100644
index 0000000..70bd729
--- /dev/null
+++ b/runtime/bridge/CallbackManager.js
@@ -0,0 +1,89 @@
+/*
+ * 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'
+
+function getHookKey (componentId, type, hookName) {
+  return `${type}@${hookName}#${componentId}`
+}
+
+/**
+ * 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 = String(instanceId)
+    this.lastCallbackId = 0
+    this.callbacks = {}
+    this.hooks = {}
+  }
+  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
+  }
+  registerHook (componentId, type, hookName, hookFunction) {
+    // TODO: validate arguments
+    const key = getHookKey(componentId, type, hookName)
+    if (this.hooks[key]) {
+      console.warn(`[JS Framework] Override an existing component hook "${key}".`)
+    }
+    this.hooks[key] = hookFunction
+  }
+  triggerHook (componentId, type, hookName, options = {}) {
+    // TODO: validate arguments
+    const key = getHookKey(componentId, type, hookName)
+    const hookFunction = this.hooks[key]
+    if (typeof hookFunction !== 'function') {
+      console.error(`[JS Framework] Invalid hook function type (${typeof hookFunction}) on "${key}".`)
+      return null
+    }
+    let result = null
+    try {
+      result = hookFunction.apply(null, options.args || [])
+    }
+    catch (e) {
+      console.error(`[JS Framework] Failed to execute the hook function on "${key}".`)
+    }
+    return result
+  }
+  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 = {}
+    this.hooks = {}
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/bridge/Handler.js
----------------------------------------------------------------------
diff --git a/runtime/bridge/Handler.js b/runtime/bridge/Handler.js
new file mode 100644
index 0000000..71bf4c5
--- /dev/null
+++ b/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/51dd277e/runtime/bridge/Listener.js
----------------------------------------------------------------------
diff --git a/runtime/bridge/Listener.js b/runtime/bridge/Listener.js
new file mode 100644
index 0000000..2bf3893
--- /dev/null
+++ b/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/51dd277e/runtime/bridge/TaskCenter.js
----------------------------------------------------------------------
diff --git a/runtime/bridge/TaskCenter.js b/runtime/bridge/TaskCenter.js
new file mode 100644
index 0000000..c2652c0
--- /dev/null
+++ b/runtime/bridge/TaskCenter.js
@@ -0,0 +1,145 @@
+/*
+ * 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 '../shared/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: String(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)
+  }
+
+  registerHook (...args) {
+    return this.callbackManager.registerHook(...args)
+  }
+
+  triggerHook (...args) {
+    return this.callbackManager.triggerHook(...args)
+  }
+
+  updateData (componentId, newData, callback) {
+    this.send('module', {
+      module: 'dom',
+      method: 'updateComponentData'
+    }, [componentId, newData, callback])
+  }
+
+  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 && v instanceof Element) {
+      return v.ref
+    }
+    if (v && 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/51dd277e/runtime/bridge/debug.js
----------------------------------------------------------------------
diff --git a/runtime/bridge/debug.js b/runtime/bridge/debug.js
new file mode 100644
index 0000000..6e24c6a
--- /dev/null
+++ b/runtime/bridge/debug.js
@@ -0,0 +1,65 @@
+/*
+ * 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 { getTaskCenter } from '../vdom/operation'
+import { isRegisteredModule } from '../api/module'
+
+export function track (id, type, value) {
+  const taskCenter = getTaskCenter(id)
+  if (!taskCenter || typeof taskCenter.send !== 'function') {
+    console.error(`[JS Framework] Failed to create tracker!`)
+    return
+  }
+  if (!type || !value) {
+    console.warn(`[JS Framework] Invalid track type (${type}) or value (${value})`)
+    return
+  }
+  const label = `jsfm.${type}.${value}`
+  try {
+    if (isRegisteredModule('userTrack', 'addPerfPoint')) {
+      const message = Object.create(null)
+      message[label] = '4'
+      taskCenter.send('module', {
+        module: 'userTrack',
+        method: 'addPerfPoint'
+      }, [message])
+    }
+  }
+  catch (err) {
+    console.error(`[JS Framework] Failed to trace "${label}"!`)
+  }
+}
+
+export function error (...messages) {
+  if (typeof console.error === 'function') {
+    console.error(`[JS Framework] `, ...messages)
+  }
+}
+
+export function handleException (err) {
+  if (typeof console.error === 'function') {
+    try {
+      console.error(err.toString())
+    }
+    catch (e) {}
+  }
+  if (process.env.NODE_ENV !== 'production') {
+    throw err
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/bridge/normalize.js
----------------------------------------------------------------------
diff --git a/runtime/bridge/normalize.js b/runtime/bridge/normalize.js
new file mode 100644
index 0000000..4c6a058
--- /dev/null
+++ b/runtime/bridge/normalize.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.
+ */
+
+import { typof, bufferToBase64, base64ToBuffer } from '../shared/utils'
+
+/**
+ * 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/51dd277e/runtime/bridge/receiver.js
----------------------------------------------------------------------
diff --git a/runtime/bridge/receiver.js b/runtime/bridge/receiver.js
new file mode 100644
index 0000000..e6dea06
--- /dev/null
+++ b/runtime/bridge/receiver.js
@@ -0,0 +1,71 @@
+/*
+ * 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 { getDoc } from '../vdom/operation'
+
+function fireEvent (document, nodeId, type, event, domChanges, params) {
+  const el = document.getRef(nodeId)
+  if (el) {
+    return document.fireEvent(el, type, event, domChanges, params)
+  }
+  return new Error(`invalid element reference "${nodeId}"`)
+}
+
+function callback (document, callbackId, data, ifKeepAlive) {
+  return document.taskCenter.callback(callbackId, data, ifKeepAlive)
+}
+
+function componentHook (document, componentId, type, hook, options) {
+  if (!document || !document.taskCenter) {
+    console.error(`[JS Framework] Can't find "document" or "taskCenter".`)
+    return null
+  }
+  let result = null
+  try {
+    result = document.taskCenter.triggerHook(componentId, type, hook, options)
+  }
+  catch (e) {
+    console.error(`[JS Framework] Failed to trigger the "${type}@${hook}" hook on ${componentId}.`)
+  }
+  return result
+}
+
+/**
+ * Accept calls from native (event or callback).
+ *
+ * @param  {string} id
+ * @param  {array} tasks list with `method` and `args`
+ */
+export function receiveTasks (id, tasks) {
+  const document = getDoc(id)
+  if (!document) {
+    return new Error(`[JS Framework] Failed to receiveTasks, `
+      + `instance (${id}) is not available.`)
+  }
+  if (Array.isArray(tasks)) {
+    return tasks.map(task => {
+      switch (task.method) {
+        case 'callback': return callback(document, ...task.args)
+        case 'fireEventSync':
+        case 'fireEvent': return fireEvent(document, ...task.args)
+        case 'componentHook': return componentHook(document, ...task.args)
+      }
+    })
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/entries/index.js
----------------------------------------------------------------------
diff --git a/runtime/entries/index.js b/runtime/entries/index.js
new file mode 100644
index 0000000..9d590b6
--- /dev/null
+++ b/runtime/entries/index.js
@@ -0,0 +1,23 @@
+/*
+ * 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 setup from './setup'
+import frameworks from '../frameworks'
+
+setup(frameworks)

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/entries/rax.js
----------------------------------------------------------------------
diff --git a/runtime/entries/rax.js b/runtime/entries/rax.js
new file mode 100644
index 0000000..2aa17fd
--- /dev/null
+++ b/runtime/entries/rax.js
@@ -0,0 +1,23 @@
+/*
+ * 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 setup from './setup'
+import * as Rax from 'weex-rax-framework'
+
+setup({ Rax })

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/entries/setup.js
----------------------------------------------------------------------
diff --git a/runtime/entries/setup.js b/runtime/entries/setup.js
new file mode 100644
index 0000000..7c59712
--- /dev/null
+++ b/runtime/entries/setup.js
@@ -0,0 +1,60 @@
+/*
+ * 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 { subversion } from '../../package.json'
+import * as shared from '../shared'
+import runtime from '../api'
+import services from '../services'
+
+/**
+ * Setup frameworks with runtime.
+ * You can package more frameworks by
+ *  passing them as arguments.
+ */
+export default function (frameworks) {
+  const { init, config } = runtime
+  config.frameworks = frameworks
+  const { native, transformer } = subversion
+
+  for (const serviceName in services) {
+    runtime.service.register(serviceName, services[serviceName])
+  }
+
+  shared.freezePrototype()
+  shared.setNativeConsole()
+  runtime.freezePrototype()
+
+  // register framework meta info
+  global.frameworkVersion = native
+  global.transformerVersion = transformer
+
+  // init frameworks
+  const globalMethods = init(config)
+
+  // set global methods
+  for (const methodName in globalMethods) {
+    global[methodName] = (...args) => {
+      const ret = globalMethods[methodName](...args)
+      if (ret instanceof Error) {
+        console.error(ret.toString())
+      }
+      return ret
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/entries/vue.js
----------------------------------------------------------------------
diff --git a/runtime/entries/vue.js b/runtime/entries/vue.js
new file mode 100644
index 0000000..3cc816b
--- /dev/null
+++ b/runtime/entries/vue.js
@@ -0,0 +1,23 @@
+/*
+ * 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 setup from './setup'
+import * as Vue from 'weex-vue-framework'
+
+setup({ Vue })

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/index.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/index.js b/runtime/frameworks/index.js
new file mode 100644
index 0000000..8880f75
--- /dev/null
+++ b/runtime/frameworks/index.js
@@ -0,0 +1,29 @@
+/*
+ * 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 * as Vanilla from './vanilla/index'
+import * as Vue from 'weex-vue-framework'
+import * as Weex from './legacy/index'
+import Rax from 'weex-rax-framework'
+
+export default {
+  Vanilla,
+  Vue,
+  Rax,
+  Weex
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/api/methods.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/api/methods.js b/runtime/frameworks/legacy/api/methods.js
new file mode 100644
index 0000000..19f1cdc
--- /dev/null
+++ b/runtime/frameworks/legacy/api/methods.js
@@ -0,0 +1,194 @@
+/*
+ * 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 The api for invoking with "$" prefix
+ */
+
+/**
+ * @deprecated use $vm instead
+ * find the vm by id
+ * Note: there is only one id in whole component
+ * @param  {string} id
+ * @return {Vm}
+ */
+export function $ (id) {
+  console.warn('[JS Framework] Vm#$ is deprecated, please use Vm#$vm instead')
+  const info = this._ids[id]
+  if (info) {
+    return info.vm
+  }
+}
+
+/**
+ * find the element by id
+ * Note: there is only one id in whole component
+ * @param  {string} id
+ * @return {Element}
+ */
+export function $el (id) {
+  const info = this._ids[id]
+  if (info) {
+    return info.el
+  }
+}
+
+/**
+ * find the vm of the custom component by id
+ * Note: there is only one id in whole component
+ * @param  {string} id
+ * @return {Vm}
+ */
+export function $vm (id) {
+  const info = this._ids[id]
+  if (info) {
+    return info.vm
+  }
+}
+
+/**
+ * Fire when differ rendering finished
+ *
+ * @param  {Function} fn
+ */
+export function $renderThen (fn) {
+  const app = this._app
+  const differ = app.differ
+  return differ.then(() => {
+    fn()
+  })
+}
+
+/**
+ * scroll an element specified by id into view,
+ * moreover specify a number of offset optionally
+ * @param  {string} id
+ * @param  {number} offset
+ */
+export function $scrollTo (id, offset) {
+  console.warn('[JS Framework] Vm#$scrollTo is deprecated, ' +
+          'please use "require(\'@weex-module/dom\')' +
+          '.scrollTo(el, options)" instead')
+  const el = this.$el(id)
+  if (el) {
+    const dom = this._app.requireModule('dom')
+    dom.scrollToElement(el.ref, { offset: offset })
+  }
+}
+
+/**
+ * perform transition animation on an element specified by id
+ * @param  {string}   id
+ * @param  {object}   options
+ * @param  {object}   options.styles
+ * @param  {object}   options.duration(ms)
+ * @param  {object}   [options.timingFunction]
+ * @param  {object}   [options.delay=0(ms)]
+ * @param  {Function} callback
+ */
+export function $transition (id, options, callback) {
+  const el = this.$el(id)
+  if (el && options && options.styles) {
+    const animation = this._app.requireModule('animation')
+    animation.transition(el.ref, options, (...args) => {
+      this._setStyle(el, options.styles)
+      callback && callback(...args)
+    })
+  }
+}
+
+/**
+ * get some config
+ * @return {object} some config for app instance
+ * @property {string} bundleUrl
+ * @property {boolean} debug
+ * @property {object} env
+ * @property {string} env.weexVersion(ex. 1.0.0)
+ * @property {string} env.appName(ex. TB/TM)
+ * @property {string} env.appVersion(ex. 5.0.0)
+ * @property {string} env.platform(ex. iOS/Android)
+ * @property {string} env.osVersion(ex. 7.0.0)
+ * @property {string} env.deviceModel **native only**
+ * @property {number} env.[deviceWidth=750]
+ * @property {number} env.deviceHeight
+ */
+export function $getConfig (callback) {
+  const config = this._app.options
+  if (typeof callback === 'function') {
+    console.warn('[JS Framework] the callback of Vm#$getConfig(callback) is deprecated, ' +
+      'this api now can directly RETURN config info.')
+    callback(config)
+  }
+  return config
+}
+
+/**
+ * @deprecated
+ * request network via http protocol
+ * @param  {object}   params
+ * @param  {Function} callback
+ */
+export function $sendHttp (params, callback) {
+  console.warn('[JS Framework] Vm#$sendHttp is deprecated, ' +
+          'please use "require(\'@weex-module/stream\')' +
+          '.sendHttp(params, callback)" instead')
+  const stream = this._app.requireModule('stream')
+  stream.sendHttp(params, callback)
+}
+
+/**
+ * @deprecated
+ * open a url
+ * @param  {string} url
+ */
+export function $openURL (url) {
+  console.warn('[JS Framework] Vm#$openURL is deprecated, ' +
+          'please use "require(\'@weex-module/event\')' +
+          '.openURL(url)" instead')
+  const event = this._app.requireModule('event')
+  event.openURL(url)
+}
+
+/**
+ * @deprecated
+ * set a title for page
+ * @param  {string} title
+ */
+export function $setTitle (title) {
+  console.warn('[JS Framework] Vm#$setTitle is deprecated, ' +
+          'please use "require(\'@weex-module/pageInfo\')' +
+          '.setTitle(title)" instead')
+  const pageInfo = this._app.requireModule('pageInfo')
+  pageInfo.setTitle(title)
+}
+
+/**
+ * @deprecated use "require('@weex-module/moduleName') instead"
+ * invoke a native method by specifing the name of module and method
+ * @param  {string} moduleName
+ * @param  {string} methodName
+ * @param  {...*} the rest arguments
+ */
+export function $call (moduleName, methodName, ...args) {
+  console.warn('[JS Framework] Vm#$call is deprecated, ' +
+    'please use "require(\'@weex-module/moduleName\')" instead')
+  const module = this._app.requireModule(moduleName)
+  if (module && module[methodName]) {
+    module[methodName](...args)
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/api/modules.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/api/modules.js b/runtime/frameworks/legacy/api/modules.js
new file mode 100644
index 0000000..60123b0
--- /dev/null
+++ b/runtime/frameworks/legacy/api/modules.js
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+/**
+ * @deprecated after native register these modules
+ */
+
+/**
+ * @module dom
+ * @type {Array}
+ */
+export const dom = [
+  /**
+   * @method scrollToElement
+   * @param {string} ref
+   * @param {object} options
+   * @param {number} options.offset
+   */
+  {
+    name: 'scrollToElement',
+    args: ['string', 'object']
+  }
+]
+
+/**
+ * @module stream
+ */
+export const stream = [
+  /**
+   * @method sendHttp
+   * @param  {object} params
+   * @param  {string} params.url
+   * @param  {string} [params.method]
+   * @param  {object} [params.header]
+   * @param  {object} params.body
+   * @param  {function} callback
+   */
+  {
+    name: 'sendHttp',
+    args: ['object', 'function']
+  }
+]
+
+/**
+ * @module event
+ */
+export const event = [
+  /**
+   * @method openURL
+   * @param {string} url
+   */
+  {
+    name: 'openURL',
+    args: ['string']
+  }
+]
+
+/**
+ * @module pageInfo
+ */
+export const pageInfo = [
+  /**
+   * @method setTitle
+   * @param {string} text
+   */
+  {
+    name: 'setTitle',
+    args: ['string']
+  }
+]
+
+/**
+ * @module animation
+ */
+export const animation = [
+
+  {
+    name: 'transition',
+    args: ['string', 'object', 'function']
+  }
+]

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/app/bundle/bootstrap.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/app/bundle/bootstrap.js b/runtime/frameworks/legacy/app/bundle/bootstrap.js
new file mode 100644
index 0000000..8821873
--- /dev/null
+++ b/runtime/frameworks/legacy/app/bundle/bootstrap.js
@@ -0,0 +1,90 @@
+/*
+ * 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 semver from 'semver'
+import Vm from '../../vm/index'
+import * as downgrade from '../downgrade'
+import { setViewport } from '../viewport'
+import {
+  requireCustomComponent
+} from '../register'
+import {
+  isPlainObject,
+  isWeexComponent,
+  isNpmModule,
+  removeWeexPrefix,
+  removeJSSurfix
+} from '../../util/index'
+
+/**
+ * bootstrap app from a certain custom component with config & data
+ */
+export function bootstrap (app, name, config, data) {
+  console.debug(`[JS Framework] bootstrap for ${name}`)
+
+  // 1. validate custom component name first
+  let cleanName
+  if (isWeexComponent(name)) {
+    cleanName = removeWeexPrefix(name)
+  }
+  else if (isNpmModule(name)) {
+    cleanName = removeJSSurfix(name)
+    // check if define by old 'define' method
+    /* istanbul ignore if */
+    if (!requireCustomComponent(app, cleanName)) {
+      return new Error(`It's not a component: ${name}`)
+    }
+  }
+  else {
+    return new Error(`Wrong component name: ${name}`)
+  }
+
+  // 2. validate configuration
+  config = isPlainObject(config) ? config : {}
+  // 2.1 transformer version check
+  if (typeof config.transformerVersion === 'string' &&
+    typeof global.transformerVersion === 'string' &&
+    !semver.satisfies(config.transformerVersion,
+      global.transformerVersion)) {
+    return new Error(`JS Bundle version: ${config.transformerVersion} ` +
+      `not compatible with ${global.transformerVersion}`)
+  }
+  // 2.2 downgrade version check
+  const downgradeResult = downgrade.check(config.downgrade)
+  /* istanbul ignore if */
+  if (downgradeResult.isDowngrade) {
+    app.callTasks([{
+      module: 'instanceWrap',
+      method: 'error',
+      args: [
+        downgradeResult.errorType,
+        downgradeResult.code,
+        downgradeResult.errorMessage
+      ]
+    }])
+    return new Error(`Downgrade[${downgradeResult.code}]: ${downgradeResult.errorMessage}`)
+  }
+
+  // set viewport
+  if (config.viewport) {
+    setViewport(app, config.viewport)
+  }
+
+  // 3. create a new Vm with custom component name and data
+  app.vm = new Vm(cleanName, null, { _app: app }, null, data)
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/app/bundle/define.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/app/bundle/define.js b/runtime/frameworks/legacy/app/bundle/define.js
new file mode 100644
index 0000000..aec30f7
--- /dev/null
+++ b/runtime/frameworks/legacy/app/bundle/define.js
@@ -0,0 +1,115 @@
+/*
+ * 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 {
+  isWeexComponent,
+  isWeexModule,
+  isNormalModule,
+  isNpmModule,
+  removeWeexPrefix,
+  removeJSSurfix
+} from '../../util/index'
+import {
+  registerCustomComponent,
+  requireCustomComponent,
+  initModules
+} from '../register'
+
+/**
+ * define(name, factory) for primary usage
+ * or
+ * define(name, deps, factory) for compatibility
+ * Notice: DO NOT use function define() {},
+ * it will cause error after builded by webpack
+ */
+export const defineFn = function (app, name, ...args) {
+  console.debug(`[JS Framework] define a component ${name}`)
+
+  // adapt args:
+  // 1. name, deps[], factory()
+  // 2. name, factory()
+  // 3. name, definition{}
+  let factory, definition
+  if (args.length > 1) {
+    definition = args[1]
+  }
+  else {
+    definition = args[0]
+  }
+  if (typeof definition === 'function') {
+    factory = definition
+    definition = null
+  }
+
+  // resolve definition from factory
+  if (factory) {
+    const r = (name) => {
+      if (isWeexComponent(name)) {
+        const cleanName = removeWeexPrefix(name)
+        return requireCustomComponent(app, cleanName)
+      }
+      if (isWeexModule(name)) {
+        const cleanName = removeWeexPrefix(name)
+        return app.requireModule(cleanName)
+      }
+      if (isNormalModule(name) || isNpmModule(name)) {
+        const cleanName = removeJSSurfix(name)
+        return app.commonModules[cleanName]
+      }
+    }
+    const m = { exports: {}}
+    factory(r, m.exports, m)
+    definition = m.exports
+  }
+
+  // apply definition
+  if (isWeexComponent(name)) {
+    const cleanName = removeWeexPrefix(name)
+    registerCustomComponent(app, cleanName, definition)
+  }
+  else if (isWeexModule(name)) {
+    const cleanName = removeWeexPrefix(name)
+    initModules({ [cleanName]: definition })
+  }
+  else if (isNormalModule(name)) {
+    const cleanName = removeJSSurfix(name)
+    app.commonModules[cleanName] = definition
+  }
+  else if (isNpmModule(name)) {
+    const cleanName = removeJSSurfix(name)
+    if (definition.template ||
+        definition.style ||
+        definition.methods) {
+      // downgrade to old define method (define('componentName', factory))
+      // the exports contain one key of template, style or methods
+      // but it has risk!!!
+      registerCustomComponent(app, cleanName, definition)
+    }
+    else {
+      app.commonModules[cleanName] = definition
+    }
+  }
+}
+
+/**
+ * @deprecated
+ */
+export function register (app, type, options) {
+  console.warn('[JS Framework] Register is deprecated, please install lastest transformer.')
+  registerCustomComponent(app, type, options)
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/app/bundle/index.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/app/bundle/index.js b/runtime/frameworks/legacy/app/bundle/index.js
new file mode 100644
index 0000000..40126cf
--- /dev/null
+++ b/runtime/frameworks/legacy/app/bundle/index.js
@@ -0,0 +1,34 @@
+/*
+ * 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
+ * api that invoked by js bundle code
+ *
+ * - define(name, factory): define a new composed component type
+ * - bootstrap(type, config, data): require a certain type &
+ *         render with (optional) data
+ *
+ * deprecated:
+ * - register(type, options): register a new composed component type
+ * - render(type, data): render by a certain type with (optional) data
+ * - require(type)(data): require a type then render with data
+ */
+
+export { bootstrap } from './bootstrap'
+export { defineFn, register } from './define'

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/app/ctrl/index.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/app/ctrl/index.js b/runtime/frameworks/legacy/app/ctrl/index.js
new file mode 100644
index 0000000..99fb3a7
--- /dev/null
+++ b/runtime/frameworks/legacy/app/ctrl/index.js
@@ -0,0 +1,40 @@
+/*
+ * 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
+ * instance controls from native
+ *
+ * - init bundle
+ * - fire event
+ * - callback
+ * - destroy
+ *
+ * corresponded with the API of instance manager (framework.js)
+ */
+export { init } from './init'
+
+export {
+  refresh,
+  destroy,
+  getRootElement,
+  fireEvent,
+  callback,
+  updateActions,
+  callTasks
+} from './misc'

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/app/ctrl/init.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/app/ctrl/init.js b/runtime/frameworks/legacy/app/ctrl/init.js
new file mode 100644
index 0000000..401d614
--- /dev/null
+++ b/runtime/frameworks/legacy/app/ctrl/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.
+ */
+/**
+ * @fileOverview
+ * instance controls from native
+ *
+ * - init bundle
+ *
+ * corresponded with the API of instance manager (framework.js)
+ */
+
+import Vm from '../../vm/index'
+import { removeWeexPrefix } from '../../util/index'
+import {
+  defineFn,
+  bootstrap,
+  register
+} from '../bundle/index'
+import { updateActions } from './misc'
+
+/**
+ * Init an app by run code witgh data
+ * @param  {object} app
+ * @param  {string} code
+ * @param  {object} data
+ */
+export function init (app, code, data, services) {
+  console.debug('[JS Framework] Intialize an instance with:\n', data)
+  let result
+
+  // prepare app env methods
+  const bundleDefine = (...args) => defineFn(app, ...args)
+  const bundleBootstrap = (name, config, _data) => {
+    result = bootstrap(app, name, config, _data || data)
+    updateActions(app)
+    app.doc.listener.createFinish()
+    console.debug(`[JS Framework] After intialized an instance(${app.id})`)
+  }
+  const bundleVm = Vm
+  /* istanbul ignore next */
+  const bundleRegister = (...args) => register(app, ...args)
+  /* istanbul ignore next */
+  const bundleRender = (name, _data) => {
+    result = bootstrap(app, name, {}, _data)
+  }
+  /* istanbul ignore next */
+  const bundleRequire = name => _data => {
+    result = bootstrap(app, name, {}, _data)
+  }
+  const bundleDocument = app.doc
+  /* istanbul ignore next */
+  const bundleRequireModule = name => app.requireModule(removeWeexPrefix(name))
+
+  const weexGlobalObject = {
+    config: app.options,
+    define: bundleDefine,
+    bootstrap: bundleBootstrap,
+    requireModule: bundleRequireModule,
+    document: bundleDocument,
+    Vm: bundleVm
+  }
+
+  Object.freeze(weexGlobalObject)
+
+  // prepare code
+  let functionBody
+  /* istanbul ignore if */
+  if (typeof code === 'function') {
+    // `function () {...}` -> `{...}`
+    // not very strict
+    functionBody = code.toString().substr(12)
+  }
+  /* istanbul ignore next */
+  else if (code) {
+    functionBody = code.toString()
+  }
+  // wrap IFFE and use strict mode
+  functionBody = `(function(global){\n\n"use strict";\n\n ${functionBody} \n\n})(Object.create(this))`
+
+  // run code and get result
+  const { WXEnvironment } = global
+  const timerAPIs = {}
+
+  /* istanbul ignore if */
+  if (WXEnvironment && WXEnvironment.platform !== 'Web') {
+    // timer APIs polyfill in native
+    const timer = app.requireModule('timer')
+    Object.assign(timerAPIs, {
+      setTimeout: (...args) => {
+        const handler = function () {
+          args[0](...args.slice(2))
+        }
+        timer.setTimeout(handler, args[1])
+        return app.doc.taskCenter.callbackManager.lastCallbackId.toString()
+      },
+      setInterval: (...args) => {
+        const handler = function () {
+          args[0](...args.slice(2))
+        }
+        timer.setInterval(handler, args[1])
+        return app.doc.taskCenter.callbackManager.lastCallbackId.toString()
+      },
+      clearTimeout: (n) => {
+        timer.clearTimeout(n)
+      },
+      clearInterval: (n) => {
+        timer.clearInterval(n)
+      }
+    })
+  }
+  // run code and get result
+  const globalObjects = Object.assign({
+    define: bundleDefine,
+    require: bundleRequire,
+    bootstrap: bundleBootstrap,
+    register: bundleRegister,
+    render: bundleRender,
+    __weex_define__: bundleDefine, // alias for define
+    __weex_bootstrap__: bundleBootstrap, // alias for bootstrap
+    __weex_document__: bundleDocument,
+    __weex_require__: bundleRequireModule,
+    __weex_viewmodel__: bundleVm,
+    weex: weexGlobalObject
+  }, timerAPIs, services)
+  if (!callFunctionNative(globalObjects, functionBody)) {
+    // If failed to compile functionBody on native side,
+    // fallback to callFunction.
+    callFunction(globalObjects, functionBody)
+  }
+
+  return result
+}
+
+/**
+ * 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)
+}
+
+/**
+ * Call a new function generated on the V8 native side.
+ * @param  {object} globalObjects
+ * @param  {string} body
+ * @return {boolean} return true if no error occurred.
+ */
+function callFunctionNative (globalObjects, body) {
+  if (typeof compileAndRunBundle !== 'function') {
+    return false
+  }
+
+  let fn = void 0
+  let isNativeCompileOk = false
+  let script = '(function ('
+  const globalKeys = []
+  const globalValues = []
+  for (const key in globalObjects) {
+    globalKeys.push(key)
+    globalValues.push(globalObjects[key])
+  }
+  for (let i = 0; i < globalKeys.length - 1; ++i) {
+    script += globalKeys[i]
+    script += ','
+  }
+  script += globalKeys[globalKeys.length - 1]
+  script += ') {'
+  script += body
+  script += '} )'
+
+  try {
+    const weex = globalObjects.weex || {}
+    const config = weex.config || {}
+    fn = compileAndRunBundle(script,
+                             config.bundleUrl,
+                             config.bundleDigest,
+                             config.codeCachePath)
+    if (fn && typeof fn === 'function') {
+      fn(...globalValues)
+      isNativeCompileOk = true
+    }
+  }
+  catch (e) {
+    console.error(e)
+  }
+
+  return isNativeCompileOk
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/app/ctrl/misc.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/app/ctrl/misc.js b/runtime/frameworks/legacy/app/ctrl/misc.js
new file mode 100644
index 0000000..13ab09e
--- /dev/null
+++ b/runtime/frameworks/legacy/app/ctrl/misc.js
@@ -0,0 +1,206 @@
+/*
+ * 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
+ * instance controls from native
+ *
+ * - fire event
+ * - callback
+ * - refresh
+ * - destroy
+ *
+ * corresponded with the API of instance manager (framework.js)
+ */
+import { extend, typof } from '../../util/index'
+
+/**
+ * Refresh an app with data to its root component options.
+ * @param  {object} app
+ * @param  {any}    data
+ */
+export function refresh (app, data) {
+  console.debug(`[JS Framework] Refresh with`, data,
+            `in instance[${app.id}]`)
+  const vm = app.vm
+  if (vm && data) {
+    if (typeof vm.refreshData === 'function') {
+      vm.refreshData(data)
+    }
+    else {
+      extend(vm, data)
+    }
+    app.differ.flush()
+    app.doc.taskCenter.send('dom', { action: 'refreshFinish' }, [])
+    return
+  }
+  return new Error(`invalid data "${data}"`)
+}
+
+/**
+ * Destroy an app.
+ * @param  {object} app
+ */
+export function destroy (app) {
+  console.debug(`[JS Framework] Destory an instance(${app.id})`)
+
+  if (app.vm) {
+    destroyVm(app.vm)
+  }
+
+  app.id = ''
+  app.options = null
+  app.blocks = null
+  app.vm = null
+  app.doc.taskCenter.destroyCallback()
+  app.doc.destroy()
+  app.doc = null
+  app.customComponentMap = null
+  app.commonModules = null
+}
+
+/**
+ * Destroy an Vm.
+ * @param {object} vm
+ */
+export function destroyVm (vm) {
+  delete vm._app
+  delete vm._computed
+  delete vm._css
+  delete vm._data
+  delete vm._ids
+  delete vm._methods
+  delete vm._options
+  delete vm._parent
+  delete vm._parentEl
+  delete vm._rootEl
+
+  // remove all watchers
+  if (vm._watchers) {
+    let watcherCount = vm._watchers.length
+    while (watcherCount--) {
+      vm._watchers[watcherCount].teardown()
+    }
+    delete vm._watchers
+  }
+
+  // destroy child vms recursively
+  if (vm._childrenVms) {
+    let vmCount = vm._childrenVms.length
+    while (vmCount--) {
+      destroyVm(vm._childrenVms[vmCount])
+    }
+    delete vm._childrenVms
+  }
+
+  console.debug(`[JS Framework] "destroyed" lifecycle in Vm(${vm._type})`)
+  vm.$emit('hook:destroyed')
+
+  delete vm._type
+  delete vm._vmEvents
+}
+
+/**
+ * Get a JSON object to describe the document body.
+ * @param  {object} app
+ * @return {object}
+ */
+export function getRootElement (app) {
+  const doc = app.doc || {}
+  const body = doc.body || {}
+  return body.toJSON ? body.toJSON() : {}
+}
+
+/**
+ * Fire an event from renderer. The event has type, an event object and an
+ * element ref. If the event comes with some virtual-DOM changes, it should
+ * have one more parameter to describe the changes.
+ * @param  {object} app
+ * @param  {string} ref
+ * @param  {type}   type
+ * @param  {object} e
+ * @param  {object} domChanges
+ */
+export function fireEvent (app, ref, type, e, domChanges) {
+  console.debug(`[JS Framework] Fire a "${type}" event on an element(${ref}) in instance(${app.id})`)
+  if (Array.isArray(ref)) {
+    ref.some((ref) => {
+      return fireEvent(app, ref, type, e) !== false
+    })
+    return
+  }
+  const el = app.doc.getRef(ref)
+  if (el) {
+    const result = app.doc.fireEvent(el, type, e, domChanges)
+    app.differ.flush()
+    app.doc.taskCenter.send('dom', { action: 'updateFinish' }, [])
+    return result
+  }
+  return new Error(`invalid element reference "${ref}"`)
+}
+
+/**
+ * Make a callback for a certain app.
+ * @param  {object}   app
+ * @param  {number}   callbackId
+ * @param  {any}      data
+ * @param  {boolean}  ifKeepAlive
+ */
+export function callback (app, callbackId, data, ifKeepAlive) {
+  console.debug(`[JS Framework] Invoke a callback(${callbackId}) with`, data,
+            `in instance(${app.id})`)
+  const result = app.doc.taskCenter.callback(callbackId, data, ifKeepAlive)
+  updateActions(app)
+  app.doc.taskCenter.send('dom', { action: 'updateFinish' }, [])
+  return result
+}
+
+/**
+ * Collect all virtual-DOM mutations together and send them to renderer.
+ * @param  {object} app
+ */
+export function updateActions (app) {
+  app.differ.flush()
+}
+
+/**
+ * Call all tasks from an app to renderer (native).
+ * @param  {object} app
+ * @param  {array}  tasks
+ */
+export function callTasks (app, tasks) {
+  let result
+
+  /* istanbul ignore next */
+  if (typof(tasks) !== 'array') {
+    tasks = [tasks]
+  }
+
+  tasks.forEach(task => {
+    result = app.doc.taskCenter.send(
+      'module',
+      {
+        module: task.module,
+        method: task.method
+      },
+      task.args
+    )
+  })
+
+  return result
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/runtime/frameworks/legacy/app/differ.js
----------------------------------------------------------------------
diff --git a/runtime/frameworks/legacy/app/differ.js b/runtime/frameworks/legacy/app/differ.js
new file mode 100644
index 0000000..61e25ef
--- /dev/null
+++ b/runtime/frameworks/legacy/app/differ.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.
+ */
+export default class Differ {
+  constructor (id) {
+    this.id = id
+    this.map = []
+    this.hooks = []
+  }
+  isEmpty () {
+    return this.map.length === 0
+  }
+  append (type, depth = 0, ref, handler) {
+    if (!this.hasTimer) {
+      this.hasTimer = true
+      setTimeout(() => {
+        this.hasTimer = false
+        this.flush(true)
+      }, 0)
+    }
+    const map = this.map
+    if (!map[depth]) {
+      map[depth] = {}
+    }
+    const group = map[depth]
+    if (!group[type]) {
+      group[type] = {}
+    }
+    if (type === 'element') {
+      if (!group[type][ref]) {
+        group[type][ref] = []
+      }
+      group[type][ref].push(handler)
+    }
+    else {
+      group[type][ref] = handler
+    }
+  }
+  flush (isTimeout) {
+    const map = this.map.slice()
+    this.map.length = 0
+    map.forEach((group) => {
+      callTypeMap(group, 'repeat')
+      callTypeMap(group, 'shown')
+      callTypeList(group, 'element')
+    })
+
+    const hooks = this.hooks.slice()
+    this.hooks.length = 0
+    hooks.forEach((fn) => {
+      fn()
+    })
+
+    if (!this.isEmpty()) {
+      this.flush()
+    }
+  }
+  then (fn) {
+    this.hooks.push(fn)
+  }
+}
+
+function callTypeMap (group, type) {
+  const map = group[type]
+  for (const ref in map) {
+    map[ref]()
+  }
+}
+
+function callTypeList (group, type) {
+  const map = group[type]
+  for (const ref in map) {
+    const list = map[ref]
+    list.forEach((handler) => { handler() })
+  }
+}


[22/22] incubator-weex git commit: [jsfm] Adjust the file structure and build scripts of js framework (#964)

Posted by ha...@apache.org.
[jsfm] Adjust the file structure and build scripts of js framework (#964)


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

Branch: refs/heads/master
Commit: c0b2cfd502dacd2d22058925b3cdeb244b2074a9
Parents: d724832 9dd6cba
Author: Hanks <zh...@gmail.com>
Authored: Wed Jan 3 15:03:44 2018 +0800
Committer: Hanks <zh...@gmail.com>
Committed: Wed Jan 3 15:03:44 2018 +0800

----------------------------------------------------------------------
 .flowconfig                                     |   17 -
 build/build.js                                  |  118 +-
 build/config.js                                 |  143 +-
 dangerfile.js                                   |   15 +-
 html5/frameworks/index.js                       |   29 -
 html5/frameworks/legacy/api/methods.js          |  194 --
 html5/frameworks/legacy/api/modules.js          |   96 -
 html5/frameworks/legacy/app/bundle/bootstrap.js |   90 -
 html5/frameworks/legacy/app/bundle/define.js    |  115 -
 html5/frameworks/legacy/app/bundle/index.js     |   34 -
 html5/frameworks/legacy/app/ctrl/index.js       |   40 -
 html5/frameworks/legacy/app/ctrl/init.js        |  215 --
 html5/frameworks/legacy/app/ctrl/misc.js        |  206 --
 html5/frameworks/legacy/app/differ.js           |   91 -
 html5/frameworks/legacy/app/downgrade.js        |  172 --
 html5/frameworks/legacy/app/index.js            |   55 -
 html5/frameworks/legacy/app/instance.js         |   47 -
 html5/frameworks/legacy/app/register.js         |  131 --
 html5/frameworks/legacy/app/viewport.js         |   56 -
 html5/frameworks/legacy/config.js               |   35 -
 html5/frameworks/legacy/core/LICENSE            |   21 -
 html5/frameworks/legacy/core/array.js           |  100 -
 html5/frameworks/legacy/core/dep.js             |   80 -
 html5/frameworks/legacy/core/object.js          |   91 -
 html5/frameworks/legacy/core/observer.js        |  323 ---
 html5/frameworks/legacy/core/state.js           |   92 -
 html5/frameworks/legacy/core/watcher.js         |  254 ---
 html5/frameworks/legacy/index.js                |   40 -
 html5/frameworks/legacy/static/bridge.js        |   56 -
 html5/frameworks/legacy/static/create.js        |   51 -
 html5/frameworks/legacy/static/life.js          |   91 -
 html5/frameworks/legacy/static/map.js           |   19 -
 html5/frameworks/legacy/static/misc.js          |   40 -
 html5/frameworks/legacy/static/register.js      |   75 -
 html5/frameworks/legacy/util/LICENSE            |   21 -
 html5/frameworks/legacy/util/index.js           |  156 --
 html5/frameworks/legacy/util/shared.js          |  162 --
 html5/frameworks/legacy/vm/compiler.js          |  636 ------
 html5/frameworks/legacy/vm/directive.js         |  377 ----
 html5/frameworks/legacy/vm/dom-helper.js        |  258 ---
 html5/frameworks/legacy/vm/events.js            |  179 --
 html5/frameworks/legacy/vm/index.js             |  131 --
 html5/frameworks/vanilla/index.js               |  110 -
 html5/render/native/index.js                    |   22 -
 html5/render/native/setup.js                    |   58 -
 html5/render/vue/README.md                      |    3 -
 html5/runtime/api/WeexInstance.js               |  138 --
 html5/runtime/api/component.js                  |   51 -
 html5/runtime/api/config.js                     |   37 -
 html5/runtime/api/init.js                       |  304 ---
 html5/runtime/api/module.js                     |   56 -
 html5/runtime/api/service.js                    |   77 -
 html5/runtime/bridge/CallbackManager.js         |   89 -
 html5/runtime/bridge/Handler.js                 |   91 -
 html5/runtime/bridge/Listener.js                |  220 --
 html5/runtime/bridge/TaskCenter.js              |  145 --
 html5/runtime/bridge/debug.js                   |   65 -
 html5/runtime/bridge/normalize.js               |   91 -
 html5/runtime/bridge/receiver.js                |   71 -
 html5/runtime/index.js                          |   55 -
 html5/runtime/utils.js                          |   71 -
 html5/runtime/vdom/Comment.js                   |   43 -
 html5/runtime/vdom/Document.js                  |  189 --
 html5/runtime/vdom/Element.js                   |  524 -----
 html5/runtime/vdom/Node.js                      |   47 -
 html5/runtime/vdom/WeexElement.js               |   84 -
 html5/runtime/vdom/index.js                     |   37 -
 html5/runtime/vdom/operation.js                 |  272 ---
 html5/services/amd/index.js                     |  105 -
 html5/services/broadcast-channel/index.js       |  125 --
 .../services/broadcast-channel/message-event.js |   40 -
 html5/services/index.js                         |   23 -
 html5/shared/arrayFrom.js                       |  105 -
 html5/shared/console.js                         |  137 --
 html5/shared/freeze.js                          |   90 -
 html5/shared/index.js                           |   32 -
 html5/shared/objectAssign.js                    |   19 -
 html5/shared/objectSetPrototypeOf.js            |   74 -
 html5/shared/promise.js                         |   28 -
 html5/shared/setTimeout.js                      |   63 -
 html5/test/case/.eslintrc                       |    9 -
 html5/test/case/basic/append-root.output.js     |   36 -
 html5/test/case/basic/append-root.source.js     |   75 -
 html5/test/case/basic/append.output.js          |   79 -
 html5/test/case/basic/append.source.js          |  123 --
 html5/test/case/basic/backward1.output.js       |   57 -
 html5/test/case/basic/backward1.source.js       |  113 -
 html5/test/case/basic/backward2.output.js       |   57 -
 html5/test/case/basic/backward2.source.js       |  113 -
 html5/test/case/basic/components.output.js      |   20 -
 html5/test/case/basic/components.source.js      |  101 -
 .../case/basic/computed-in-repeat.output.js     |   19 -
 .../case/basic/computed-in-repeat.source.js     |   82 -
 html5/test/case/basic/created.output.js         |   11 -
 html5/test/case/basic/created.source.js         |   56 -
 html5/test/case/basic/dynamic-id.output.js      |   44 -
 html5/test/case/basic/dynamic-id.source.js      |   94 -
 .../test/case/basic/dynamic-property.output.js  |   24 -
 .../test/case/basic/dynamic-property.source.js  |   79 -
 html5/test/case/basic/dynamic-type.output.js    |   10 -
 html5/test/case/basic/dynamic-type.source.js    |   45 -
 html5/test/case/basic/foo.output.js             |    3 -
 html5/test/case/basic/foo.source.js             |   44 -
 html5/test/case/basic/foo2.output.js            |   10 -
 html5/test/case/basic/foo2.source.js            |   52 -
 html5/test/case/basic/foo3.output.js            |    9 -
 html5/test/case/basic/foo3.source.js            |   41 -
 html5/test/case/basic/foo4.output.js            |   12 -
 html5/test/case/basic/foo4.source.js            |   44 -
 html5/test/case/basic/foo5.output.js            |   14 -
 html5/test/case/basic/foo5.source.js            |   50 -
 html5/test/case/basic/foo6.output.js            |   28 -
 html5/test/case/basic/foo6.source.js            |   71 -
 html5/test/case/basic/foo7.output.js            |   26 -
 html5/test/case/basic/foo7.source.js            |   76 -
 html5/test/case/basic/foo8.output.js            |   60 -
 html5/test/case/basic/foo8.source.js            |   91 -
 html5/test/case/basic/foo9.output.js            |   57 -
 html5/test/case/basic/foo9.source.js            |  113 -
 .../case/basic/global-weex-object.output.js     |    6 -
 .../case/basic/global-weex-object.source.js     |   37 -
 html5/test/case/basic/id.output.js              |   32 -
 html5/test/case/basic/id.source.js              |   80 -
 html5/test/case/basic/if-repeat.output.js       |   13 -
 html5/test/case/basic/if-repeat.source.js       |   60 -
 html5/test/case/basic/if-root.output.js         |    1 -
 html5/test/case/basic/if-root.source.js         |   44 -
 html5/test/case/basic/if.output.js              |   13 -
 html5/test/case/basic/if.source.js              |   57 -
 html5/test/case/basic/promise.output.js         |    3 -
 html5/test/case/basic/promise.source.js         |   47 -
 html5/test/case/basic/ready.output.js           |   11 -
 html5/test/case/basic/ready.source.js           |   56 -
 html5/test/case/basic/repeat-array-kv.output.js |   22 -
 html5/test/case/basic/repeat-array-kv.source.js |   63 -
 .../case/basic/repeat-array-no-kv.output.js     |   22 -
 .../case/basic/repeat-array-no-kv.source.js     |   61 -
 .../case/basic/repeat-array-non-obj.output.js   |   22 -
 .../case/basic/repeat-array-non-obj.source.js   |   62 -
 html5/test/case/basic/repeat-array-v.output.js  |   22 -
 html5/test/case/basic/repeat-array-v.source.js  |   62 -
 html5/test/case/basic/repeat-index.output.js    |   22 -
 html5/test/case/basic/repeat-index.source.js    |   59 -
 html5/test/case/basic/repeat-root.output.js     |    1 -
 html5/test/case/basic/repeat-root.source.js     |   47 -
 html5/test/case/basic/repeat-watch.output.js    |   46 -
 html5/test/case/basic/repeat-watch.source.js    |   75 -
 html5/test/case/basic/require.output.js         |    9 -
 html5/test/case/basic/require.source.js         |   52 -
 html5/test/case/basic/reset-style.output.js     |   15 -
 html5/test/case/basic/reset-style.source.js     |   58 -
 html5/test/case/basic/static1.output.js         |   12 -
 html5/test/case/basic/static1.source.js         |   51 -
 html5/test/case/basic/static2.output.js         |   30 -
 html5/test/case/basic/static2.source.js         |   62 -
 html5/test/case/basic/static3.output.js         |   33 -
 html5/test/case/basic/static3.source.js         |   62 -
 html5/test/case/basic/static4.output.js         |   35 -
 html5/test/case/basic/static4.source.js         |   93 -
 html5/test/case/basic/subvm.output.js           |   20 -
 html5/test/case/basic/subvm.source.js           |   98 -
 html5/test/case/basic/transformer1.output.js    |    3 -
 html5/test/case/basic/transformer1.source.js    |   46 -
 .../case/complex/append-root-event.output.js    |   13 -
 .../case/complex/append-root-event.source.js    |   63 -
 html5/test/case/complex/click.output.js         |   26 -
 html5/test/case/complex/click.source.js         |   76 -
 .../complex/component-append-tree.output.js     |   22 -
 .../complex/component-append-tree.source.js     |   87 -
 html5/test/case/complex/computed.output.js      |   15 -
 html5/test/case/complex/computed.source.js      |   72 -
 html5/test/case/complex/if-refresh.output.js    |    3 -
 html5/test/case/complex/if-refresh.source.js    |   57 -
 .../case/complex/if-repeat-refresh.output.js    |   22 -
 .../case/complex/if-repeat-refresh.source.js    |   60 -
 html5/test/case/complex/inline-click.output.js  |   14 -
 html5/test/case/complex/inline-click.source.js  |   54 -
 html5/test/case/complex/input-binding.output.js |   12 -
 html5/test/case/complex/input-binding.source.js |   78 -
 html5/test/case/complex/refresh2.output.js      |   20 -
 html5/test/case/complex/refresh2.source.js      |  133 --
 .../test/case/complex/repeat-track-by.output.js |   24 -
 .../test/case/complex/repeat-track-by.source.js |   64 -
 html5/test/case/complex/transformer2.output.js  |    3 -
 html5/test/case/complex/transformer2.source.js  |   46 -
 html5/test/case/complex/transformer3.output.js  |    3 -
 html5/test/case/complex/transformer3.source.js  |   46 -
 .../case/multi/clear-dep-target-error.source.js |   45 -
 .../case/multi/clear-dep-target-fine.output.js  |   11 -
 .../case/multi/clear-dep-target-fine.source.js  |   48 -
 html5/test/case/multi/clear-moduleA.output.js   |   12 -
 html5/test/case/multi/clear-moduleA.source.js   |   61 -
 html5/test/case/multi/clear-moduleB.output.js   |   11 -
 html5/test/case/multi/clear-moduleB.source.js   |   50 -
 html5/test/case/prepare.js                      |   83 -
 html5/test/case/signals/modal.output.js         |    9 -
 html5/test/case/signals/modal.source.js         |   60 -
 html5/test/case/signals/signals-long.source.js  | 1223 -----------
 html5/test/case/signals/signals.source.js       |   84 -
 html5/test/case/signals/timer.output.js         |    9 -
 html5/test/case/signals/timer.source.js         |   54 -
 html5/test/case/tester.js                       |  528 -----
 .../test/case/throws/global-variable1.source.js |   35 -
 .../test/case/throws/global-variable2.source.js |   33 -
 .../test/case/throws/global-variable3.source.js |   40 -
 html5/test/unit/default/api/methods.js          |  190 --
 html5/test/unit/default/api/modules.js          |   43 -
 html5/test/unit/default/app/bundle.js           |  409 ----
 html5/test/unit/default/app/ctrl.js             |  230 --
 html5/test/unit/default/app/downgrade.js        |  265 ---
 html5/test/unit/default/app/index.js            |  147 --
 html5/test/unit/default/app/register.js         |  200 --
 html5/test/unit/default/app/viewport.js         |   79 -
 html5/test/unit/default/util/index.js           |  185 --
 html5/test/unit/default/util/shared.js          |  143 --
 html5/test/unit/default/vm/directive.js         |  228 --
 html5/test/unit/default/vm/dom-helper.js        |  405 ----
 html5/test/unit/default/vm/events.js            |  416 ----
 html5/test/unit/default/vm/vm.js                | 1975 ------------------
 html5/test/unit/runtime/legacy-framework.js     |  348 ---
 html5/test/unit/runtime/runner/helper.js        |   73 -
 html5/test/unit/runtime/runner/vanilla.js       |   59 -
 html5/test/unit/runtime/runner/vue.js           |   67 -
 html5/test/unit/runtime/utils.js                |   41 -
 html5/test/unit/runtime/vdom/index.js           |  639 ------
 html5/test/unit/runtime/vdom/listener.js        |  381 ----
 html5/test/unit/runtime/vdom/node.js            |   39 -
 html5/test/unit/shared/BroadcastChannel.js      |  231 --
 html5/test/unit/shared/arrayFrom.js             |   90 -
 html5/test/unit/shared/console.js               |  224 --
 html5/test/unit/shared/index.js                 |   73 -
 html5/test/unit/shared/objectAssign.js          |   50 -
 html5/test/unit/shared/objectSetPrototypeOf.js  |   42 -
 html5/test/unit/vanilla/index.js                |  159 --
 package.json                                    |   31 +-
 runtime/api/WeexInstance.js                     |  138 ++
 runtime/api/component.js                        |   51 +
 runtime/api/config.js                           |   37 +
 runtime/api/index.js                            |   40 +
 runtime/api/init.js                             |  304 +++
 runtime/api/module.js                           |   56 +
 runtime/api/service.js                          |   77 +
 runtime/bridge/CallbackManager.js               |   89 +
 runtime/bridge/Handler.js                       |   91 +
 runtime/bridge/Listener.js                      |  220 ++
 runtime/bridge/TaskCenter.js                    |  145 ++
 runtime/bridge/debug.js                         |   65 +
 runtime/bridge/normalize.js                     |   91 +
 runtime/bridge/receiver.js                      |   71 +
 runtime/entries/index.js                        |   23 +
 runtime/entries/rax.js                          |   23 +
 runtime/entries/setup.js                        |   60 +
 runtime/entries/vue.js                          |   23 +
 runtime/frameworks/index.js                     |   29 +
 runtime/frameworks/legacy/api/methods.js        |  194 ++
 runtime/frameworks/legacy/api/modules.js        |   96 +
 .../frameworks/legacy/app/bundle/bootstrap.js   |   90 +
 runtime/frameworks/legacy/app/bundle/define.js  |  115 +
 runtime/frameworks/legacy/app/bundle/index.js   |   34 +
 runtime/frameworks/legacy/app/ctrl/index.js     |   40 +
 runtime/frameworks/legacy/app/ctrl/init.js      |  215 ++
 runtime/frameworks/legacy/app/ctrl/misc.js      |  206 ++
 runtime/frameworks/legacy/app/differ.js         |   91 +
 runtime/frameworks/legacy/app/downgrade.js      |  172 ++
 runtime/frameworks/legacy/app/index.js          |   55 +
 runtime/frameworks/legacy/app/instance.js       |   47 +
 runtime/frameworks/legacy/app/register.js       |  131 ++
 runtime/frameworks/legacy/app/viewport.js       |   56 +
 runtime/frameworks/legacy/config.js             |   35 +
 runtime/frameworks/legacy/core/LICENSE          |   21 +
 runtime/frameworks/legacy/core/array.js         |  100 +
 runtime/frameworks/legacy/core/dep.js           |   80 +
 runtime/frameworks/legacy/core/object.js        |   91 +
 runtime/frameworks/legacy/core/observer.js      |  323 +++
 runtime/frameworks/legacy/core/state.js         |   92 +
 runtime/frameworks/legacy/core/watcher.js       |  254 +++
 runtime/frameworks/legacy/index.js              |   40 +
 runtime/frameworks/legacy/static/bridge.js      |   56 +
 runtime/frameworks/legacy/static/create.js      |   51 +
 runtime/frameworks/legacy/static/life.js        |   91 +
 runtime/frameworks/legacy/static/map.js         |   19 +
 runtime/frameworks/legacy/static/misc.js        |   40 +
 runtime/frameworks/legacy/static/register.js    |   75 +
 runtime/frameworks/legacy/util/LICENSE          |   21 +
 runtime/frameworks/legacy/util/index.js         |  156 ++
 runtime/frameworks/legacy/util/shared.js        |  162 ++
 runtime/frameworks/legacy/vm/compiler.js        |  636 ++++++
 runtime/frameworks/legacy/vm/directive.js       |  377 ++++
 runtime/frameworks/legacy/vm/dom-helper.js      |  258 +++
 runtime/frameworks/legacy/vm/events.js          |  179 ++
 runtime/frameworks/legacy/vm/index.js           |  131 ++
 runtime/frameworks/vanilla/index.js             |  110 +
 runtime/services/amd/index.js                   |  105 +
 runtime/services/broadcast-channel/index.js     |  125 ++
 .../services/broadcast-channel/message-event.js |   40 +
 runtime/services/index.js                       |   23 +
 runtime/shared/env/console.js                   |  137 ++
 runtime/shared/env/freeze.js                    |   90 +
 runtime/shared/env/setTimeout.js                |   63 +
 runtime/shared/index.js                         |   33 +
 runtime/shared/polyfill/arrayFrom.js            |  105 +
 runtime/shared/polyfill/objectAssign.js         |   19 +
 runtime/shared/polyfill/objectSetPrototypeOf.js |   74 +
 runtime/shared/polyfill/promise.js              |   28 +
 runtime/shared/utils.js                         |   71 +
 runtime/vdom/Comment.js                         |   43 +
 runtime/vdom/Document.js                        |  189 ++
 runtime/vdom/Element.js                         |  524 +++++
 runtime/vdom/Node.js                            |   47 +
 runtime/vdom/WeexElement.js                     |   84 +
 runtime/vdom/index.js                           |   37 +
 runtime/vdom/operation.js                       |  272 +++
 test/js-framework/case/.eslintrc                |    9 +
 .../case/basic/append-root.output.js            |   36 +
 .../case/basic/append-root.source.js            |   75 +
 test/js-framework/case/basic/append.output.js   |   79 +
 test/js-framework/case/basic/append.source.js   |  123 ++
 .../js-framework/case/basic/backward1.output.js |   57 +
 .../js-framework/case/basic/backward1.source.js |  113 +
 .../js-framework/case/basic/backward2.output.js |   57 +
 .../js-framework/case/basic/backward2.source.js |  113 +
 .../case/basic/components.output.js             |   20 +
 .../case/basic/components.source.js             |  101 +
 .../case/basic/computed-in-repeat.output.js     |   19 +
 .../case/basic/computed-in-repeat.source.js     |   82 +
 test/js-framework/case/basic/created.output.js  |   11 +
 test/js-framework/case/basic/created.source.js  |   56 +
 .../case/basic/dynamic-id.output.js             |   44 +
 .../case/basic/dynamic-id.source.js             |   94 +
 .../case/basic/dynamic-property.output.js       |   24 +
 .../case/basic/dynamic-property.source.js       |   79 +
 .../case/basic/dynamic-type.output.js           |   10 +
 .../case/basic/dynamic-type.source.js           |   45 +
 test/js-framework/case/basic/foo.output.js      |    3 +
 test/js-framework/case/basic/foo.source.js      |   44 +
 test/js-framework/case/basic/foo2.output.js     |   10 +
 test/js-framework/case/basic/foo2.source.js     |   52 +
 test/js-framework/case/basic/foo3.output.js     |    9 +
 test/js-framework/case/basic/foo3.source.js     |   41 +
 test/js-framework/case/basic/foo4.output.js     |   12 +
 test/js-framework/case/basic/foo4.source.js     |   44 +
 test/js-framework/case/basic/foo5.output.js     |   14 +
 test/js-framework/case/basic/foo5.source.js     |   50 +
 test/js-framework/case/basic/foo6.output.js     |   28 +
 test/js-framework/case/basic/foo6.source.js     |   71 +
 test/js-framework/case/basic/foo7.output.js     |   26 +
 test/js-framework/case/basic/foo7.source.js     |   76 +
 test/js-framework/case/basic/foo8.output.js     |   60 +
 test/js-framework/case/basic/foo8.source.js     |   91 +
 test/js-framework/case/basic/foo9.output.js     |   57 +
 test/js-framework/case/basic/foo9.source.js     |  113 +
 .../case/basic/global-weex-object.output.js     |    6 +
 .../case/basic/global-weex-object.source.js     |   37 +
 test/js-framework/case/basic/id.output.js       |   32 +
 test/js-framework/case/basic/id.source.js       |   80 +
 .../js-framework/case/basic/if-repeat.output.js |   13 +
 .../js-framework/case/basic/if-repeat.source.js |   60 +
 test/js-framework/case/basic/if-root.output.js  |    1 +
 test/js-framework/case/basic/if-root.source.js  |   44 +
 test/js-framework/case/basic/if.output.js       |   13 +
 test/js-framework/case/basic/if.source.js       |   57 +
 test/js-framework/case/basic/promise.output.js  |    3 +
 test/js-framework/case/basic/promise.source.js  |   47 +
 test/js-framework/case/basic/ready.output.js    |   11 +
 test/js-framework/case/basic/ready.source.js    |   56 +
 .../case/basic/repeat-array-kv.output.js        |   22 +
 .../case/basic/repeat-array-kv.source.js        |   63 +
 .../case/basic/repeat-array-no-kv.output.js     |   22 +
 .../case/basic/repeat-array-no-kv.source.js     |   61 +
 .../case/basic/repeat-array-non-obj.output.js   |   22 +
 .../case/basic/repeat-array-non-obj.source.js   |   62 +
 .../case/basic/repeat-array-v.output.js         |   22 +
 .../case/basic/repeat-array-v.source.js         |   62 +
 .../case/basic/repeat-index.output.js           |   22 +
 .../case/basic/repeat-index.source.js           |   59 +
 .../case/basic/repeat-root.output.js            |    1 +
 .../case/basic/repeat-root.source.js            |   47 +
 .../case/basic/repeat-watch.output.js           |   46 +
 .../case/basic/repeat-watch.source.js           |   75 +
 test/js-framework/case/basic/require.output.js  |    9 +
 test/js-framework/case/basic/require.source.js  |   52 +
 .../case/basic/reset-style.output.js            |   15 +
 .../case/basic/reset-style.source.js            |   58 +
 test/js-framework/case/basic/static1.output.js  |   12 +
 test/js-framework/case/basic/static1.source.js  |   51 +
 test/js-framework/case/basic/static2.output.js  |   30 +
 test/js-framework/case/basic/static2.source.js  |   62 +
 test/js-framework/case/basic/static3.output.js  |   33 +
 test/js-framework/case/basic/static3.source.js  |   62 +
 test/js-framework/case/basic/static4.output.js  |   35 +
 test/js-framework/case/basic/static4.source.js  |   93 +
 test/js-framework/case/basic/subvm.output.js    |   20 +
 test/js-framework/case/basic/subvm.source.js    |   98 +
 .../case/basic/transformer1.output.js           |    3 +
 .../case/basic/transformer1.source.js           |   46 +
 .../case/complex/append-root-event.output.js    |   13 +
 .../case/complex/append-root-event.source.js    |   63 +
 test/js-framework/case/complex/click.output.js  |   26 +
 test/js-framework/case/complex/click.source.js  |   76 +
 .../complex/component-append-tree.output.js     |   22 +
 .../complex/component-append-tree.source.js     |   87 +
 .../case/complex/computed.output.js             |   15 +
 .../case/complex/computed.source.js             |   72 +
 .../case/complex/if-refresh.output.js           |    3 +
 .../case/complex/if-refresh.source.js           |   57 +
 .../case/complex/if-repeat-refresh.output.js    |   22 +
 .../case/complex/if-repeat-refresh.source.js    |   60 +
 .../case/complex/inline-click.output.js         |   14 +
 .../case/complex/inline-click.source.js         |   54 +
 .../case/complex/input-binding.output.js        |   12 +
 .../case/complex/input-binding.source.js        |   78 +
 .../case/complex/refresh2.output.js             |   20 +
 .../case/complex/refresh2.source.js             |  133 ++
 .../case/complex/repeat-track-by.output.js      |   24 +
 .../case/complex/repeat-track-by.source.js      |   64 +
 .../case/complex/transformer2.output.js         |    3 +
 .../case/complex/transformer2.source.js         |   46 +
 .../case/complex/transformer3.output.js         |    3 +
 .../case/complex/transformer3.source.js         |   46 +
 .../case/multi/clear-dep-target-error.source.js |   45 +
 .../case/multi/clear-dep-target-fine.output.js  |   11 +
 .../case/multi/clear-dep-target-fine.source.js  |   48 +
 .../case/multi/clear-moduleA.output.js          |   12 +
 .../case/multi/clear-moduleA.source.js          |   61 +
 .../case/multi/clear-moduleB.output.js          |   11 +
 .../case/multi/clear-moduleB.source.js          |   50 +
 test/js-framework/case/prepare.js               |   83 +
 test/js-framework/case/signals/modal.output.js  |    9 +
 test/js-framework/case/signals/modal.source.js  |   60 +
 .../case/signals/signals-long.source.js         | 1223 +++++++++++
 .../js-framework/case/signals/signals.source.js |   84 +
 test/js-framework/case/signals/timer.output.js  |    9 +
 test/js-framework/case/signals/timer.source.js  |   54 +
 test/js-framework/case/tester.js                |  528 +++++
 .../case/throws/global-variable1.source.js      |   35 +
 .../case/throws/global-variable2.source.js      |   33 +
 .../case/throws/global-variable3.source.js      |   40 +
 test/js-framework/unit/default/api/methods.js   |  190 ++
 test/js-framework/unit/default/api/modules.js   |   43 +
 test/js-framework/unit/default/app/bundle.js    |  409 ++++
 test/js-framework/unit/default/app/ctrl.js      |  230 ++
 test/js-framework/unit/default/app/downgrade.js |  265 +++
 test/js-framework/unit/default/app/index.js     |  147 ++
 test/js-framework/unit/default/app/register.js  |  200 ++
 test/js-framework/unit/default/app/viewport.js  |   79 +
 test/js-framework/unit/default/util/index.js    |  185 ++
 test/js-framework/unit/default/util/shared.js   |  143 ++
 test/js-framework/unit/default/vm/directive.js  |  228 ++
 test/js-framework/unit/default/vm/dom-helper.js |  405 ++++
 test/js-framework/unit/default/vm/events.js     |  416 ++++
 test/js-framework/unit/default/vm/vm.js         | 1975 ++++++++++++++++++
 .../unit/runtime/legacy-framework.js            |  348 +++
 test/js-framework/unit/runtime/runner/helper.js |   73 +
 .../js-framework/unit/runtime/runner/vanilla.js |   59 +
 test/js-framework/unit/runtime/runner/vue.js    |   67 +
 test/js-framework/unit/runtime/utils.js         |   41 +
 test/js-framework/unit/runtime/vdom/index.js    |  639 ++++++
 test/js-framework/unit/runtime/vdom/listener.js |  381 ++++
 test/js-framework/unit/runtime/vdom/node.js     |   39 +
 .../unit/shared/BroadcastChannel.js             |  231 ++
 test/js-framework/unit/shared/arrayFrom.js      |   90 +
 test/js-framework/unit/shared/console.js        |  227 ++
 test/js-framework/unit/shared/index.js          |   73 +
 test/js-framework/unit/shared/objectAssign.js   |   50 +
 .../unit/shared/objectSetPrototypeOf.js         |   42 +
 test/js-framework/unit/vanilla/index.js         |  159 ++
 466 files changed, 23441 insertions(+), 23444 deletions(-)
----------------------------------------------------------------------



[12/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/vm/directive.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/vm/directive.js b/html5/test/unit/default/vm/directive.js
deleted file mode 100644
index fa21969..0000000
--- a/html5/test/unit/default/vm/directive.js
+++ /dev/null
@@ -1,228 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import {
-  applyNaitveComponentOptions,
-  bindSubVm,
-  bindSubVmAfterInitialized
-} from '../../../../frameworks/legacy/vm/directive'
-
-import {
-  initState
-} from '../../../../frameworks/legacy/core/state'
-import config from '../../../../frameworks/legacy/config'
-
-const { nativeComponentMap } = config
-const directive = {}
-
-function extendVm (vm, methodNames) {
-  methodNames.forEach((name) => {
-    vm[name] = directive[name]
-  })
-  initState(vm)
-}
-
-function initElement (el) {
-  el.setAttr = function (k, v) { this.attr[k] = v }
-  el.setStyle = function (k, v) { this.style[k] = v }
-  el.setClassStyle = function (style) { this.classStyle = style }
-  el.addEvent = function (t, h) {
-    this.event[t] = h
-  }
-}
-
-describe('apply component options', () => {
-  it('apply top prop', () => {
-    nativeComponentMap['test-apply'] = {
-      type: 'test-apply1',
-      append: 'tree'
-    }
-    const template = {
-      type: 'test-apply'
-    }
-    applyNaitveComponentOptions(template)
-    expect(template.type).to.be.equal('test-apply')
-    expect(template.append).to.be.equal('tree')
-
-    delete nativeComponentMap['test-apply']
-  })
-
-  it('apply a object', () => {
-    nativeComponentMap['test-apply'] = {
-      classList: ['c'],
-      attr: {
-        a: 'a',
-        b: 'b'
-      }
-    }
-    const spy = sinon.spy()
-    const template = {
-      type: 'test-apply',
-      classList: spy,
-      attr: {
-        b: '2'
-      }
-    }
-    applyNaitveComponentOptions(template)
-
-    expect(template).to.deep.equal({
-      type: 'test-apply',
-      classList: spy,
-      attr: {
-        a: 'a',
-        b: '2'
-      }
-    })
-
-    delete nativeComponentMap['test-apply']
-  })
-})
-
-// exports._bindSubVm(subVm, template)
-describe('bind external infomations to sub vm', () => {
-  let vm, subVm
-  // const methodNames = [
-  //   '_watch', '_bindKey', '_bindDir',
-  //   '_setId', '_setAttr', '_setClass', '_setStyle',
-  //   '_setEvent', '_bindEvents', '_bindElement',
-  //   '_bindSubVm', '_bindSubVmAfterInitialized']
-  beforeEach(() => {
-    vm = {
-      _data: { a: 1, b: 2, c: 'class-style1' },
-      _watchers: [],
-      _app: { eventManager: { add: () => {} }},
-      _options: {
-        style: {
-          'class-style1': {
-            aaa: 1,
-            bbb: 2
-          },
-          'class-style2': {
-            aaa: 2,
-            ccc: 3
-          }
-        }
-      },
-      foo: function () {}
-    }
-    extendVm(vm, [])
-    subVm = {
-      _options: {
-        props: {
-          a: String,
-          b: String
-        }
-      }
-    }
-  })
-
-  it('bind to no-root-element sub vm', () => {
-    bindSubVm(vm, subVm, {
-      attr: { a: 3, c: 4 },
-      style: { a: 2 },
-      events: { click: 'foo' }
-    })
-    expect(subVm.a).eql(3)
-    expect(subVm.c).is.undefined
-    expect(subVm._rootEl).is.undefined
-  })
-
-  it('bind props with external data', () => {
-    bindSubVm(vm, subVm, {
-      attr: { a: function () { return this.b } }
-    })
-    expect(subVm.a).eql(2)
-  })
-
-  it('bind styles to a sub vm with root element', () => {
-    subVm._rootEl = {
-      attr: {},
-      style: {},
-      event: []
-    }
-    const template = {
-      style: { aaa: 2, bbb: function () { return this.a } }
-    }
-    initElement(subVm._rootEl)
-    bindSubVm(vm, subVm, template)
-    bindSubVmAfterInitialized(vm, subVm, template)
-    expect(subVm._rootEl.style.aaa).eql(2)
-    expect(subVm._rootEl.style.bbb).eql(1)
-    vm.a = 3
-    expect(subVm._rootEl.style.bbb).eql(3)
-  })
-
-  it('bind simply classlist to a sub vm with root element', () => {
-    subVm._rootEl = {
-      attr: {},
-      style: {},
-      event: []
-    }
-    const template = {
-      classList: ['class-style1']
-    }
-    initElement(subVm._rootEl)
-    bindSubVm(vm, subVm, template)
-    bindSubVmAfterInitialized(vm, subVm, template)
-    expect(subVm._rootEl.classStyle.aaa).eql(1)
-    expect(subVm._rootEl.classStyle.bbb).eql(2)
-  })
-
-  it('bind classlist to a sub vm with root element', () => {
-    subVm._rootEl = {
-      attr: {},
-      style: {},
-      event: []
-    }
-    const template = {
-      classList: function () {
-        return [this.c]
-      }
-    }
-    initElement(subVm._rootEl)
-    bindSubVm(vm, subVm, template)
-    bindSubVmAfterInitialized(vm, subVm, template)
-    expect(subVm._rootEl.classStyle.aaa).eql(1)
-    expect(subVm._rootEl.classStyle.bbb).eql(2)
-    vm.c = 'class-style2'
-    expect(subVm._rootEl.classStyle.aaa).eql(2)
-    expect(subVm._rootEl.classStyle.bbb).to.be.undefined
-    expect(subVm._rootEl.classStyle.ccc).eql(3)
-  })
-
-  it('bind events to a sub vm with root element', () => {
-    subVm._rootEl = {
-      attr: {},
-      style: {},
-      event: {}
-    }
-    const template = {
-      events: { click: 'foo' }
-    }
-    initElement(subVm._rootEl)
-    bindSubVm(vm, subVm, template)
-    bindSubVmAfterInitialized(vm, subVm, template)
-    // expect(subVm._rootEl.event.click).a('function')
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/vm/dom-helper.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/vm/dom-helper.js b/html5/test/unit/default/vm/dom-helper.js
deleted file mode 100644
index 48c0a87..0000000
--- a/html5/test/unit/default/vm/dom-helper.js
+++ /dev/null
@@ -1,405 +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 chai from 'chai'
-const { expect } = chai
-
-import {
-  createElement,
-  createBlock,
-  createBody,
-  attachTarget,
-  moveTarget,
-  removeTarget
-} from '../../../../frameworks/legacy/vm/dom-helper'
-import { Document } from '../../../../runtime/vdom'
-import Listener from '../../../../runtime/bridge/Listener'
-
-describe('help create body', () => {
-  let vm
-
-  beforeEach(() => {
-    vm = {
-      _app: { doc: new Document('foo', null, null, Listener) }
-    }
-  })
-
-  afterEach(() => {
-    vm._app.doc.destroy()
-    vm = null
-  })
-
-  it('create body with type', () => {
-    const result = createBody(vm, 'bar')
-    expect(result).is.an.object
-    expect(result.type).eql('bar')
-    expect(result.ref).eql('_root')
-    expect(result.docId).is.not.ok
-  })
-})
-
-describe('help create element', () => {
-  let vm
-
-  beforeEach(() => {
-    vm = {
-      _app: { doc: new Document('foo', null, null, Listener) }
-    }
-  })
-
-  afterEach(() => {
-    vm._app.doc.destroy()
-    vm = null
-  })
-
-  it('create element with type', () => {
-    const result = createElement(vm, 'bar')
-    expect(result).is.an.object
-    expect(result.type).eql('bar')
-    expect(result.docId).is.not.ok
-  })
-})
-
-describe('help create block', () => {
-  let vm
-
-  beforeEach(() => {
-    vm = {
-      _app: { doc: new Document('foo', null, null, Listener) }
-    }
-  })
-
-  afterEach(() => {
-    vm._app.doc.destroy()
-    vm = null
-  })
-
-  it('create block with element', () => {
-    const element = vm._app.doc.createElement('bar')
-    const result = createBlock(vm, element)
-    expect(result).is.an.object
-    expect(result.start).is.an.object
-    expect(result.end).is.an.object
-    expect(result.start.type).eql('comment')
-    expect(result.end.type).eql('comment')
-    expect(result.start.value).eql('start')
-    expect(result.end.value).eql('end')
-    expect(result.element).equal(element)
-    expect(result.element.children).eql([result.start, result.end])
-  })
-})
-
-describe('help attach target', () => {
-  let vm
-
-  beforeEach(() => {
-    vm = {
-      _app: { doc: new Document('foo', null, null, Listener) }
-    }
-  })
-
-  afterEach(() => {
-    vm._app.doc.destroy()
-    vm = null
-  })
-
-  it('attach body to documentElement', () => {
-    const oriCallnative = global.callNative
-    global.callNative = function () {}
-    const target = createBody(vm, 'bar')
-    const dest = vm._app.doc.documentElement
-    attachTarget(vm, target, dest)
-    expect(dest.children).eql([target])
-    global.callNative = oriCallnative
-  })
-
-  it('attach element to body', () => {
-    const target = createElement(vm, 'bar')
-    const dest = createBody(vm, 'baz')
-    attachTarget(vm, target, dest)
-    expect(dest.children).eql([target])
-  })
-
-  it('attach element to element', () => {
-    const target = createElement(vm, 'bar')
-    const dest = createElement(vm, 'baz')
-    attachTarget(vm, target, dest)
-    expect(dest.children).eql([target])
-  })
-
-  it('attach block to element', () => {
-    const parent = createElement(vm, 'bar')
-    const target = createBlock(vm, parent)
-    const dest = createElement(vm, 'baz')
-    attachTarget(vm, target, dest)
-    // block can't attach to another element
-    expect(dest.children).eql([])
-    expect(parent.children).eql([target.start, target.end])
-  })
-
-  it('attach element to block', () => {
-    const target = createElement(vm, 'bar')
-    const parent = createElement(vm, 'baz')
-    const dest = createBlock(vm, parent)
-    attachTarget(vm, target, dest)
-    expect(parent.children).eql([dest.start, target, dest.end])
-  })
-
-  it('attach block to block', () => {
-    const element = createElement(vm, 'bar')
-    const target = createBlock(vm, element)
-    const parent = createElement(vm, 'baz')
-    const dest = createBlock(vm, parent)
-    attachTarget(vm, target, dest)
-    // block can't attach to another element
-    expect(parent.children).eql([dest.start, dest.end])
-    expect(element.children).eql([target.start, target.end])
-  })
-
-  it('attach element to block with an update mark', () => {
-    const target = createElement(vm, 'bar')
-    const parent = createElement(vm, 'baz')
-    const dest = createBlock(vm, parent)
-    const mark = createElement(vm, 'qux')
-
-    attachTarget(vm, target, dest)
-    attachTarget(vm, mark, dest)
-    expect(parent.children).eql([dest.start, target, mark, dest.end])
-
-    dest.updateMark = mark
-    attachTarget(vm, target, dest)
-    expect(parent.children).eql([dest.start, mark, target, dest.end])
-    expect(dest.updateMark).eql(target)
-  })
-
-  it('attach block to block with an update mark', () => {
-    const element = createElement(vm, 'bar')
-    const target = createBlock(vm, element)
-    const parent = createElement(vm, 'baz')
-    const dest = createBlock(vm, parent)
-    const mark = createElement(vm, 'qux')
-
-    attachTarget(vm, target, dest)
-    attachTarget(vm, mark, dest)
-    // block can't attach to another element
-    expect(parent.children).eql([dest.start, mark, dest.end])
-    expect(element.children).eql([target.start, target.end])
-
-    dest.updateMark = mark
-    attachTarget(vm, target, dest)
-    // block can't attach to another element
-    expect(parent.children).eql([dest.start, mark, dest.end])
-    expect(element.children).eql([target.start, target.end])
-  })
-})
-
-describe('help move target', () => {
-  let vm, parent, dest, target1, block1, target2, block2, target3, block3
-
-  beforeEach(() => {
-    vm = {
-      _app: { doc: new Document('foo', null, null, Listener) }
-    }
-    parent = createElement(vm, 'r')
-    dest = createBlock(vm, parent)
-    target1 = createElement(vm, 't1')
-    attachTarget(vm, target1, dest)
-    block1 = createBlock(vm, dest)
-    target2 = createElement(vm, 't2')
-    attachTarget(vm, target2, dest)
-    block2 = createBlock(vm, dest)
-    target3 = createElement(vm, 't3')
-    attachTarget(vm, target3, block2)
-    block3 = createBlock(vm, block2)
-  })
-
-  afterEach(() => {
-    vm._app.doc.destroy()
-    vm = null
-  })
-
-  it('move an element to an element mark', () => {
-    /* eslint-disable indent */
-    expect(parent.children).eql([
-      dest.start,
-      target1, block1.start, block1.end,
-      target2, block2.start,
-        target3, block3.start, block3.end,
-      block2.end,
-      dest.end])
-    /* eslint-enable indent */
-
-    const mark = target2
-    dest.updateMark = mark
-
-    moveTarget(vm, target1, mark)
-
-    /* eslint-disable indent */
-    expect(parent.children).eql([
-      dest.start,
-      block1.start, block1.end,
-      target2, target1, block2.start,
-        target3, block3.start, block3.end,
-      block2.end,
-      dest.end])
-    /* eslint-enable indent */
-  })
-
-  it('move an element to a block mark', () => {
-    const mark = block2.end
-    dest.updateMark = mark
-
-    moveTarget(vm, target1, mark)
-
-    /* eslint-disable indent */
-    expect(parent.children).eql([
-      dest.start,
-      block1.start, block1.end,
-      target2, block2.start,
-        target3, block3.start, block3.end,
-      block2.end, target1,
-      dest.end])
-    /* eslint-enable indent */
-  })
-
-  it('move a block to an element mark', () => {
-    const mark = target2
-    dest.updateMark = mark
-
-    moveTarget(vm, block1, mark)
-
-    /* eslint-disable indent */
-    expect(parent.children).eql([
-      dest.start,
-      target1,
-      target2, block1.start, block1.end,
-      block2.start,
-        target3, block3.start, block3.end,
-      block2.end,
-      dest.end])
-    /* eslint-enable indent */
-  })
-
-  it('move a block to a block mark', () => {
-    const mark = block2.end
-    dest.updateMark = mark
-
-    moveTarget(vm, block1, mark)
-
-    /* eslint-disable indent */
-    expect(parent.children).eql([
-      dest.start,
-      target1,
-      target2,
-      block2.start,
-        target3, block3.start, block3.end,
-      block2.end, block1.start, block1.end,
-      dest.end])
-    /* eslint-enable indent */
-  })
-
-  it('move a whole block to a block mark', () => {
-    const mark = block1.end
-    dest.updateMark = mark
-
-    moveTarget(vm, block2, mark)
-
-    /* eslint-disable indent */
-    expect(parent.children).eql([
-      dest.start,
-      target1, block1.start, block1.end,
-      block2.start,
-        target3, block3.start, block3.end,
-      block2.end,
-      target2,
-      dest.end])
-    /* eslint-enable indent */
-  })
-})
-
-describe('help remove target', () => {
-  let vm
-
-  beforeEach(() => {
-    vm = {
-      _app: { doc: new Document('foo', null, null, Listener) }
-    }
-  })
-
-  afterEach(() => {
-    vm._app.doc.destroy()
-    vm = null
-  })
-
-  it('remove body', () => {
-    const oriCallnative = global.callNative
-    global.callNative = function () {}
-    const parent = vm._app.doc.documentElement
-    const element = createBody(vm, 'baz')
-    parent.appendChild(element)
-    expect(parent.children).eql([element])
-    removeTarget(vm, element)
-    expect(parent.children).eql([])
-    global.callNative = oriCallnative
-  })
-
-  it('remove element', () => {
-    const parent = createElement(vm, 'bar')
-    const element = createElement(vm, 'baz')
-    parent.appendChild(element)
-    expect(parent.children).eql([element])
-    removeTarget(vm, element)
-    expect(parent.children).eql([])
-  })
-
-  it('remove block', () => {
-    const element = createElement(vm, 'baz')
-    const prevElement = createElement(vm, 'prev')
-    const nextElement = createElement(vm, 'next')
-    const parent = createElement(vm, 'bar')
-    attachTarget(vm, prevElement, parent)
-    const block = createBlock(vm, parent)
-    attachTarget(vm, element, block)
-    attachTarget(vm, nextElement, parent)
-
-    expect(parent.children).eql([
-      prevElement, block.start, element, block.end, nextElement])
-
-    removeTarget(vm, block)
-    expect(parent.children).eql([
-      prevElement, nextElement])
-  })
-
-  it('remove block but preserved itself', () => {
-    const element = createElement(vm, 'baz')
-    const prevElement = createElement(vm, 'prev')
-    const nextElement = createElement(vm, 'next')
-    const parent = createElement(vm, 'bar')
-    attachTarget(vm, prevElement, parent)
-    const block = createBlock(vm, parent)
-    attachTarget(vm, element, block)
-    attachTarget(vm, nextElement, parent)
-
-    expect(parent.children).eql([
-      prevElement, block.start, element, block.end, nextElement])
-
-    removeTarget(vm, block, true)
-    expect(parent.children).eql([
-      prevElement, block.start, block.end, nextElement])
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/vm/events.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/vm/events.js b/html5/test/unit/default/vm/events.js
deleted file mode 100644
index a3af0d2..0000000
--- a/html5/test/unit/default/vm/events.js
+++ /dev/null
@@ -1,416 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import Vm from '../../../../frameworks/legacy/vm'
-import { Document } from '../../../../runtime/vdom'
-
-describe('bind and fire events', () => {
-  let doc, customComponentMap, spy
-
-  function checkReady (vm, handler) {
-    /* istanbul ignore else */
-    if (vm._ready) {
-      handler()
-    }
-    else {
-      vm.$on('hook:ready', handler)
-    }
-  }
-
-  beforeEach(() => {
-    spy = sinon.spy()
-    doc = new Document('test', '', spy)
-    customComponentMap = {}
-  })
-
-  afterEach(() => {
-    doc.destroy()
-  })
-
-  it('bind event and fire in a single dom', (done) => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        attr: {
-          a: function () { return this.x }
-        },
-        events: { click: 'handleClick' }
-      },
-      data: function () {
-        return { x: 1 }
-      },
-      methods: {
-        handleClick: function () {
-          this.x = 2
-        }
-      }
-    }
-
-    const app = { doc, customComponentMap }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    checkReady(vm, function () {
-      expect(doc.body.event.click.handler).a('function')
-
-      const el = doc.body
-      expect(el.attr.a).eql(1)
-      expect(spy.args.length).eql(1)
-      expect(doc.listener.updates.length).eql(0)
-
-      el.event.click.handler({ xxx: 1 })
-      expect(el.attr.a).eql(2)
-      expect(spy.args.length).eql(2)
-      expect(spy.args[1][0]).eql([
-        { module: 'dom', method: 'updateAttrs', args: [el.ref, { a: 2 }] }
-      ])
-
-      done()
-    })
-  })
-
-  it('emit, broadcast and dispatch vm events', (done) => {
-    customComponentMap.foo = {
-      template: {
-        type: 'aaa',
-        children: [{ type: 'bar', component: true }]
-      }
-    }
-    customComponentMap.bar = {
-      template: {
-        type: 'bbb'
-      }
-    }
-
-    const app = { doc, customComponentMap }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    checkReady(vm, function () {
-      doc.close()
-
-      expect(vm._childrenVms.length).eql(1)
-
-      const subVm = vm._childrenVms[0]
-      expect(vm._type).eql('foo')
-      expect(subVm._type).eql('bar')
-
-      const spyA = sinon.spy()
-      const spyB = sinon.spy()
-
-      vm.$on('customTypeA', spyA)
-      subVm.$on('customTypeA', spyB)
-
-      let detail = { aaa: 111 }
-      vm.$emit('customTypeA', detail)
-      expect(spyA.args.length).eql(1)
-      expect(spyB.args.length).eql(0)
-
-      detail = { aaa: 111 }
-      subVm.$emit('customTypeA', detail)
-      expect(spyA.args.length).eql(1)
-      expect(spyB.args.length).eql(1)
-
-      detail = { aaa: 111 }
-      vm.$broadcast('customTypeA', detail)
-      expect(spyA.args.length).eql(2)
-      expect(spyB.args.length).eql(2)
-
-      detail = { aaa: 111 }
-      subVm.$broadcast('customTypeA', detail)
-      expect(spyA.args.length).eql(2)
-      expect(spyB.args.length).eql(3)
-
-      detail = { aaa: 111 }
-      vm.$dispatch('customTypeA', detail)
-      expect(spyA.args.length).eql(3)
-      expect(spyB.args.length).eql(3)
-
-      detail = { aaa: 111 }
-      subVm.$dispatch('customTypeA', detail)
-      expect(spyA.args.length).eql(4)
-      expect(spyB.args.length).eql(4)
-
-      vm.$off('customTypeA', spyA)
-      subVm.$off('customTypeA')
-
-      detail = { aaa: 111 }
-      vm.$emit('customTypeA', detail)
-      detail = { aaa: 111 }
-      subVm.$emit('customTypeA', detail)
-      detail = { aaa: 111 }
-      vm.$broadcast('customTypeA', detail)
-      detail = { aaa: 111 }
-      subVm.$broadcast('customTypeA', detail)
-      detail = { aaa: 111 }
-      vm.$dispatch('customTypeA', detail)
-      detail = { aaa: 111 }
-      subVm.$dispatch('customTypeA', detail)
-
-      expect(spyA.args.length).eql(4)
-      expect(spyB.args.length).eql(4)
-
-      const spyAA = function (e) {
-        e.stop()
-        spyA()
-      }
-      const spyBB = function (e) {
-        e.stop()
-        spyB()
-      }
-
-      vm.$on('customTypeA', spyAA)
-      subVm.$on('customTypeA', spyBB)
-      vm.$off()
-      vm.$off('unknownTypeA', spyA)
-
-      detail = { aaa: 111 }
-      vm.$emit('customTypeA', detail)
-      expect(spyA.args.length).eql(5)
-      expect(spyB.args.length).eql(4)
-      detail = { aaa: 111 }
-      subVm.$emit('customTypeA', detail)
-      expect(spyA.args.length).eql(5)
-      expect(spyB.args.length).eql(5)
-      detail = { aaa: 111 }
-      vm.$broadcast('customTypeA', detail)
-      expect(spyA.args.length).eql(6)
-      expect(spyB.args.length).eql(5)
-      detail = { aaa: 111 }
-      subVm.$broadcast('customTypeA', detail)
-      expect(spyA.args.length).eql(6)
-      expect(spyB.args.length).eql(6)
-      detail = { aaa: 111 }
-      vm.$dispatch('customTypeA', detail)
-      expect(spyA.args.length).eql(7)
-      expect(spyB.args.length).eql(6)
-      detail = { aaa: 111 }
-      subVm.$dispatch('customTypeA', detail)
-      expect(spyA.args.length).eql(7)
-      expect(spyB.args.length).eql(7)
-
-      done()
-    })
-  })
-
-  it('broadcast for repeat vms', (done) => {
-    const vmSpy = sinon.spy()
-    const subVmSpy = sinon.spy()
-
-    customComponentMap.foo = {
-      data: function () {
-        return { list: [{ a: 1 }, { a: 2 }, { a: 3 }] }
-      },
-      template: {
-        type: 'container',
-        children: [{
-          type: 'bar', component: true,
-          repeat: function () { return this.list }
-        }]
-      },
-      events: {
-        customTypeA: function (e) {
-          vmSpy(this, e)
-        }
-      }
-    }
-    customComponentMap.bar = {
-      template: {
-        type: 'aaa'
-      },
-      events: {
-        customTypeA: function (e) {
-          subVmSpy(this, e)
-        }
-      }
-    }
-
-    const app = { doc, customComponentMap }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    checkReady(vm, function () {
-      doc.close()
-      expect(vm._childrenVms.length).eql(3)
-
-      let detail = { aaa: 111 }
-      vm.$emit('customTypeA', detail)
-      expect(vmSpy.args.length).eql(1)
-      expect(subVmSpy.args.length).eql(0)
-      expect(vmSpy.args[0][0]).equal(vm)
-      expect(vmSpy.args[0][1]).is.an.object
-      expect(vmSpy.args[0][1].detail).equal(detail)
-
-      detail = { aaa: 111 }
-      vm.$broadcast('customTypeA', detail)
-      expect(vmSpy.args.length).eql(2)
-      expect(subVmSpy.args.length).eql(3)
-      expect(vmSpy.args[1][0]).equal(vm)
-      expect(vmSpy.args[1][1]).is.an.object
-      expect(vmSpy.args[1][1].detail).equal(detail)
-      expect(subVmSpy.args[1][0]).equal(vm._childrenVms[1])
-      expect(subVmSpy.args[1][1]).is.an.object
-      expect(subVmSpy.args[1][1].detail).equal(detail)
-
-      done()
-    })
-  })
-
-  it('dispatch from repeat vms', (done) => {
-    const vmSpy = sinon.spy()
-    const subVmSpy = sinon.spy()
-
-    customComponentMap.foo = {
-      data: function () {
-        return { list: [{ a: 1 }, { a: 2 }, { a: 3 }] }
-      },
-      template: {
-        type: 'container',
-        children: [{
-          type: 'bar', component: true,
-          repeat: function () { return this.list }
-        }]
-      },
-      events: {
-        customTypeA: function (e) {
-          vmSpy(this, e)
-        }
-      }
-    }
-    customComponentMap.bar = {
-      template: {
-        type: 'aaa'
-      },
-      events: {
-        customTypeA: function (e) {
-          subVmSpy(this, e)
-        }
-      }
-    }
-
-    const app = { doc, customComponentMap }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    checkReady(vm, function () {
-      doc.close()
-      expect(vm._childrenVms.length).eql(3)
-
-      let detail = { aaa: 111 }
-      const subVm = vm._childrenVms[1]
-      subVm.$emit('customTypeA', detail)
-      expect(vmSpy.args.length).eql(0)
-      expect(subVmSpy.args.length).eql(1)
-      expect(subVmSpy.args[0][0]).equal(subVm)
-      expect(subVmSpy.args[0][1]).is.an.object
-      expect(subVmSpy.args[0][1].detail).equal(detail)
-
-      detail = { aaa: 111 }
-      subVm.$dispatch('customTypeA', detail)
-      expect(vmSpy.args.length).eql(1)
-      expect(subVmSpy.args.length).eql(2)
-      expect(vmSpy.args[0][0]).equal(vm)
-      expect(vmSpy.args[0][1]).is.an.object
-      expect(vmSpy.args[0][1].detail).equal(detail)
-      expect(subVmSpy.args[1][0]).equal(subVm)
-      expect(subVmSpy.args[1][1]).is.an.object
-      expect(subVmSpy.args[1][1].detail).equal(detail)
-
-      done()
-    })
-  })
-
-  it('lifecycel events', (done) => {
-    const vmSpy = sinon.spy()
-
-    customComponentMap.foo = {
-      template: {
-        type: 'aaa',
-        children: [{ type: 'bar', component: true }]
-      },
-      data: function () {
-        return {
-          x: 1
-        }
-      },
-      init: function (e) {
-        vmSpy(this, e)
-        expect(this._data).is.not.ok
-        expect(this.x).is.not.ok
-        expect(this._parentEl).is.not.ok
-      },
-      created: function (e) {
-        vmSpy(this, e)
-        expect(this._data).is.ok
-        expect(this.x).is.ok
-        expect(this._parentEl).is.not.ok
-      },
-      ready: function (e) {
-        vmSpy(this, e)
-        expect(this._data).is.ok
-        expect(this.x).is.ok
-        expect(this._parentEl).is.ok
-        expect(this._parentEl.children.length).eql(1)
-      }
-    }
-    customComponentMap.bar = {
-      template: {
-        type: 'bbb'
-      },
-      created: function (e) {
-        vmSpy(this, e)
-      }
-    }
-
-    const app = { doc, customComponentMap }
-    const evSpy = sinon.spy()
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app }, null, null,
-      {
-        'hook:init': () => evSpy('hook:init'),
-        'hook:created': () => evSpy('hook:created'),
-        'hook:ready': () => evSpy('hook:ready')
-      }
-    )
-
-    checkReady(vm, function () {
-      doc.close()
-
-      expect(vmSpy.args.length).eql(4)
-
-      const subVm = vm._childrenVms[0]
-      const calls = vmSpy.args.map(args => [args[0], args[1].type])
-      expect(calls).eql([
-        [vm, 'hook:init'],
-        [vm, 'hook:created'],
-        [subVm, 'hook:created'],
-        [vm, 'hook:ready']
-      ])
-
-      expect(evSpy.callCount).to.be.equal(3)
-      expect(evSpy.args).to.deep.equal([
-        ['hook:init'],
-        ['hook:created'],
-        ['hook:ready']
-      ])
-
-      done()
-    })
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/vm/vm.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/vm/vm.js b/html5/test/unit/default/vm/vm.js
deleted file mode 100644
index 1ce0db3..0000000
--- a/html5/test/unit/default/vm/vm.js
+++ /dev/null
@@ -1,1975 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import Vm from '../../../../frameworks/legacy/vm'
-import { Document } from '../../../../runtime/vdom'
-import { init as resetTaskHandler } from '../../../../runtime/bridge/TaskCenter'
-import Differ from '../../../../frameworks/legacy/app/differ'
-
-const oriCallNative = global.callNative
-
-describe('generate virtual dom for a single vm', () => {
-  const spy = sinon.spy()
-  const spy1 = sinon.spy()
-  let doc
-  let customComponentMap
-  let differ
-
-  beforeEach(() => {
-    differ = new Differ('test')
-    doc = new Document('test', '', function (actions) {
-      actions.forEach((action) => {
-        spy.apply(null, ['test', action.method].concat(action.args))
-      })
-    })
-    customComponentMap = {}
-  })
-
-  afterEach(() => {
-    spy.reset()
-    spy1.reset()
-    doc.destroy()
-  })
-
-  it('$watch', () => {
-    const data = {
-      a: {
-        b: 1
-      }
-    }
-
-    customComponentMap.foo = {
-      template: {
-        type: 'container'
-      },
-      data: data
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(doc.body).is.an.object
-
-    vm.$watch(function () {
-      return this.a.b
-    }, (value) => {
-      expect(value).eql(2)
-    })
-
-    data.a.b = 2
-  })
-
-  it('vm.data is not a object', () => {
-    const data = 'hello'
-
-    customComponentMap.foo = {
-      template: {
-        type: 'container'
-      },
-      data: data
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(doc.body).is.an.object
-  })
-
-  it('no param parentVm', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container'
-      }
-    }
-
-    const vm = new Vm('foo', customComponentMap.foo, null)
-
-    expect(vm._app).is.an.object
-    expect(doc.body).is.an.object
-  })
-
-  it('no param options', () => {
-    const app = {}
-    const vm = new Vm('foo', null, { _app: app })
-
-    expect(vm._app).is.an.object
-    expect(doc.body).is.an.object
-  })
-
-  it('old method.ready', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container'
-      },
-      methods: {
-        ready: spy1
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(doc.body).is.an.object
-    expect(doc.body.type).eql('container')
-    expect(spy1).callCount(1)
-  })
-
-  it('generate an static element ', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        attr: {
-          a: 1,
-          b: 2,
-          static: ''
-        },
-        style: {
-          c: 3,
-          d: 4
-        }
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(vm._static).eql(true)
-    expect(doc.body).is.an.object
-    expect(doc.body.type).eql('container')
-    expect(doc.body.attr).eql({ a: 1, b: 2, static: '' })
-    expect(doc.body.style).eql({ c: 3, d: 4 })
-    expect(doc.body.children).is.an.array
-    expect(doc.body.children.length).eql(0)
-  })
-
-  it('generate an single element', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        attr: {
-          a: 1,
-          b: 2
-        },
-        style: {
-          c: 3,
-          d: 4
-        }
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(doc.body).is.an.object
-    expect(doc.body.type).eql('container')
-    expect(doc.body.attr).eql({ a: 1, b: 2 })
-    expect(doc.body.style).eql({ c: 3, d: 4 })
-    expect(doc.body.children).is.an.array
-    expect(doc.body.children.length).eql(0)
-  })
-
-  it('generate an element tree', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container', attr: { a: 1, b: 2 }, style: { c: 3, d: 4 },
-        children: [
-          { type: 'image', attr: { src: '<some image url>' }},
-          { type: 'text', attr: { value: '<some text content>' }}
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.attr).eql({ a: 1, b: 2 })
-    expect(el.style).eql({ c: 3, d: 4 })
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(2)
-
-    const image = el.children[0]
-    const text = el.children[1]
-    expect(image.type).eql('image')
-    expect(image.attr).eql({ src: '<some image url>' })
-    expect(text.type).eql('text')
-    expect(text.attr).eql({ value: '<some text content>' })
-  })
-
-  it('generate an element tree with component options', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'cell',
-        children: [
-          { type: 'text', attr: { value: '<some text content>' }}
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(spy.firstCall.args[0]).to.be.equal('test')
-    expect(spy.firstCall.args[1]).to.be.equal('createBody')
-    expect(spy.firstCall.args[2]).to.deep.equal({
-      ref: '_root',
-      type: 'cell',
-      attr: {
-        append: 'tree'
-      },
-      children: [{
-        ref: spy.firstCall.args[2].children[0].ref,
-        type: 'text',
-        attr: {
-          value: '<some text content>'
-        },
-        style: {}
-      }],
-      style: {}
-    })
-
-    expect(vm._app).equal(app)
-    const el = doc.body
-    expect(el.type).eql('cell')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(1)
-
-    const text = el.children[0]
-    expect(text.type).eql('text')
-    expect(text.attr).eql({ value: '<some text content>' })
-  })
-
-  it('generate an element tree with data binding', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container', attr: { a: 1, b: 2 }, style: { c: 3, d: 4 },
-        children: [
-          { type: 'image', attr: { src: function () { return this.x } }},
-          { type: 'text', attr: { value: function () { return this.n } }},
-          { type: 'text', attr: { value: function () { return this.m } }}
-        ]
-      },
-      data: {
-        x: '<some image url>', y: '<some text content>'
-      },
-      computed: {
-        n: function () {
-          return this.y.toUpperCase()
-        },
-        m: {
-          get: function () {
-            return this.y.toUpperCase()
-          },
-          set: function (v) {
-            this.y = v
-          }
-        }
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(vm.x).eql('<some image url>')
-    expect(vm.y).eql('<some text content>')
-    expect(vm.n).eql('<SOME TEXT CONTENT>')
-    expect(vm.m).eql('<SOME TEXT CONTENT>')
-
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.attr).eql({ a: 1, b: 2 })
-    expect(el.style).eql({ c: 3, d: 4 })
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(3)
-
-    const image = el.children[0]
-    const text = el.children[1]
-    const text2 = el.children[2]
-    expect(image.type).eql('image')
-    expect(image.attr).eql({ src: '<some image url>' })
-    expect(text.type).eql('text')
-    expect(text.attr).eql({ value: '<SOME TEXT CONTENT>' })
-    expect(text2.type).eql('text')
-    expect(text2.attr).eql({ value: '<SOME TEXT CONTENT>' })
-
-    vm.x = '<some image url>'
-    differ.flush()
-    expect(el).equal(doc.body)
-    expect(image).equal(el.children[0])
-    expect(text).equal(el.children[1])
-    expect(text2).equal(el.children[2])
-
-    vm.x = 'other string value'
-    differ.flush()
-    expect(el).equal(doc.body)
-    expect(image).equal(el.children[0])
-    expect(image.attr).eql({ src: 'other string value' })
-
-    vm.y = 'other string value'
-    differ.flush()
-    expect(el).equal(doc.body)
-    expect(text).equal(el.children[1])
-    expect(text.attr).eql({ value: 'OTHER STRING VALUE' })
-    expect(text2).equal(el.children[2])
-    expect(text2.attr).eql({ value: 'OTHER STRING VALUE' })
-
-    vm.m = 'third string value'
-    differ.flush()
-    expect(vm.x).eql('other string value')
-    expect(vm.y).eql('third string value')
-    expect(vm.n).eql('THIRD STRING VALUE')
-    expect(vm.m).eql('THIRD STRING VALUE')
-    expect(el).equal(doc.body)
-    expect(text).equal(el.children[1])
-    expect(text.attr).eql({ value: 'THIRD STRING VALUE' })
-    expect(text2).equal(el.children[2])
-    expect(text2.attr).eql({ value: 'THIRD STRING VALUE' })
-  })
-
-  it('generate an element tree with shown', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [
-          { type: 'prev' },
-          {
-            shown: function () { return this.y },
-            type: 'image', attr: { src: function () { return this.x } }
-          },
-          { type: 'next' }
-        ]
-      },
-      data: {
-        x: '<some image url>', y: true
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(vm.x).eql('<some image url>')
-    expect(vm.y).eql(true)
-
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(5)
-
-    const prev = el.children[0]
-    const starter = el.children[1]
-    const image = el.children[2]
-    const ender = el.children[3]
-    const next = el.children[4]
-    expect(prev.type).eql('prev')
-    expect(starter.type).eql('comment')
-    expect(ender.type).eql('comment')
-    expect(next.type).eql('next')
-    expect(image.type).eql('image')
-    expect(image.attr).eql({ src: '<some image url>' })
-
-    vm.y = false
-    differ.flush()
-
-    expect(el).equal(doc.body)
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(4)
-    expect(prev).equal(el.children[0])
-    expect(starter).equal(el.children[1])
-    expect(ender).equal(el.children[2])
-    expect(next).equal(el.children[3])
-
-    vm.y = true
-    differ.flush()
-
-    expect(el).equal(doc.body)
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(5)
-    expect(prev).equal(el.children[0])
-    expect(starter).equal(el.children[1])
-
-    const image2 = el.children[2]
-    expect(image2.type).eql('image')
-    expect(image2.attr).eql({ src: '<some image url>' })
-    expect(ender).equal(el.children[3])
-    expect(next).equal(el.children[4])
-  })
-
-  it('generate an element tree with repeat', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [
-          { type: 'prev' },
-          {
-            repeat: function () { return this.list },
-            type: 'image', attr: { src: function () { return this.x } }
-          },
-          { type: 'next' }
-        ]
-      },
-      data: {
-        x: '<some image url>',
-        list: [
-          { uid: 1, x: 1 }, { uid: 2, x: 2 }, { uid: 3 }
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(vm.x).eql('<some image url>')
-    expect(vm.list).eql([
-      { uid: 1, x: 1, $index: 0 },
-      { uid: 2, x: 2, $index: 1 },
-      { uid: 3, $index: 2 }])
-
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(7)
-
-    const prev = el.children[0]
-    const starter = el.children[1]
-    const ender = el.children[5]
-    const next = el.children[6]
-    expect(prev.type).eql('prev')
-    expect(starter.type).eql('comment')
-    expect(ender.type).eql('comment')
-    expect(next.type).eql('next')
-
-    expect(el.children[2].type).eql('image')
-    expect(el.children[2].attr).eql({ src: 1 })
-    expect(el.children[3].type).eql('image')
-    expect(el.children[3].attr).eql({ src: 2 })
-    expect(el.children[4].type).eql('image')
-    expect(el.children[4].attr).eql({ src: '<some image url>' })
-
-    vm.list[1].x = 3
-    differ.flush()
-    expect(el.children.length).eql(7)
-    expect(el.children[2].attr).eql({ src: 1 })
-    expect(el.children[3].attr).eql({ src: 3 })
-    expect(el.children[4].attr).eql({ src: '<some image url>' })
-
-    vm.x = 'other string value'
-    differ.flush()
-    expect(el.children.length).eql(7)
-    expect(el.children[2].attr).eql({ src: 1 })
-    expect(el.children[3].attr).eql({ src: 3 })
-    expect(el.children[4].attr).eql({ src: 'other string value' })
-
-    vm.list.push({ uid: 4, x: 4 })
-    vm.list.push({ uid: 5 }, { uid: 6, x: 6 })
-
-    differ.flush()
-
-    expect(el.children.length).eql(10)
-    expect(el.children[2].attr).eql({ src: 1 })
-    expect(el.children[3].attr).eql({ src: 3 })
-    expect(el.children[4].attr).eql({ src: 'other string value' })
-    expect(el.children[5].attr).eql({ src: 4 })
-    expect(el.children[6].attr).eql({ src: 'other string value' })
-    expect(el.children[7].attr).eql({ src: 6 })
-
-    vm.list.splice(1, 2, { uid: 7, x: 7 })
-    differ.flush()
-
-    expect(el.children.length).eql(9)
-    expect(el.children[2].attr).eql({ src: 1 })
-    expect(el.children[3].attr).eql({ src: 7 })
-    expect(el.children[4].attr).eql({ src: 4 })
-    expect(el.children[5].attr).eql({ src: 'other string value' })
-    expect(el.children[6].attr).eql({ src: 6 })
-
-    vm.list.$set(0, { uid: 321, x: 32 })
-    vm.list.$set(10, { uid: 8889, x: 8888 })
-    differ.flush()
-
-    expect(el.children.length).eql(10)
-    expect(el.children[2].attr).eql({ src: 32 })
-    expect(el.children[7].attr).eql({ src: 8888 })
-
-    vm.list.unshift({ uid: 12345, x: 123456 })
-    differ.flush()
-
-    expect(el.children.length).eql(11)
-    expect(el.children[2].attr).eql({ src: 123456 })
-  })
-
-  it('generate an static element tree with shown and repeat', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        attr: {
-          static: ''
-        },
-        children: [
-          { type: 'prev' },
-          {
-            shown: function () { return this.x % 2 === 0 },
-            repeat: function () { return this.list },
-            type: 'image', attr: { src: function () { return this.x } }
-          },
-          { type: 'next' }
-        ]
-      },
-      data: {
-        x: '<some image url>',
-        list: [
-          { uid: 1, x: 1 }, { uid: 2, x: 2 }, { uid: 3 }
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(vm._static).equal(true)
-    expect(vm.x).eql('<some image url>')
-    expect(vm.list).eql([
-      { uid: 1, x: 1, $index: 0 },
-      { uid: 2, x: 2, $index: 1 },
-      { uid: 3, $index: 2 }])
-
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(11)
-
-    const prev = el.children[0]
-    const next = el.children[10]
-    expect(prev.type).eql('prev')
-    expect(el.children[1].type).eql('comment')
-    expect(el.children[1].value).eql('start')
-    expect(el.children[9].type).eql('comment')
-    expect(el.children[9].value).eql('end')
-
-    expect(el.children[2].type).eql('comment')
-    expect(el.children[2].value).eql('start')
-    expect(el.children[3].type).eql('comment')
-    expect(el.children[3].value).eql('end')
-
-    expect(el.children[4].type).eql('comment')
-    expect(el.children[4].value).eql('start')
-    expect(el.children[5].type).eql('image')
-    expect(el.children[5].attr).eql({ src: 2 })
-    expect(el.children[6].type).eql('comment')
-    expect(el.children[6].value).eql('end')
-
-    expect(el.children[7].type).eql('comment')
-    expect(el.children[7].value).eql('start')
-    expect(el.children[8].type).eql('comment')
-    expect(el.children[8].value).eql('end')
-    expect(next.type).eql('next')
-
-    vm.list[0].x = 4
-    differ.flush()
-
-    expect(el.children.length).eql(11)
-    expect(el.children[5].type).eql('image')
-    expect(el.children[5].attr).eql({ src: 2 })
-  })
-
-  it('generate an element tree which root element with shown', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        shown: function () { return this.x % 2 === 0 }
-      },
-      data: {
-        x: 2
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(vm.x).eql(2)
-  })
-
-  it('append tree', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        attr: {
-          append: 'tree'
-        },
-        children: [
-          { type: 'a' },
-          { repeat: {}}
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-  })
-
-  it('repeat is not a function', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [
-          { type: 'a' },
-          { repeat: {}}
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-  })
-
-  it('repeat oldStyle with item which not a object', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [
-          { type: 'a' },
-          { repeat: function () { return [1, 2, 3] } }
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-  })
-
-  it('classList length is zero', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        classList: []
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-  })
-
-  it('classList is a function', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        classList: function () { return [] }
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-  })
-
-  it('generate an element tree which root element with repeat', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        repeat: function () { return this.list }
-      },
-      data: {
-        list: [
-          { uid: 1, x: 1 }, { uid: 2, x: 2 }, { uid: 3 }
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(vm.list).eql([
-      { uid: 1, x: 1 },
-      { uid: 2, x: 2 },
-      { uid: 3 }
-    ])
-  })
-
-  it('generate an element tree with shown and repeat', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [
-          { type: 'prev' },
-          {
-            shown: function () { return this.x % 2 === 0 },
-            repeat: function () { return this.list },
-            type: 'image', attr: { src: function () { return this.x } }
-          },
-          { type: 'next' }
-        ]
-      },
-      data: {
-        x: '<some image url>',
-        list: [
-          { uid: 1, x: 1 }, { uid: 2, x: 2 }, { uid: 3 }
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(vm.x).eql('<some image url>')
-    expect(vm.list).eql([
-      { uid: 1, x: 1, $index: 0 },
-      { uid: 2, x: 2, $index: 1 },
-      { uid: 3, $index: 2 }])
-
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(11)
-
-    const prev = el.children[0]
-    const next = el.children[10]
-    expect(prev.type).eql('prev')
-    expect(el.children[1].type).eql('comment')
-    expect(el.children[1].value).eql('start')
-    expect(el.children[9].type).eql('comment')
-    expect(el.children[9].value).eql('end')
-
-    expect(el.children[2].type).eql('comment')
-    expect(el.children[2].value).eql('start')
-    expect(el.children[3].type).eql('comment')
-    expect(el.children[3].value).eql('end')
-
-    expect(el.children[4].type).eql('comment')
-    expect(el.children[4].value).eql('start')
-    expect(el.children[5].type).eql('image')
-    expect(el.children[5].attr).eql({ src: 2 })
-    expect(el.children[6].type).eql('comment')
-    expect(el.children[6].value).eql('end')
-
-    expect(el.children[7].type).eql('comment')
-    expect(el.children[7].value).eql('start')
-    expect(el.children[8].type).eql('comment')
-    expect(el.children[8].value).eql('end')
-    expect(next.type).eql('next')
-
-    vm.list[0].x = 4
-    differ.flush()
-
-    expect(el.children.length).eql(12)
-    expect(el.children[3].type).eql('image')
-    expect(el.children[3].attr).eql({ src: 4 })
-    expect(el.children[6].type).eql('image')
-    expect(el.children[6].attr).eql({ src: 2 })
-  })
-
-  it('generate more than one group of elements with the same repeat data', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [
-          { type: 'begin' },
-          {
-            repeat: {
-              expression: function () { return this.list },
-              value: 'v1'
-            },
-            type: 'image', attr: { src: function () { return this.v1.x } }
-          },
-          { type: 'middle' },
-          {
-            repeat: {
-              expression: function () { return this.list },
-              value: 'v2'
-            },
-            type: 'text', attr: { value: function () { return this.v2.x } }
-          },
-          { type: 'end' }
-        ]
-      },
-      data: {
-        list: [
-          { uid: 1, x: 1 }, { uid: 2, x: 2 }, { uid: 3 }
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(vm.list).eql([
-      { uid: 1, x: 1 },
-      { uid: 2, x: 2 },
-      { uid: 3 }])
-
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-
-    // [begin, comment, image x 3, comment, middle, comment, text x 3, comment, end]
-    expect(el.children.length).eql(13)
-
-    expect(el.children[0].type).eql('begin')
-    expect(el.children[1].type).eql('comment')
-    expect(el.children[1].value).eql('start')
-    expect(el.children[2].type).eql('image')
-    expect(el.children[2].attr).eql({ src: 1 })
-    expect(el.children[3].type).eql('image')
-    expect(el.children[3].attr).eql({ src: 2 })
-    expect(el.children[4].type).eql('image')
-    expect(el.children[4].attr).eql({})
-    expect(el.children[5].type).eql('comment')
-    expect(el.children[5].value).eql('end')
-    expect(el.children[6].type).eql('middle')
-    expect(el.children[7].type).eql('comment')
-    expect(el.children[7].value).eql('start')
-    expect(el.children[8].type).eql('text')
-    expect(el.children[8].attr).eql({ value: 1 })
-    expect(el.children[9].type).eql('text')
-    expect(el.children[9].attr).eql({ value: 2 })
-    expect(el.children[10].type).eql('text')
-    expect(el.children[10].attr).eql({})
-    expect(el.children[11].type).eql('comment')
-    expect(el.children[11].value).eql('end')
-    expect(el.children[12].type).eql('end')
-
-    vm.list[0].x = 4
-    differ.flush()
-
-    expect(el.children.length).eql(13)
-    expect(el.children[2].attr).eql({ src: 4 })
-    expect(el.children[8].attr).eql({ value: 4 })
-
-    // [begin, comment, image x 4, comment, middle, comment, text x 4, comment, end]
-    vm.list.push({ uid: 10, x: 10 })
-    differ.flush()
-
-    expect(el.children.length).eql(15)
-    expect(el.children[5].attr).eql({ src: 10 })
-    expect(el.children[12].attr).eql({ value: 10 })
-
-    // [begin, comment, image x 2, comment, middle, comment, text x 2, comment, end]
-    vm.list = [
-      { uid: 100, x: 100 }, { uid: 1, x: 1 }
-    ]
-    differ.flush()
-
-    expect(el.children.length).eql(11)
-    expect(el.children[2].attr).eql({ src: 100 })
-    expect(el.children[3].attr).eql({ src: 1 })
-    expect(el.children[7].attr).eql({ value: 100 })
-    expect(el.children[8].attr).eql({ value: 1 })
-
-    vm.list[0].x = 4
-    differ.flush()
-
-    expect(el.children.length).eql(11)
-    expect(el.children[2].attr).eql({ src: 4 })
-    expect(el.children[3].attr).eql({ src: 1 })
-    expect(el.children[7].attr).eql({ value: 4 })
-    expect(el.children[8].attr).eql({ value: 1 })
-
-    vm.list[1].x = 5
-    differ.flush()
-
-    expect(el.children.length).eql(11)
-    expect(el.children[2].attr).eql({ src: 4 })
-    expect(el.children[3].attr).eql({ src: 5 })
-    expect(el.children[7].attr).eql({ value: 4 })
-    expect(el.children[8].attr).eql({ value: 5 })
-  })
-
-  it('generate an element with external data', () => {
-    customComponentMap.foo = {
-      data: () => {
-        return { a: 10, b: 20 }
-      },
-      template: {
-        type: 'container',
-        attr: {
-          x: function () { return this.a },
-          y: function () { return this.b }
-        }
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app }, null, { a: 1000 })
-
-    expect(vm._app).equal(app)
-
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.attr).eql({ x: 1000, y: 20 })
-
-    vm.a = 100
-    vm.b = 200
-    differ.flush()
-
-    expect(el).equal(doc.body)
-    expect(el.attr).eql({ x: 100, y: 200 })
-  })
-})
-
-describe('generate virtual dom for sub vm', () => {
-  let doc
-  let customComponentMap
-  let differ
-
-  beforeEach(() => {
-    global.callNative = function () {}
-    resetTaskHandler()
-    doc = new Document('test', null, null)
-    customComponentMap = {}
-    differ = new Differ('test')
-  })
-
-  afterEach(() => {
-    doc.destroy()
-    global.callNative = oriCallNative
-  })
-
-  it('generate sub elements', () => {
-    customComponentMap.foo = {
-      data: function () {
-        return {
-          showbar1: false,
-          showbar2: false,
-          bar2list: [{
-            id: 'bar2-1'
-          }, {
-            id: 'bar2-2'
-          }]
-        }
-      },
-      template: {
-        type: 'div',
-        children: [
-          { type: 'bar', id: 'bar', component: true,
-            events: { click: 'handleClick' }
-          },
-          { type: 'bar1',
-            shown: function () { return this.showbar1 },
-            id: 'bar1', component: true
-          },
-          { type: 'bar2',
-              shown: function () { return this.showbar2 },
-              repeat: function () { return this.bar2list },
-              id: function () { return this.id },
-              component: true
-          }
-        ]
-      },
-      methods: {
-        handleClick: sinon.spy()
-      }
-    }
-    customComponentMap.bar = {
-      template: {
-        type: 'container',
-        children: [{ type: 'aaa' }, { type: 'bbb' }]
-      }
-    }
-    customComponentMap.bar1 = {
-      template: {
-        type: 'container',
-        children: [{ type: 'aaaa' }, { type: 'bbbb' }]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(vm._rootEl).to.deep.equal(vm._parentEl.children[0])
-
-    expect(vm._childrenVms.length).to.be.equal(1)
-    expect(vm._childrenVms[0]).to.deep.equal(vm._ids['bar'].vm)
-    expect(vm._childrenVms[0]._rootEl).to.deep.equal(vm._ids['bar'].el)
-
-    const el = doc.body
-    expect(el.type).eql('div')
-    expect(el.pureChildren).is.an.array
-    expect(el.pureChildren.length).eql(1)
-
-    const sub = el.children[0]
-    expect(sub.type).eql('container')
-    expect(sub.children).is.an.array
-    expect(sub.children.length).eql(2)
-    expect(sub.children[0].type).eql('aaa')
-    expect(sub.children[1].type).eql('bbb')
-    expect(sub.event.click).is.a.function
-
-    const spy = customComponentMap.foo.methods.handleClick
-    sub.event.click.handler(1, 2, 3)
-    expect(spy.args.length).eql(1)
-    expect(spy.args[0]).eql([1, 2, 3])
-
-    vm.showbar1 = true
-    differ.flush()
-
-    expect(vm._childrenVms.length).to.be.equal(2)
-    expect(vm._childrenVms[1]).to.deep.equal(vm._ids['bar1'].vm)
-    expect(vm._childrenVms[1]._rootEl).to.deep.equal(vm._ids['bar1'].el)
-
-    vm.showbar2 = true
-    differ.flush()
-
-    expect(vm._ids['bar2-1'].vm).to.be.not.undefined
-    expect(vm._ids['bar2-1'].el).to.be.not.undefined
-    expect(vm._ids['bar2-2'].vm).to.be.not.undefined
-    expect(vm._ids['bar2-2'].el).to.be.not.undefined
-  })
-
-  it('generate sub element with static', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        attr: {
-          static: ''
-        },
-        children: [{ type: 'bar', component: true }]
-      }
-    }
-    customComponentMap.bar = {
-      replace: true,
-      template: {
-        type: 'container',
-        children: [{ type: 'aaa' }]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    expect(vm._static).eql(true)
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(1)
-
-    const aaa = el.children[0]
-    expect(aaa.type).eql('aaa')
-    expect(vm._childrenVms.length).to.be.equal(1)
-    expect(vm._childrenVms[0]._static).eql(true)
-  })
-
-  it('generate replaced sub element', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [{ type: 'bar', component: true }]
-      }
-    }
-    customComponentMap.bar = {
-      replace: true,
-      template: {
-        type: 'container',
-        children: [{ type: 'aaa' }]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(1)
-
-    const aaa = el.children[0]
-    expect(aaa.type).eql('aaa')
-  })
-
-  it('generate replaced sub elements', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [{ type: 'bar', component: true }]
-      }
-    }
-    customComponentMap.bar = {
-      replace: true,
-      template: {
-        type: 'container',
-        children: [{ type: 'aaa' }, { type: 'bbb' }]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(4)
-
-    expect(el.children[0].type).eql('comment')
-    const aaa = el.children[1]
-    expect(aaa.type).eql('aaa')
-    const bbb = el.children[2]
-    expect(bbb.type).eql('bbb')
-    expect(el.children[3].type).eql('comment')
-  })
-
-  it('generate sub elements with no props', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [{ type: 'bar', component: true, attr: { x: 10, y: 20 }}]
-      }
-    }
-    customComponentMap.bar = {
-      data: () => {
-        return { x: 1, y: 2 }
-      },
-      template: {
-        type: 'container',
-        children: [
-          { type: 'aaa', attr: { a: function () { return this.x } }},
-          { type: 'bbb', attr: { b: function () { return this.y } }}
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(1)
-
-    const sub = el.children[0]
-    const aaa = sub.children[0]
-    expect(aaa.type).eql('aaa')
-    expect(aaa.attr).eql({ a: 10 })
-    const bbb = sub.children[1]
-    expect(bbb.type).eql('bbb')
-    expect(bbb.attr).eql({ b: 20 })
-  })
-
-  it('generate sub elements with array props', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [{ type: 'bar', component: true, attr: { x: 10, y: 20 }}]
-      }
-    }
-    customComponentMap.bar = {
-      props: ['x'],
-      data: () => {
-        return { x: 1, y: 2 }
-      },
-      template: {
-        type: 'container',
-        children: [
-          { type: 'aaa', attr: { a: function () { return this.x } }},
-          { type: 'bbb', attr: { b: function () { return this.y } }}
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(1)
-
-    const sub = el.children[0]
-    const aaa = sub.children[0]
-    expect(aaa.type).eql('aaa')
-    expect(aaa.attr).eql({ a: 10 })
-    const bbb = sub.children[1]
-    expect(bbb.type).eql('bbb')
-    expect(bbb.attr).eql({ b: 2 })
-  })
-
-  it('generate sub elements with props', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [{ type: 'bar', component: true, attr: { x: 10, y: 20 }}]
-      }
-    }
-    customComponentMap.bar = {
-      props: { x: Number },
-      data: () => {
-        return { x: 1, y: 2 }
-      },
-      template: {
-        type: 'container',
-        children: [
-          { type: 'aaa', attr: { a: function () { return this.x } }},
-          { type: 'bbb', attr: { b: function () { return this.y } }}
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(1)
-
-    const sub = el.children[0]
-    const aaa = sub.children[0]
-    expect(aaa.type).eql('aaa')
-    expect(aaa.attr).eql({ a: 10 })
-    const bbb = sub.children[1]
-    expect(bbb.type).eql('bbb')
-    expect(bbb.attr).eql({ b: 2 })
-  })
-
-  it('generate sub elements with data', () => {
-    customComponentMap.foo = {
-      data: () => {
-        return { a: 10, b: 20 }
-      },
-      template: {
-        type: 'container',
-        children: [{ type: 'bar', component: true, attr: {
-          x: function () { return this.a },
-          y: function () { return this.b }
-        }}]
-      }
-    }
-    customComponentMap.bar = {
-      props: { x: Number },
-      data: () => {
-        return { x: 1, y: 2 }
-      },
-      template: {
-        type: 'container',
-        children: [
-          { type: 'aaa', attr: { a: function () { return this.x } }},
-          { type: 'bbb', attr: { b: function () { return this.y } }}
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(1)
-
-    const sub = el.children[0]
-    const aaa = sub.children[0]
-    expect(aaa.type).eql('aaa')
-    expect(aaa.attr).eql({ a: 10 })
-    const bbb = sub.children[1]
-    expect(bbb.type).eql('bbb')
-    expect(bbb.attr).eql({ b: 2 })
-
-    vm.a = 100
-    vm.b = 200
-    differ.flush()
-
-    expect(el).equal(doc.body)
-    expect(sub).equal(el.children[0])
-    expect(aaa).equal(sub.children[0])
-    expect(aaa.attr).eql({ a: 100 })
-    expect(bbb).equal(sub.children[1])
-    expect(bbb.attr).eql({ b: 2 })
-  })
-
-  it('generate sub elements with shown data', () => {
-    customComponentMap.foo = {
-      data: () => {
-        return { a: 10, b: 20 }
-      },
-      template: {
-        type: 'container',
-        children: [{ type: 'bar', component: true, attr: {
-          x: function () { return this.a },
-          y: function () { return this.b }
-        }, shown: function () { return this.a < 15 } }]
-      }
-    }
-    customComponentMap.bar = {
-      props: { x: Number },
-      data: () => {
-        return { x: 1, y: 2 }
-      },
-      template: {
-        type: 'container',
-        children: [
-          { type: 'aaa', attr: { a: function () { return this.x } }},
-          { type: 'bbb', attr: { b: function () { return this.y } }}
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(3)
-
-    const sub = el.children[1]
-    const aaa = sub.children[0]
-    expect(aaa.type).eql('aaa')
-    expect(aaa.attr).eql({ a: 10 })
-    const bbb = sub.children[1]
-    expect(bbb.type).eql('bbb')
-    expect(bbb.attr).eql({ b: 2 })
-
-    vm.a = 20
-    differ.flush()
-
-    expect(el).equal(doc.body)
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(2)
-  })
-
-  it('generate sub elements with repeat data', () => {
-    customComponentMap.foo = {
-      data: () => {
-        return { list: [{ uid: 1, a: 1 }, { uid: 2, a: 2 }, { uid: 3, a: 3 }] }
-      },
-      template: {
-        type: 'container',
-        children: [{ type: 'bar', component: true, attr: {
-          x: function () { return this.a },
-          y: function () { return this.b }
-        }, repeat: function () { return this.list } }]
-      }
-    }
-    customComponentMap.bar = {
-      props: { x: Number },
-      data: () => {
-        return { x: 1, y: 2 }
-      },
-      template: {
-        type: 'container',
-        children: [
-          { type: 'aaa', attr: { a: function () { return this.x } }},
-          { type: 'bbb', attr: { b: function () { return this.y } }}
-        ]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(vm._app).equal(app)
-
-    const el = doc.body
-    expect(el.type).eql('container')
-    expect(el.children).is.an.array
-    expect(el.children.length).eql(5)
-
-    const sub1 = el.children[1]
-    const aaa1 = sub1.children[0]
-    expect(aaa1.type).eql('aaa')
-    expect(aaa1.attr).eql({ a: 1 })
-    const bbb1 = sub1.children[1]
-    expect(bbb1.type).eql('bbb')
-    expect(bbb1.attr).eql({ b: 2 })
-
-    const sub2 = el.children[2]
-    const aaa2 = sub2.children[0]
-    expect(aaa2.type).eql('aaa')
-    expect(aaa2.attr).eql({ a: 2 })
-    const bbb2 = sub2.children[1]
-    expect(bbb2.type).eql('bbb')
-    expect(bbb2.attr).eql({ b: 2 })
-
-    const sub3 = el.children[3]
-    const aaa3 = sub3.children[0]
-    expect(aaa3.type).eql('aaa')
-    expect(aaa3.attr).eql({ a: 3 })
-    const bbb3 = sub3.children[1]
-    expect(bbb3.type).eql('bbb')
-    expect(bbb3.attr).eql({ b: 2 })
-  })
-
-  it('generate sub elements with content', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [{ type: 'bar', component: true, children: [
-          { type: 'a' }, { type: 'b', attr: { bbb: function () { return this.x } }}
-        ] }]
-      },
-      data: () => {
-        return {
-          x: 1, y: 2
-        }
-      }
-    }
-    customComponentMap.bar = {
-      replace: true,
-      template: {
-        type: 'container',
-        children: [{ type: 'aaa' }, { type: 'content' }, { type: 'bbb' }]
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    const el = doc.body
-
-    expect(el.type).eql('container')
-    // [comment, aaa, comment, a, b, comment, bbb, comment]
-    expect(el.children.length).eql(8)
-    expect(el.children[0].type).eql('comment')
-    expect(el.children[2].type).eql('comment')
-    expect(el.children[5].type).eql('comment')
-    expect(el.children[7].type).eql('comment')
-
-    expect(el.pureChildren.length).eql(4)
-    expect(el.pureChildren[0].type).eql('aaa')
-    expect(el.pureChildren[1].type).eql('a')
-    expect(el.pureChildren[2].type).eql('b')
-    expect(el.pureChildren[2].attr).eql({ bbb: 1 })
-    expect(el.pureChildren[3].type).eql('bbb')
-
-    vm.x = 10
-    differ.flush()
-    expect(el.pureChildren[2].attr).eql({ bbb: 10 })
-  })
-
-  it('generate sub elements with dynamic types', (done) => {
-    customComponentMap.foo = {
-      data: {
-        x: 'bar',
-        y: 'hello'
-      },
-      template: {
-        type: 'div',
-        children: [{
-          type: function () { return this.x },
-          attr: { value: function () { return this.y } }
-        }]
-      }
-    }
-    customComponentMap.bar = {
-      template: {
-        type: 'text',
-        attr: { value: 'bar' }
-      }
-    }
-    customComponentMap.baz = {
-      data: function () {
-        return {
-          value: ''
-        }
-      },
-      template: {
-        type: 'text',
-        attr: { value: function () { return this.value + ' baz' } }
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(doc.body.pureChildren[0].type).eql('text')
-    expect(doc.body.pureChildren[0].attr).eql({ value: 'bar' })
-
-    vm.x = 'baz'
-    expect(doc.body.pureChildren[0].type).eql('text')
-    expect(doc.body.pureChildren[0].attr).eql({ value: 'hello baz' })
-
-    vm.y = 'bye'
-    setTimeout(() => {
-      expect(doc.body.pureChildren[0].type).eql('text')
-      expect(doc.body.pureChildren[0].attr).eql({ value: 'bye baz' })
-
-      vm.x = 'text'
-      expect(doc.body.pureChildren[0].type).eql('text')
-      expect(doc.body.pureChildren[0].attr).eql({ value: 'bye' })
-
-      done()
-    })
-  })
-
-  it('generate sub elements with repeat dynamic types', (done) => {
-    customComponentMap.foo = {
-      data: {
-        list: [
-          { uid: 1, type: 'bar' },
-          { uid: 2, type: 'baz' },
-          { uid: 3, type: 'bar' },
-          { uid: 4, type: 'baz' }
-        ]
-      },
-      template: {
-        type: 'div',
-        children: [{
-          type: function () { return this.v.type },
-          repeat: {
-            expression: function () { return this.list },
-            trackBy: 'uid',
-            value: 'v'
-          }
-        }]
-      }
-    }
-    customComponentMap.bar = {
-      template: {
-        type: 'text',
-        attr: { value: 'bar' }
-      }
-    }
-    customComponentMap.baz = {
-      template: {
-        type: 'text',
-        attr: { value: 'baz' }
-      }
-    }
-
-    const app = { doc, customComponentMap, differ }
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(doc.body.pureChildren.length).eql(4)
-    expect(doc.body.pureChildren.map(el => el.attr.value)).eql(['bar', 'baz', 'bar', 'baz'])
-
-    vm.list.splice(2, 0, { uid: 5, type: 'baz' })
-    setTimeout(() => {
-      expect(doc.body.pureChildren.length).eql(5)
-      expect(doc.body.pureChildren.map(el => el.attr.value)).eql(['bar', 'baz', 'baz', 'bar', 'baz'])
-
-      done()
-    })
-  })
-})
-
-describe('generate dom actions', () => {
-  let doc, app, spy, customComponentMap, differ
-
-  beforeEach(() => {
-    spy = sinon.spy()
-    doc = new Document('foo', '', function (actions) {
-      actions.forEach((action) => {
-        spy.apply(null, ['bar', action.method].concat(action.args))
-      })
-    })
-    differ = new Differ('foo')
-    customComponentMap = {}
-    app = { doc, customComponentMap, differ }
-  })
-
-  afterEach(() => {
-    customComponentMap = {}
-    doc.destroy()
-    app = doc = spy = null
-  })
-
-  it('received create body and add element actions', () => {
-    const handler = sinon.spy()
-
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        attr: {
-          a: 1,
-          b: 2
-        },
-        style: {
-          c: 3,
-          d: 4
-        },
-        classList: ['classA'],
-        events: { click: 'handleClick' }
-      },
-      style: {
-        classA: { d: 5, e: 6 }
-      },
-      methods: {
-        handleClick: handler
-      }
-    }
-
-    new Vm('foo', customComponentMap.foo, { _app: app })
-    const el = {
-      ref: '_root',
-      type: 'container',
-      attr: { a: 1, b: 2 }, style: { c: 3, d: 4, e: 6 },
-      event: ['click']
-    }
-
-    expect(spy.args.length).eql(1)
-    expect(spy.args[0]).eql(['bar', 'createBody', el])
-    expect(doc.body.event.click).is.a.function
-    expect(handler.args.length).eql(0)
-    doc.body.fireEvent('click')
-    expect(handler.args.length).eql(0)
-  })
-
-  it('received actions for a template with if & repeat', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [
-          { type: 'prev' },
-          {
-            shown: function () { return this.x % 2 === 0 },
-            repeat: function () { return this.list },
-            trackBy: 'uid',
-            type: 'image', attr: { src: function () { return this.x } }
-          },
-          { type: 'next' }
-        ]
-      },
-      data: {
-        x: '<some image url>',
-        list: [
-          { uid: 1, x: 1 }, { uid: 2, x: 2 }, { uid: 3 }
-        ]
-      }
-    }
-
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    const pureChildren = doc.body.pureChildren
-    const el = { ref: '_root', type: 'container', attr: {}, style: {}}
-    const prev = {
-      ref: pureChildren[0].ref,
-      type: 'prev', attr: {}, style: {}
-    }
-    const img = {
-      ref: pureChildren[1].ref,
-      type: 'image', attr: { src: 2 }, style: {}
-    }
-    const next = {
-      ref: pureChildren[2].ref,
-      type: 'next', attr: {}, style: {}
-    }
-
-    expect(spy.args.length).eql(4)
-    expect(spy.args[0]).eql(['bar', 'createBody', el])
-    expect(spy.args[1]).eql(['bar', 'addElement', '_root', prev, -1])
-    expect(spy.args[2]).eql(['bar', 'addElement', '_root', img, 1])
-    expect(spy.args[3]).eql(['bar', 'addElement', '_root', next, -1])
-
-    vm.list[1].x = 3
-    differ.flush()
-
-    // [1, 3, undefined]
-    expect(spy.args.length).eql(5)
-    expect(spy.args[4]).eql(['bar', 'removeElement', img.ref])
-
-    vm.list[1].x = 12
-    differ.flush()
-
-    // [1, !12, undefined]
-    expect(spy.args.length).eql(6)
-    img.ref = pureChildren[1].ref
-    img.attr.src = 12
-    expect(spy.args[5]).eql(['bar', 'addElement', '_root', img, 1])
-
-    vm.x = 'other string value'
-    differ.flush()
-
-    expect(spy.args.length).eql(6)
-
-    vm.list.push({ uid: 4, x: 4 })
-    vm.list.push({ uid: 5 }, { uid: 6, x: 6 })
-    differ.flush()
-
-    // [1, !12, undefined, !4, undefined, !6]
-    const img2 = {
-      ref: pureChildren[2].ref,
-      type: 'image', attr: { src: 4 }, style: {}
-    }
-    const img3 = {
-      ref: pureChildren[3].ref,
-      type: 'image', attr: { src: 6 }, style: {}
-    }
-    expect(spy.args.length).eql(8)
-    expect(spy.args[6]).eql(['bar', 'addElement', '_root', img2, 2])
-    expect(spy.args[7]).eql(['bar', 'addElement', '_root', img3, 3])
-
-    const temp1 = vm.list[1] // 12
-    const temp2 = vm.list[5] // 6
-    // vm.list.splice(0, 6, temp2, {uid: 7, x: 7}, temp1)
-    vm.list = []
-    vm.list.push(temp2, { uid: 7, x: 7 }, temp1)
-    differ.flush()
-
-    // [!6, 7, !12]
-    expect(spy.args.length).eql(10)
-    expect(spy.args[8]).eql(['bar', 'removeElement', img2.ref])
-    expect(spy.args[9]).eql(['bar', 'moveElement', img3.ref, '_root', 1])
-  })
-
-  it('received actions for element updates', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container', attr: { a: 1, b: 2 }, style: { c: 3, d: 4 },
-        children: [
-          { type: 'image', attr: { src: function () { return this.x } }},
-          { type: 'text', attr: { value: function () { return this.y } }}
-        ]
-      },
-      data: {
-        x: '<some image url>', y: '<some text content>'
-      }
-    }
-
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-    const pureChildren = doc.body.pureChildren
-    const length = spy.args.length
-
-    vm.x = '<some image url>'
-    differ.flush()
-    expect(spy.args.length - length).eql(0)
-
-    vm.x = 'other string value'
-    differ.flush()
-    const change = [
-      'bar', 'updateAttrs',
-      pureChildren[0].ref,
-      { src: 'other string value' }
-    ]
-    expect(spy.args.length - length).eql(1)
-    expect(spy.args[length]).eql(change)
-  })
-
-  it('received no action when no virtual dom different', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container', children: [
-          {
-            type: 'text',
-            shown: function () { return this.name.length > 3 },
-            attr: {
-              value: function () { return this.name.toUpperCase() }
-            }
-          }
-        ]
-      },
-      data: {
-        name: 'Mike'
-      }
-    }
-
-    const vm = new Vm('foo', customComponentMap.foo, { _app: app })
-
-    expect(doc.body.pureChildren.length).eql(1)
-    const text = doc.body.pureChildren[0]
-    expect(text.attr.value).eql('MIKE')
-
-    const initCalls = spy.args.length
-
-    vm.name = 'MiKe'
-    differ.flush()
-
-    expect(spy.args.length).eql(initCalls)
-
-    text.setAttr('value', 'MIKE')
-    differ.flush()
-
-    expect(spy.args.length).eql(initCalls)
-
-    text.setAttr('value', 'STEVE')
-    differ.flush()
-
-    expect(spy.args.length).eql(initCalls + 1)
-    expect(spy.args[initCalls]).eql([
-      'bar', 'updateAttrs', text.ref, { value: 'STEVE' }])
-
-    vm.name = 'Steve'
-    differ.flush()
-
-    expect(spy.args.length).eql(initCalls + 1)
-  })
-
-  it('received actions for components', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'container',
-        children: [{ type: 'bar', component: true }]
-      }
-    }
-    customComponentMap.bar = {
-      template: {
-        type: 'container',
-        children: [{ type: 'aaa' }, { type: 'bbb' }]
-      }
-    }
-
-    new Vm('foo', customComponentMap.foo, { _app: app })
-    const pureChildren = doc.body.pureChildren
-    const first = pureChildren[0]
-    const second = first.pureChildren[0]
-    const third = first.pureChildren[1]
-    expect(spy.args.length).eql(4)
-    let el = { ref: '_root', type: 'container', attr: {}, style: {}}
-    expect(spy.args[0]).eql(['bar', 'createBody', el])
-    el = { ref: first.ref, type: 'container', attr: {}, style: {}}
-    expect(spy.args[1]).eql(['bar', 'addElement', '_root', el, -1])
-    el = { ref: second.ref, type: 'aaa', attr: {}, style: {}}
-    expect(spy.args[2]).eql(['bar', 'addElement', first.ref, el, -1])
-    el = { ref: third.ref, type: 'bbb', attr: {}, style: {}}
-    expect(spy.args[3]).eql(['bar', 'addElement', first.ref, el, -1])
-  })
-
-  it('received actions for complicated components', () => {
-    customComponentMap.foo = {
-      data: () => {
-        return { list: [{ uid: 1, a: 1 }, { uid: 2, a: 2 }, { uid: 3, a: 3 }] }
-      },
-      template: {
-        type: 'container',
-        children: [{ type: 'bar', component: true, attr: {
-          x: function () { return this.a },
-          y: function () { return this.b }
-        }, repeat: function () { return this.list } }]
-      }
-    }
-    customComponentMap.bar = {
-      props: { x: Number },
-      data: () => {
-        return { x: 1, y: 2 }
-      },
-      template: {
-        type: 'container',
-        children: [
-          { type: 'aaa', attr: { a: function () { return this.x } }},
-          { type: 'bbb', attr: { b: function () { return this.y } }}
-        ]
-      }
-    }
-
-    new Vm('foo', customComponentMap.foo, { _app: app })
-    const pureChildren = doc.body.pureChildren
-    const first = pureChildren[0]
-    const second = pureChildren[1]
-    const third = pureChildren[2]
-
-    /* eslint-disable indent */
-    // expect(spy.args[0]).eql([ 'bar', 'createBody', 'container' ])
-    expect(spy.args[0]).eql(['bar', 'createBody', { ref: '_root', type: 'container', attr: {}, style: {}}])
-      expect(spy.args[1]).eql(['bar', 'addElement', '_root', { ref: first.ref, type: 'container', attr: {}, style: {}}, 0])
-        expect(spy.args[2]).eql(['bar', 'addElement', first.ref, { ref: first.pureChildren[0].ref, type: 'aaa', attr: { a: 1 }, style: {}}, -1])
-        expect(spy.args[3]).eql(['bar', 'addElement', first.ref, { ref: first.pureChildren[1].ref, type: 'bbb', attr: { b: 2 }, style: {}}, -1])
-      expect(spy.args[4]).eql(['bar', 'addElement', '_root', { ref: second.ref, type: 'container', attr: {}, style: {}}, 1])
-        expect(spy.args[5]).eql(['bar', 'addElement', second.ref, { ref: second.pureChildren[0].ref, type: 'aaa', attr: { a: 2 }, style: {}}, -1])
-        expect(spy.args[6]).eql(['bar', 'addElement', second.ref, { ref: second.pureChildren[1].ref, type: 'bbb', attr: { b: 2 }, style: {}}, -1])
-      expect(spy.args[7]).eql(['bar', 'addElement', '_root', { ref: third.ref, type: 'container', attr: {}, style: {}}, 2])
-        expect(spy.args[8]).eql(['bar', 'addElement', third.ref, { ref: third.pureChildren[0].ref, type: 'aaa', attr: { a: 3 }, style: {}}, -1])
-        expect(spy.args[9]).eql(['bar', 'addElement', third.ref, { ref: third.pureChildren[1].ref, type: 'bbb', attr: { b: 2 }, style: {}}, -1])
-    /* eslint-enable indent */
-  })
-
-  it('received actions for add a tree element', () => {
-    customComponentMap.foo = {
-      template: {
-        type: 'r',
-        children: [
-          { type: 'a' }, { type: 'b', children: [
-            { type: 'd' }, { type: 'e', append: 'tree', children: [
-              { type: 'g' }, { type: 'h' }, { type: 'i' }
-            ] }, { type: 'f' }
-          ] }, { type: 'c' }
-        ]
-      }
-    }
-
-    new Vm('foo', customComponentMap.foo, { _app: app })
-    const pureChildren = doc.body.pureChildren
-
-    expect(spy.args.length).eql(7)
-    // body, r, r.a, r.b, r.b.d, r.b.e(tree), r.b.f, r.c
-
-    expect(spy.args[0][2].ref).eql('_root')
-    expect(spy.args[0][2].type).eql('r')
-
-    expect(spy.args[1][3].ref).eql(pureChildren[0].ref)
-    expect(spy.args[1][3].type).eql('a')
-    expect(spy.args[1][2]).eql('_root')
-    expect(spy.args[1][4]).eql(-1)
-
-    expect(spy.args[2][3].ref).eql(pureChildren[1].ref)
-    expect(spy.args[2][3].type).eql('b')
-    expect(spy.args[2][2]).eql('_root')
-    expect(spy.args[2][4]).eql(-1)
-
-    expect(spy.args[3][3].ref).eql(pureChildren[1].pureChildren[0].ref)
-    expect(spy.args[3][3].type).eql('d')
-    expect(spy.args[3][2]).eql(pureChildren[1].ref)
-    expect(spy.args[3][4]).eql(-1)
-
-    const tree = pureChildren[1].pureChildren[1]
-    expect(spy.args[4][3].ref).eql(tree.ref)
-    expect(spy.args[4][3].type).eql('e')
-    expect(spy.args[4][2]).eql(pureChildren[1].ref)
-    expect(spy.args[4][4]).eql(-1)
-    expect(spy.args[4][3].children).eql([
-      { ref: tree.pureChildren[0].ref, type: 'g', attr: {}, style: {}},
-      { ref: tree.pureChildren[1].ref, type: 'h', attr: {}, style: {}},
-      { ref: tree.pureChildren[2].ref, type: 'i', attr: {}, style: {}}])
-
-    expect(spy.args[5][3].ref).eql(pureChildren[1].pureChildren[2].ref)
-    expect(spy.args[5][3].type).eql('f')
-    expect(spy.args[5][2]).eql(pureChildren[1].ref)
-    expect(spy.args[5][4]).eql(-1)
-
-    expect(spy.args[6][3].ref).eql(pureChildren[2].ref)
-    expect(spy.args[6][3].type).eql('c')
-    expect(spy.args[6][2]).eql('_root')
-    expect(spy.args[6][4]).eql(-1)
-  })
-})



[03/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/signals/signals.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/signals/signals.source.js b/test/js-framework/case/signals/signals.source.js
new file mode 100644
index 0000000..c16234e
--- /dev/null
+++ b/test/js-framework/case/signals/signals.source.js
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+define('@weex-component/signals-sub', function (require, exports, module) {
+  module.exports = {
+    data: function () {return {
+      thumb: '', title: ''
+    }},
+    style: {},
+    template: {
+      type: 'div',
+      children: [
+        {type: 'image', attr: {src: function () {return this.thumb}}},
+        {type: 'text', attr: {value: function () {return this.title}}}
+      ]
+    }
+  }
+})
+
+define('@weex-component/signals', function (require, exports, module) {
+  module.exports = {
+    data: function () {return {
+      list: [
+        {thumb: '<some image url 1>', title: 'Hello World 1'},
+        {thumb: '<some image url 2>', title: 'Hello World 2'},
+        {thumb: '<some image url 3>', title: 'Hello World 3'},
+        {thumb: '<some image url 4>', title: 'Hello World 4'},
+        {thumb: '<some image url 5>', title: 'Hello World 5'},
+        {thumb: '<some image url 6>', title: 'Hello World 6'},
+        {thumb: '<some image url 7>', title: 'Hello World 7'},
+        {thumb: '<some image url 8>', title: 'Hello World 8'},
+        {thumb: '<some image url 9>', title: 'Hello World 9'},
+        {thumb: '<some image url 10>', title: 'Hello World 10'},
+        {thumb: '<some image url 11>', title: 'Hello World 11'},
+        {thumb: '<some image url 12>', title: 'Hello World 12'},
+        {thumb: '<some image url 13>', title: 'Hello World 13'},
+        {thumb: '<some image url 14>', title: 'Hello World 14'},
+        {thumb: '<some image url 15>', title: 'Hello World 15'},
+        {thumb: '<some image url 16>', title: 'Hello World 16'},
+        {thumb: '<some image url 17>', title: 'Hello World 17'},
+        {thumb: '<some image url 18>', title: 'Hello World 18'},
+        {thumb: '<some image url 19>', title: 'Hello World 19'},
+        {thumb: '<some image url 20>', title: 'Hello World 20'}
+      ]
+    }},
+    style: {},
+    template: {
+      type: 'div',
+      children: [
+        {
+          type: 'signals-sub',
+          repeat: {
+            expression: function () {return this.list},
+            key: 'i',
+            value: 'item'
+          },
+          attr: {
+            thumb: function () {return this.item.thumb},
+            title: function () {return this.item.title}
+          }
+        }
+      ]
+    }
+  }
+})
+
+// // require module
+
+bootstrap('@weex-component/signals')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/signals/timer.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/signals/timer.output.js b/test/js-framework/case/signals/timer.output.js
new file mode 100644
index 0000000..f645aa2
--- /dev/null
+++ b/test/js-framework/case/signals/timer.output.js
@@ -0,0 +1,9 @@
+{
+  type: 'div',
+  children: [{
+    type: 'text',
+    attr: {
+      value: 'foo'
+    }
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/signals/timer.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/signals/timer.source.js b/test/js-framework/case/signals/timer.source.js
new file mode 100644
index 0000000..4222d3e
--- /dev/null
+++ b/test/js-framework/case/signals/timer.source.js
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+define('@weex-component/timer', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {
+      return {
+        x: 'foo'
+      }
+    },
+    created: function () {
+      setTimeout(function () {
+        this.x = 'bar'
+      }.bind(this), 1500)
+    }
+  }
+
+
+;module.exports.template = {
+  "type": "div",
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "value": function () {
+          return this.x
+        }
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/timer')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/tester.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/tester.js b/test/js-framework/case/tester.js
new file mode 100644
index 0000000..c885fef
--- /dev/null
+++ b/test/js-framework/case/tester.js
@@ -0,0 +1,528 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+
+const expect = chai.expect
+chai.use(sinonChai)
+
+import { createRuntime, createApp, getCode } from './prepare'
+
+describe('test input and output', function () {
+  let runtime
+
+  before(() => {
+    runtime = createRuntime()
+    sinon.stub(console, 'info')
+    sinon.stub(console, 'warn')
+    sinon.stub(console, 'error')
+  })
+
+  after(() => {
+    console.info.restore()
+    console.warn.restore()
+    console.error.restore()
+  })
+
+  describe('one-time rendering cases', () => {
+    let app
+
+    beforeEach(() => {
+      app = createApp(runtime)
+      callNativeHandler = (...args) => app._target.callNative(...args)
+    })
+
+    afterEach(() => {
+      app = null
+      callNativeHandler = function () {}
+    })
+
+    function checkOutput (app, name) {
+      const source = getCode('basic/' + name + '.source.js')
+      const output = getCode('basic/' + name + '.output.js')
+      const result = eval('(' + output + ')')
+
+      app.$create(source)
+      expect(app.getRealRoot()).eql(result)
+      app.$destroy()
+    }
+
+    it('global Weex object', () => checkOutput(app, 'global-weex-object'))
+
+    it('single case', () => checkOutput(app, 'foo'))
+    it('foo2 case', () => checkOutput(app, 'foo2'))
+    it('foo3 case', () => checkOutput(app, 'foo3'))
+    it('foo4 case', () => checkOutput(app, 'foo4'))
+    it('foo5 case', () => checkOutput(app, 'foo5'))
+    it('foo6 case', () => checkOutput(app, 'foo6'))
+    it('foo7 case', () => checkOutput(app, 'foo7'))
+    it('foo8 case', () => checkOutput(app, 'foo8'))
+    it('foo9 case', () => checkOutput(app, 'foo9'))
+
+    it('static1 case', () => checkOutput(app, 'static1'))
+    it('static2 case', () => checkOutput(app, 'static2'))
+    it('static3 case', () => checkOutput(app, 'static3'))
+    it('static4 case', () => checkOutput(app, 'static4'))
+
+    it('computed in repeat case', () => checkOutput(app, 'computed-in-repeat'))
+    it('backward(register/render) case', () => checkOutput(app, 'backward1'))
+    it('backward(define/require) case', () => checkOutput(app, 'backward2'))
+    it('append case', () => checkOutput(app, 'append'))
+    it('append-root case', () => checkOutput(app, 'append-root'))
+
+    it('if case', () => checkOutput(app, 'if'))
+    it('if-repeat case', () => checkOutput(app, 'if-repeat'))
+    it('if in root element case', () => checkOutput(app, 'if-root'))
+    it('repeat in root element case', () => checkOutput(app, 'repeat-root'))
+    it('repeat with index case case', () => checkOutput(app, 'repeat-index'))
+    it('repeat with array v case', () => checkOutput(app, 'repeat-array-v'))
+    it('repeat with array kv case', () => checkOutput(app, 'repeat-array-kv'))
+    it('repeat with array no-kv case', () => checkOutput(app, 'repeat-array-no-kv'))
+    it('repeat with array non-obj case', () => checkOutput(app, 'repeat-array-non-obj'))
+    it('repeat watch case', () => checkOutput(app, 'repeat-watch'))
+
+    it('id case', () => checkOutput(app, 'id'))
+    it('dynamic id case', () => checkOutput(app, 'dynamic-id'))
+
+    it('reset style case', () => checkOutput(app, 'reset-style'))
+    it('dynamic type case', () => checkOutput(app, 'dynamic-type'))
+    it('dynamic property case', () => checkOutput(app, 'dynamic-property'))
+
+    it('bind subvm', () => checkOutput(app, 'subvm'))
+    it('components options', () => checkOutput(app, 'components'))
+    it('change data when created', () => checkOutput(app, 'created'))
+    it('change data when ready', () => checkOutput(app, 'ready'))
+
+    it('require 3rd', () => checkOutput(app, 'require'))
+    it('a correct transformer version', () => checkOutput(app, 'transformer1'))
+
+    it('promise case', () => checkOutput(app, 'promise'))
+  })
+
+  describe('invalid usage', function () {
+    describe('strict mode', () => {
+      const readSource = name => getCode('throws/' + name + '.source.js')
+
+      let app
+      beforeEach(() => { app = createApp(runtime) })
+      afterEach(() => { app = null })
+
+      it('global variable 1', () => {
+        const sourceCode = readSource('global-variable1')
+        expect(() => app.$create(sourceCode)).to.throw(ReferenceError)
+      })
+
+      it('global variable 2', () => {
+        const sourceCode = readSource('global-variable2')
+        expect(() => app.$create(sourceCode)).to.throw(ReferenceError)
+      })
+
+      it('global variable 3', () => {
+        const sourceCode = readSource('global-variable3')
+        expect(() => app.$create(sourceCode)).to.throw(ReferenceError)
+      })
+    })
+  })
+
+  describe('complex cases', function () {
+    let app
+
+    beforeEach(() => {
+      app = createApp(runtime)
+      callNativeHandler = (...args) => app._target.callNative(...args)
+    })
+
+    afterEach(() => {
+      app = null
+      callNativeHandler = function () {}
+    })
+
+    const readSource = name => getCode('complex/' + name + '.source.js')
+    const readOutput = name => getCode('complex/' + name + '.output.js')
+
+    it('computed case', () => {
+      const name = 'computed'
+      const sourceCode = readSource(name)
+      const outputCode = readOutput(name)
+
+      app.$create(sourceCode)
+      const expected = eval('(' + outputCode + ')')
+      expect(app.getRealRoot()).eql(expected)
+
+      app.$refresh({ x: 10 })
+      expected.children[0].attr.value = 12
+      expected.children[1].attr.value = 12
+      expect(app.getRealRoot()).eql(expected)
+
+      app.$refresh({ m: 10 })
+      expected.children[0].attr.value = 20
+      expected.children[1].attr.value = 20
+      expect(app.getRealRoot()).eql(expected)
+
+      app.$destroy()
+    })
+
+    it('append-root-event case', () => {
+      const name = 'append-root-event'
+      const sourceCode = readSource(name)
+      const outputCode = readOutput(name)
+
+      app.$create(sourceCode)
+      const expected = eval('(' + outputCode + ')')
+      const actual = app.getRealRoot()
+      expect(actual).eql(expected)
+
+      app.$fireEvent(app.doc.body.children[0].ref, 'click', {})
+      const actual2 = app.getRealRoot()
+      expect(actual2.children[0].attr.value).eql(2)
+
+      app.$destroy()
+    })
+
+    it('use append tree and sub components', (done) => {
+      const name = 'component-append-tree'
+      const sourceCode = readSource(name)
+      const outputCode = readOutput(name)
+
+      app.$create(sourceCode)
+      const expected = eval('(' + outputCode + ')')
+      const actual = app.getRealRoot()
+      expect(actual).eql(expected)
+
+      setTimeout(() => {
+        const actual2 = app.getRealRoot()
+        expect(actual2.children[0].children[0].attr.value).eql('timeout')
+        expect(actual2.children[1].children[0].attr.value).eql('timeout')
+        app.$destroy()
+        done()
+      }, 200)
+    })
+
+    it('repeat with array track-by case', () => {
+      const name = 'repeat-track-by'
+      const sourceCode = readSource(name)
+      const outputCode = readOutput(name)
+
+      app.$create(sourceCode)
+      app.$refresh({
+        titlelist: [
+          { text: 'Hello World2' },
+          { text: 'Hello World1' }
+        ]
+      })
+      const expected = eval('(' + outputCode + ')')
+      expect(app.getRealRoot()).eql(expected)
+
+      app.$destroy()
+    })
+
+    it('if-refresh case', () => {
+      const name = 'if-refresh'
+      const sourceCode = readSource(name)
+      const outputCode = readOutput(name)
+
+      app.$create(sourceCode)
+      app.$refresh({ showTitle: false })
+      const expected = eval('(' + outputCode + ')')
+      expect(app.getRealRoot()).eql(expected)
+
+      app.$destroy()
+    })
+
+    it('if-repeat-refresh case', () => {
+      const name = 'if-repeat-refresh'
+      const sourceCode = readSource(name)
+      const outputCode = readOutput(name)
+
+      app.$create(sourceCode)
+      app.$refresh({
+        titlelist: [
+          { showTitle: false, title: 'Hello World1' },
+          { showTitle: true, title: 'Hello World2' },
+          { showTitle: true, title: 'Hello World3' }
+        ]
+      })
+      const expected = eval('(' + outputCode + ')')
+      expect(app.getRealRoot()).eql(expected)
+
+      app.$destroy()
+    })
+
+    it('click case', () => {
+      const name = 'click'
+      const sourceCode = readSource(name)
+      const outputCode = readOutput(name)
+
+      app.$create(sourceCode)
+      const expected = eval('(' + outputCode + ')')
+      expect(app.getRealRoot()).eql(expected)
+
+      app.$fireEvent(app.doc.body.children[0].ref, 'click', {})
+
+      app.$destroy()
+    })
+
+    it('inline click case', () => {
+      const name = 'inline-click'
+      const sourceCode = readSource(name)
+      const outputCode = readOutput(name)
+
+      app.$create(sourceCode)
+      const expected = eval('(' + outputCode + ')')
+      expect(app.getRealRoot()).eql(expected)
+
+      app.$fireEvent(app.doc.body.children[0].ref, 'click', {})
+
+      expected.children[0].attr.value = 'Hello World2'
+      expect(app.getRealRoot()).eql(expected)
+
+      app.$destroy()
+    })
+
+    it('refresh twice', () => {
+      const name = 'refresh2'
+      const sourceCode = readSource(name)
+      const outputCode = readOutput(name)
+
+      app.$create(sourceCode)
+      expect(app.getRealRoot()).eql({ type: 'container' })
+
+      app.$refresh({ ext: { showbar1: false }})
+      app.$refresh({ ext: { showbar1: true }})
+      const expected = eval('(' + outputCode + ')')
+      expect(app.getRealRoot()).eql(expected)
+
+      app.$destroy()
+    })
+
+    it('a less wrong transformer version', () => {
+      const name = 'transformer2'
+      const sourceCode = readSource(name)
+
+      const result = app.$create(sourceCode)
+      expect(result).to.be.an.instanceof(Error)
+      app.$destroy()
+    })
+
+    it('a bigger wrong transformer version', () => {
+      const name = 'transformer3'
+      const sourceCode = readSource(name)
+
+      const result = app.$create(sourceCode)
+      expect(result).to.be.an.instanceof(Error)
+      app.$destroy()
+    })
+
+    it('input binding', () => {
+      const name = 'input-binding'
+      const sourceCode = readSource(name)
+      const outputCode = readOutput(name)
+
+      app.$create(sourceCode)
+      const expected = eval('(' + outputCode + ')')
+      expect(app.getRealRoot()).eql(expected)
+
+      app.doc.body.children[0].attr.value = 'abcdefg'
+      app.$fireEvent(app.doc.body.children[0].ref, 'change', {}, { attrs: { value: 'abcdefg' }})
+      expected.children[0].attr.value = 'abcdefg'
+      expected.children.push({ type: 'text', attr: { value: '1 - abcdefg' }})
+      expect(app.getRealRoot()).eql(expected)
+
+      app.doc.body.children[0].attr.value = '12345'
+      app.$fireEvent(app.doc.body.children[0].ref, 'change', {}, { attrs: { value: '12345' }})
+      expected.children[0].attr.value = '12345'
+      expected.children.push({ type: 'text', attr: { value: '2 - 12345' }})
+      expect(app.getRealRoot()).eql(expected)
+
+      app.$destroy()
+    })
+  })
+
+  describe('multi page cases', function () {
+    let appA
+    let appB
+
+    beforeEach(() => {
+      appA = createApp(runtime)
+      appB = createApp(runtime)
+
+      callNativeHandler = (id, ...args) => {
+        switch (true) {
+          case (appA.id === id) : { appA._target.callNative(id, ...args) } break
+          case (appB.id === id) : { appB._target.callNative(id, ...args) } break
+        }
+      }
+    })
+
+    afterEach(() => {
+      appA = null
+      appB = null
+      callNativeHandler = function () {}
+    })
+
+    const readSource = name => getCode('multi/' + name + '.source.js')
+    const readOutput = name => getCode('multi/' + name + '.output.js')
+
+    it('clear-module case', () => {
+      const nameA = 'clear-moduleA'
+      const nameB = 'clear-moduleB'
+      const sourceCodeA = readSource(nameA)
+      const sourceCodeB = readSource(nameB)
+      const expectedA = eval('(' + readOutput(nameA) + ')')
+      const expectedB = eval('(' + readOutput(nameB) + ')')
+
+      appA.$create(sourceCodeA)
+      appB.$create(sourceCodeB)
+
+      expect(appB.getRealRoot()).eql(expectedB)
+
+      appB.$destroy()
+      appA.$fireEvent(appA.doc.body.children[0].ref, 'click', {})
+      expect(appA.getRealRoot()).eql(expectedA)
+
+      appA.$destroy()
+    })
+
+    it('clear-dep-target case', () => {
+      const nameError = 'clear-dep-target-error'
+      const nameFine = 'clear-dep-target-fine'
+      const sourceCodeError = readSource(nameError)
+      const sourceCodeFine = readSource(nameFine)
+      const expectedFine = eval('(' + readOutput(nameFine) + ')')
+
+      // should throw
+      expect(() => { appA.$create(sourceCodeError) }).to.throw(TypeError)
+      appA.$destroy()
+
+      // not throw
+      appB.$create(sourceCodeFine)
+      expect(appB.getRealRoot()).eql(expectedFine)
+
+      appB.$destroy()
+    })
+  })
+
+  describe('timer & callNative signals', function () {
+    let app
+
+    const callNativeSpy = sinon.spy()
+    function genCallNativeWrapper (count) {
+      return (name, tasks, cbId) => {
+        callNativeSpy(tasks)
+        const length = callNativeSpy.callCount
+        if (length > count) {
+          return -1
+        }
+        return length
+      }
+    }
+    beforeEach(() => {
+      app = createApp(runtime)
+      callNativeHandler = (...args) => app._target.callNative(...args)
+    })
+
+    afterEach(() => {
+      app = null
+      callNativeSpy.reset()
+      callNativeHandler = function () {}
+    })
+
+    const readSource = name => getCode('signals/' + name + '.source.js')
+    const readOutput = name => getCode('signals/' + name + '.output.js')
+
+    it('use HTML5 timer API', function (done) {
+      this.timeout(5000)
+      const name = 'timer'
+      const sourceCode = readSource(name)
+      const outputCode = readOutput(name)
+
+      app.$create(sourceCode)
+      const expected = eval('(' + outputCode + ')')
+      expect(app.getRealRoot()).eql(expected)
+
+      setTimeout(_ => {
+        expect(app.getRealRoot()).eql(expected)
+        setTimeout(_ => {
+          expected.children[0].attr.value = 'bar'
+          expect(app.getRealRoot()).eql(expected)
+          app.$destroy()
+          done()
+        }, 1000)
+      }, 1000)
+    })
+
+    it('use modal API', function (done) {
+      this.timeout(5000)
+      const name = 'modal'
+      const sourceCode = readSource(name)
+      const outputCode = readOutput(name)
+
+      app.$create(sourceCode)
+      const expected = eval('(' + outputCode + ')')
+      expect(app.getRealRoot()).eql(expected)
+
+      // the test driver will hold the API callback about 1 sec
+      setTimeout(_ => {
+        expected.children[0].attr.value = 'bar'
+        expect(app.getRealRoot()).eql(expected)
+        app.$destroy()
+        done()
+      }, 1500)
+    })
+
+    it('signals control', function () {
+      this.timeout(15000)
+
+      const name = 'signals'
+      const sourceCode = readSource(name)
+
+      function run (calls) {
+        callNativeSpy.reset()
+        callNativeHandler = genCallNativeWrapper(calls)
+        app.$create(sourceCode)
+        app.$destroy()
+        expect(callNativeSpy.callCount).eql(calls + 2)
+      }
+
+      for (let i = 5; i < 60; i++) {
+        run(i)
+      }
+    })
+
+    it('long signals control', function () {
+      this.timeout(500000)
+
+      const name = 'signals-long'
+      const sourceCode = readSource(name)
+
+      function run (calls) {
+        callNativeSpy.reset()
+        callNativeHandler = genCallNativeWrapper(calls)
+        app.$create(sourceCode)
+        app.$destroy()
+        expect(callNativeSpy.callCount).eql(calls + 2)
+      }
+
+      run(10)
+      run(30)
+      run(90)
+    })
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/throws/global-variable1.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/throws/global-variable1.source.js b/test/js-framework/case/throws/global-variable1.source.js
new file mode 100644
index 0000000..b60fc47
--- /dev/null
+++ b/test/js-framework/case/throws/global-variable1.source.js
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+something = 'whatever'
+
+define('@weex-component/global-variable1', function (require, exports, module) {
+
+  module.exports.template = { "type": "container" }
+
+  module.exports.style = {}
+
+  module.exports = {
+    ready: function () {
+      console.log('I an ready')
+    }
+  }
+
+})
+
+bootstrap('@weex-component/global-variable1')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/throws/global-variable2.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/throws/global-variable2.source.js b/test/js-framework/case/throws/global-variable2.source.js
new file mode 100644
index 0000000..b1b4f4e
--- /dev/null
+++ b/test/js-framework/case/throws/global-variable2.source.js
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+define('@weex-component/global-variable2', function (require, exports, module) {
+
+  module.exports.template = { "type": "container" }
+
+  module.exports.style = {}
+
+  module.exports = {
+    ready: function () {
+      abc = 'abc'
+    }
+  }
+
+})
+
+bootstrap('@weex-component/global-variable2')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/throws/global-variable3.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/throws/global-variable3.source.js b/test/js-framework/case/throws/global-variable3.source.js
new file mode 100644
index 0000000..e010ca1
--- /dev/null
+++ b/test/js-framework/case/throws/global-variable3.source.js
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+define('@weex-component/global-variable3', function (require, exports, module) {
+
+  module.exports.template = { "type": "container" }
+
+  module.exports.style = {}
+
+  module.exports = {
+    data: function() {
+      return {
+        lists: ['A', 'B', 'C']
+      }
+    },
+    ready: function () {
+      for (i = 0; i < this.lists.length; ++i) {
+        console.log(this.lists[i])
+      }
+    }
+  }
+
+})
+
+bootstrap('@weex-component/global-variable3')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/api/methods.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/api/methods.js b/test/js-framework/unit/default/api/methods.js
new file mode 100644
index 0000000..83f6586
--- /dev/null
+++ b/test/js-framework/unit/default/api/methods.js
@@ -0,0 +1,190 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import '../../../../../runtime/shared/env/console'
+import * as modules from '../../../../../runtime/frameworks/legacy/api/modules'
+import * as methods from '../../../../../runtime/frameworks/legacy/api/methods'
+import Differ from '../../../../../runtime/frameworks/legacy/app/differ'
+import { initModules, requireModule, clearModules, initMethods } from '../../../../../runtime/frameworks/legacy/app/register'
+
+function Vm () {
+}
+
+describe('built-in methods', () => {
+  let vm
+  const requireSpy = sinon.spy()
+  const moduleSpy = sinon.spy()
+
+  before(() => {
+    clearModules()
+
+    initModules(modules)
+    initMethods(Vm, methods)
+
+    vm = new Vm()
+
+    Object.assign(vm, {
+      _app: {
+        options: {
+          debug: true,
+          bundleUrl: 'path_to_bundleUrl'
+        },
+        differ: new Differ(),
+        requireModule: (name) => {
+          requireSpy(name)
+          const module = requireModule(this, name)
+          const mockModule = {}
+          for (const moduleName in module) {
+            mockModule[moduleName] = function (...args) {
+              moduleSpy(...args)
+              if (typeof args[args.length - 1] === 'function') {
+                args[args.length - 1]()
+              }
+            }
+          }
+          return mockModule
+        }
+      },
+      _setStyle: function () {},
+      _ids: {
+        a: {
+          vm: {},
+          el: {
+            ref: '_root'
+          }
+        }
+      }
+    })
+
+    for (const apiName in vm) {
+      if (apiName.indexOf('$') === 0) {
+        vm[apiName] = sinon.spy(vm, apiName)
+      }
+    }
+    sinon.stub(console, 'warn')
+  })
+
+  beforeEach(() => {
+    requireSpy.reset()
+    moduleSpy.reset()
+    console.warn.reset()
+  })
+
+  after(() => {
+    clearModules()
+    console.warn.restore()
+  })
+
+  it('$', () => {
+    expect(vm.$('a')).to.deep.equal(vm._ids.a.vm)
+    expect(vm.$('invalid')).to.be.undefined
+    expect(console.warn.callCount).to.be.equal(2)
+  })
+
+  it('$el', () => {
+    expect(vm.$el('a')).to.deep.equal(vm._ids.a.el)
+    expect(vm.$el('invalid')).to.be.undefined
+  })
+
+  it('$vm', () => {
+    expect(vm.$vm('a')).to.deep.equal(vm._ids.a.vm)
+    expect(vm.$vm('invalid')).to.be.undefined
+  })
+
+  it('$renderThen', () => {
+    const fnSpy = sinon.spy()
+    vm.$renderThen(fnSpy)
+    vm._app.differ.flush()
+    expect(fnSpy.callCount).to.be.equal(1)
+  })
+
+  it('$scrollTo', () => {
+    vm.$scrollTo('a', 100)
+    expect(vm.$scrollTo('invalid', 100)).to.be.undefined
+    expect(requireSpy.firstCall.args[0]).to.be.equal('dom')
+    expect(moduleSpy.firstCall.args.length).to.be.equal(2)
+  })
+
+  it('$transition', () => {
+    const callback = sinon.spy()
+    vm.$transition('a', { styles: { color: '#FF0000' }}, callback)
+    expect(vm.$transition('invalid', {})).to.be.undefined
+    expect(requireSpy.firstCall.args[0]).eql('animation')
+    expect(moduleSpy.firstCall.args.length).eql(3)
+    expect(moduleSpy.firstCall.args[0]).eql('_root')
+    expect(moduleSpy.firstCall.args[1]).eql({
+      styles: { color: '#FF0000' }
+    })
+    expect(callback.callCount).eql(1)
+  })
+
+  it('$getConfig', () => {
+    const config = vm.$getConfig()
+    expect(config).eql({
+      debug: true,
+      bundleUrl: 'path_to_bundleUrl'
+    })
+
+    const configSpy = sinon.spy()
+    vm.$getConfig(configSpy)
+    expect(console.warn.callCount).to.be.equal(1)
+    expect(configSpy.args.length).eql(1)
+    expect(configSpy.args[0][0]).eql({
+      debug: true,
+      bundleUrl: 'path_to_bundleUrl'
+    })
+  })
+
+  it('$sendHttp', () => {
+    const callback = sinon.spy()
+    vm.$sendHttp({ a: 1 }, callback)
+    expect(requireSpy.firstCall.args[0]).eql('stream')
+    expect(moduleSpy.firstCall.args.length).eql(2)
+    expect(moduleSpy.firstCall.args).eql([{ a: 1 }, callback])
+    expect(callback.callCount).eql(1)
+  })
+
+  it('$openURL', () => {
+    vm.$openURL('url')
+    expect(requireSpy.firstCall.args[0]).eql('event')
+    expect(moduleSpy.firstCall.args.length).eql(1)
+    expect(moduleSpy.firstCall.args).eql(['url'])
+  })
+
+  it('$setTitle', () => {
+    vm.$setTitle('title')
+    expect(requireSpy.firstCall.args[0]).eql('pageInfo')
+    expect(moduleSpy.firstCall.args.length).eql(1)
+    expect(moduleSpy.firstCall.args).eql(['title'])
+  })
+
+  it('$call', () => {
+    vm.$call('event', 'openURL', 'url')
+    expect(vm.$call('invalid', 'module')).to.be.undefined
+    expect(vm.$call('event', 'invalid')).to.be.undefined
+    expect(requireSpy.firstCall.args[0]).eql('event')
+    expect(moduleSpy.firstCall.args.length).eql(1)
+    expect(moduleSpy.firstCall.args[0]).eql('url')
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/api/modules.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/api/modules.js b/test/js-framework/unit/default/api/modules.js
new file mode 100644
index 0000000..5545337
--- /dev/null
+++ b/test/js-framework/unit/default/api/modules.js
@@ -0,0 +1,43 @@
+/*
+ * 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 chai from 'chai'
+const { expect } = chai
+
+import * as modules from '../../../../../runtime/frameworks/legacy/api/modules'
+import { initModules, requireModule, clearModules } from '../../../../../runtime/frameworks/legacy/app/register'
+
+describe('built-in modules', () => {
+  before(() => {
+    clearModules()
+  })
+
+  after(() => {
+    clearModules()
+  })
+
+  it('have keys', () => {
+    const app = {}
+    initModules(modules)
+    expect(requireModule(app, 'dom')).to.have.all.keys('scrollToElement')
+    expect(requireModule(app, 'stream')).to.have.all.keys('sendHttp')
+    expect(requireModule(app, 'event')).to.have.all.keys('openURL')
+    expect(requireModule(app, 'pageInfo')).to.have.all.keys('setTitle')
+    expect(requireModule(app, 'animation')).to.have.all.keys('transition')
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/app/bundle.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/app/bundle.js b/test/js-framework/unit/default/app/bundle.js
new file mode 100644
index 0000000..8ee6ae6
--- /dev/null
+++ b/test/js-framework/unit/default/app/bundle.js
@@ -0,0 +1,409 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import * as bundle from '../../../../../runtime/frameworks/legacy/app/bundle'
+import * as register from '../../../../../runtime/frameworks/legacy/app/register'
+import { removeWeexPrefix } from '../../../../../runtime/frameworks/legacy/util'
+import { Document } from '../../../../../runtime/vdom'
+import Listener from '../../../../../runtime/bridge/Listener'
+
+describe('parsing a bundle file', () => {
+  const componentTemplate = {
+    type: 'container',
+    children: [{
+      type: 'text',
+      attr: {
+        value: 'Hello World'
+      }
+    }]
+  }
+
+  before(() => {
+    sinon.stub(console, 'info')
+    sinon.stub(console, 'warn')
+    sinon.stub(console, 'error')
+    sinon.stub(console, 'debug')
+  })
+
+  after(() => {
+    console.info.restore()
+    console.warn.restore()
+    console.error.restore()
+    console.debug.restore()
+  })
+
+  describe('use define/bootstrap', () => {
+    let app
+    let callTasksSpy
+
+    before(() => {
+      const id = Date.now()
+      callTasksSpy = sinon.spy()
+
+      const doc = new Document(id, '', (tasks) => {
+        app.callTasks(tasks)
+      })
+
+      app = {
+        id, doc,
+        customComponentMap: {},
+        commonModules: {},
+        callbacks: {},
+        callTasks: callTasksSpy,
+        requireModule: function (name) {
+          return register.requireModule(this, name)
+        }
+      }
+    })
+
+    afterEach(() => {
+      callTasksSpy.reset()
+    })
+
+    describe('define', () => {
+      it('a weex component with factory and deps', () => {
+        bundle.defineFn(app, '@weex-component/a', (require, exports, module) => {
+          module.exports = {
+            template: componentTemplate
+          }
+        })
+        bundle.defineFn(app, '@weex-component/aa', [], (require, exports, module) => {
+          module.exports = {
+            template: componentTemplate
+          }
+        })
+
+        expect(app.customComponentMap['a'].template).to.deep.equal(componentTemplate)
+        expect(app.customComponentMap['aa'].template).to.deep.equal(componentTemplate)
+      })
+
+      it('a weex component with definition object and deps', () => {
+        bundle.defineFn(app, '@weex-component/aaa', {
+          template: componentTemplate
+        })
+        bundle.defineFn(app, '@weex-component/aaaa', [], {
+          template: componentTemplate
+        })
+
+        expect(app.customComponentMap['aaa'].template).to.deep.equal(componentTemplate)
+        expect(app.customComponentMap['aaaa'].template).to.deep.equal(componentTemplate)
+      })
+
+      it('a weex module', () => {
+        const methods = [{
+          name: 'createBody',
+          args: []
+        }]
+
+        bundle.defineFn(app, '@weex-module/dom', (require, exports, module) => {
+          module.exports = methods
+        })
+      })
+
+      it('a normal module', () => {
+        bundle.defineFn(app, './a', (require, exports, module) => {
+          exports.version = '0.1'
+        })
+      })
+
+      it('a npm module', () => {
+        bundle.defineFn(app, 'lib-httpurl', (require, exports, module) => {
+          exports.version = '0.2'
+        })
+      })
+
+      it('a CMD module', () => {
+        bundle.defineFn(app, 'kg/base', [], (require, exports, module) => {
+          exports.version = '0.3'
+        })
+      })
+    })
+
+    describe('require', () => {
+      it('a weex component', (done) => {
+        bundle.defineFn(app, '@weex-component/b', (require, exports, module) => {
+          const componentA = require('@weex-component/a')
+
+          expect(componentA.template).to.be.equal(componentTemplate)
+          done()
+        })
+      })
+
+      it('a weex module', (done) => {
+        // eslint-disable-next-line camelcase
+        const __weex_require__ = name => register.requireModule(app, removeWeexPrefix(name))
+        bundle.defineFn(app, '@weex-component/c', (require, exports, module) => {
+          const dom = require('@weex-module/dom')
+          expect(dom.createBody).to.be.a('function')
+          const dom2 = __weex_require__('dom')
+          expect(dom2.createBody).to.be.a('function')
+          const dom3 = __weex_require__('@weex-module/dom')
+          expect(dom3.createBody).to.be.a('function')
+          done()
+        })
+      })
+
+      it('a normal module', (done) => {
+        bundle.defineFn(app, '@weex-component/d', (require, exports, module) => {
+          const a = require('./a')
+
+          expect(a.version).to.be.equal('0.1')
+          done()
+        })
+      })
+
+      it('a npm module', (done) => {
+        bundle.defineFn(app, '@weex-component/e', (require, exports, module) => {
+          const HttpUrl = require('lib-httpurl')
+
+          expect(HttpUrl.version).to.be.equal('0.2')
+          done()
+        })
+      })
+
+      it('a CMD module', (done) => {
+        bundle.defineFn(app, 'kg/sample', ['kg/base'], (require, exports, module) => {
+          const base = require('kg/base')
+
+          expect(base.version).to.be.equal('0.3')
+          done()
+        })
+      })
+    })
+
+    describe('bootstrap', () => {
+      const ready = sinon.spy()
+
+      before(() => {
+        global.transformerVersion = '>=0.1 <1.0'
+        bundle.defineFn(app, '@weex-component/main', (require, exports, module) => {
+          module.exports = {
+            template: componentTemplate,
+            ready: ready
+          }
+        })
+      })
+
+      after(() => {
+        global.transformerVersion = undefined
+      })
+
+      it('not a weex component', () => {
+        const result = bundle.bootstrap(app, '@weex-module/dom')
+        expect(result).instanceof(Error)
+      })
+
+      it('a weex component', () => {
+        const result = bundle.bootstrap(
+          app,
+          '@weex-component/main',
+          { transformerVersion: '0.1.99' }
+        )
+
+        expect(result).not.instanceof(Error)
+        expect(callTasksSpy.calledTwice).to.be.true
+
+        expect(ready.calledOnce).to.be.true
+        const task1 = callTasksSpy.firstCall.args[0][0]
+        expect(task1.module).to.be.equal('dom')
+        expect(task1.method).to.be.equal('createBody')
+        expect(task1.args[0]).to.deep.equal({
+          type: 'container',
+          ref: '_root',
+          attr: {},
+          style: {}
+        })
+
+        const task2 = callTasksSpy.secondCall.args[0][0]
+        expect(task2.module).to.be.equal('dom')
+        expect(task2.method).to.be.equal('addElement')
+        expect(task2.args[1]).to.deep.equal({
+          type: 'text',
+          ref: app.doc.body.children[0].ref,
+          attr: {
+            value: 'Hello World'
+          },
+          style: {}
+        })
+        expect(task2.args[0]).to.be.equal('_root')
+        expect(task2.args[2]).to.be.equal(-1)
+      })
+
+      it('with a less wrong transformer version', () => {
+        const result = bundle.bootstrap(
+          app,
+          '@weex-component/main',
+          { transformerVersion: '0.0.1' }
+        )
+        expect(result).instanceof(Error)
+      })
+
+      it('with a bigger wrong transformer version', () => {
+        const result = bundle.bootstrap(
+          app,
+          '@weex-component/main',
+          { transformerVersion: '9.9.9' }
+        )
+        expect(result).instanceof(Error)
+      })
+
+      it('with viewport config', () => {
+        bundle.bootstrap(
+          app,
+          '@weex-component/undefined',
+          {
+            viewport: { width: 640 }
+          }
+        )
+        expect(callTasksSpy.callCount).to.be.equal(1)
+        const tasks = callTasksSpy.lastCall.args[0]
+        expect(tasks[0].module).to.be.equal('meta')
+        expect(tasks[0].method).to.be.equal('setViewport')
+        expect(tasks[0].args).to.deep.equal([{ width: 640 }])
+      })
+    })
+  })
+
+  describe('use regsiter/render(backward compatibility)', () => {
+    let app
+    let callTasksSpy
+    let readySpy
+    const template = {
+      type: 'container',
+      children: [{
+        type: 'text',
+        attr: {
+          value: 'Hello World'
+        }
+      }, {
+        type: 'custom'
+      }]
+    }
+
+    before(() => {
+      const id = Date.now()
+      callTasksSpy = sinon.spy()
+      readySpy = sinon.spy()
+
+      const doc = new Document(id, '', (tasks, callback) => {
+        app.callTasks(tasks, callback)
+      }, Listener)
+
+      app = {
+        id, doc,
+        customComponentMap: {},
+        callbacks: {},
+        callTasks: (tasks, callback) => {
+          callTasksSpy(tasks)
+          callback && callback()
+        },
+        requireModule: function (name) {
+          return register.requireModule(this, name)
+        }
+      }
+
+      Object.assign(app, bundle)
+    })
+
+    afterEach(() => {
+      callTasksSpy.reset()
+    })
+
+    describe('register', () => {
+      const readyfn = function () {
+        readySpy(this)
+      }
+
+      it('a component', () => {
+        bundle.register(app, 'custom', {
+          template: componentTemplate,
+          data: {
+            b: 'c'
+          },
+          methods: {
+            ready: readyfn
+          }
+        })
+
+        bundle.register(app, 'main', {
+          template: template,
+          data: {
+            a: 'b'
+          },
+          methods: {
+            ready: readyfn
+          }
+        })
+
+        expect(app.customComponentMap['custom'].template)
+          .to.deep.equal(componentTemplate)
+        expect(app.customComponentMap['main'].template)
+          .to.deep.equal(template)
+      })
+    })
+  })
+
+  describe('use define/require(backward compatibility)', () => {
+    let app
+    let callTasksSpy
+
+    before(() => {
+      const id = Date.now()
+      callTasksSpy = sinon.spy()
+
+      const doc = new Document(id, '', (tasks, callback) => {
+        app.callTasks(tasks, callback)
+      }, Listener)
+
+      app = {
+        id, doc,
+        customComponentMap: {},
+        callbacks: {},
+        callTasks: (tasks, callback) => {
+          callTasksSpy(tasks)
+          callback && callback()
+        },
+        requireModule: function (name) {
+          return register.requireModule(this, name)
+        }
+      }
+    })
+
+    afterEach(() => {
+      callTasksSpy.reset()
+    })
+
+    describe('define(old)', () => {
+      it('a component', () => {
+        bundle.defineFn(app, 'main', (require, exports, module) => {
+          module.exports = {
+            template: componentTemplate
+          }
+        })
+        expect(app.customComponentMap['main'].template)
+          .to.deep.equal(componentTemplate)
+      })
+    })
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/app/ctrl.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/app/ctrl.js b/test/js-framework/unit/default/app/ctrl.js
new file mode 100644
index 0000000..9937626
--- /dev/null
+++ b/test/js-framework/unit/default/app/ctrl.js
@@ -0,0 +1,230 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import * as ctrl from '../../../../../runtime/frameworks/legacy/app/ctrl'
+import Differ from '../../../../../runtime/frameworks/legacy/app/differ'
+import { Document } from '../../../../../runtime/vdom'
+
+describe('the api of app', () => {
+  let app
+  let spy1
+  let spy2
+
+  function createApp () {
+    const id = Date.now()
+
+    const app = {
+      id: id,
+      customComponentMap: {},
+      registerComponent: function () {},
+      // define: sinon.spy(),
+      // bootstrap: sinon.stub(),
+      vm: {},
+      differ: new Differ(id)
+    }
+
+    app.doc = new Document(id, '', spy1)
+    app.doc.createBody('div')
+
+    app.doc.taskCenter.callbackManager.add(spy2)
+    // app.bootstrap.returns()
+
+    return app
+  }
+
+  before(() => {
+    spy1 = sinon.spy()
+    spy2 = sinon.spy()
+    app = createApp()
+    !console.debug && (console.debug = function () {})
+  })
+
+  afterEach(() => {
+    spy1.reset()
+    // spy2.reset()
+  })
+
+  describe('init', () => {
+    before(() => {
+      global.transformerVersion = '0.1.3'
+    })
+
+    after(() => {
+      global.transformerVersion = undefined
+    })
+
+    it('a simple bundle', () => {
+      app.requireModule = () => {}
+      ctrl.init(app, `
+        define('main', function (r, e, m) {
+          e.template = {
+            "type": "container",
+            "children": [{
+              "type": "text",
+              "attr": {
+                "value": "Hello World"
+              }
+            }]
+          }
+        })
+
+        bootstrap('main')
+      `)
+
+      // expect(app.define.calledOnce).to.be.true
+      // expect(app.bootstrap.calledOnce).to.be.true
+
+      const task = spy1.lastCall.args[0][0]
+      expect(task.module).to.be.equal('dom')
+      expect(task.method).to.be.equal('createFinish')
+      expect(task.args.length).to.be.equal(0)
+    })
+  })
+
+  describe('getRootElement', () => {
+    it('from a simple', () => {
+      const json = ctrl.getRootElement(app)
+      expect(json.ref).eql('_root')
+      expect(json.type).eql('div')
+      expect(json.children.length).eql(1)
+    })
+    it('from empty object', () => {
+      expect(ctrl.getRootElement({})).to.deep.equal({})
+    })
+  })
+
+  describe('fireEvent', () => {
+    it('click on root', () => {
+      ctrl.fireEvent(app, '_root', 'click')
+      const task = spy1.lastCall.args[0][0]
+      expect(task.module).to.be.equal('dom')
+      expect(task.method).to.be.equal('updateFinish')
+      expect(task.args.length).to.be.equal(0)
+    })
+
+    it('error', () => {
+      const result = ctrl.fireEvent(app, '_rootTest', 'click')
+      expect(result).to.be.an.instanceof(Error)
+    })
+
+    it('click on both root & _rootTest', () => {
+      ctrl.fireEvent(app, ['_root', '_rootTest'], 'click')
+      const task = spy1.lastCall.args[0][0]
+      expect(task.module).to.be.equal('dom')
+      expect(task.method).to.be.equal('updateFinish')
+      expect(task.args.length).to.be.equal(0)
+    })
+  })
+
+  describe('callback', () => {
+    it('with a simple data', () => {
+      const data = { a: 'b' }
+      ctrl.callback(app, '1', data, true)
+      expect(spy2.calledOnce).to.be.true
+
+      const task = spy1.firstCall.args[0][0]
+      expect(task.module).to.be.equal('dom')
+      expect(task.method).to.be.equal('updateFinish')
+      expect(task.args.length).to.be.equal(0)
+    })
+
+    it('multiple called', () => {
+      const data = { a: 'b' }
+      ctrl.callback(app, '1', data, true)
+      expect(spy2.calledTwice).to.be.true
+
+      ctrl.callback(app, '1', data, false)
+      expect(spy2.calledThrice).to.be.true
+    })
+
+    it('error', () => {
+      const data = null
+      const result = ctrl.callback(app, '1', data, true)
+      expect(result).to.be.an.instanceof(Error)
+    })
+  })
+
+  describe('refreshData', () => {
+    it('a simple data', () => {
+      const data = { b: 'c' }
+      ctrl.refresh(app, data)
+      expect(app.vm.b).to.deep.equal(data.b)
+
+      const task = spy1.lastCall.args[0][0]
+      expect(task.module).to.be.equal('dom')
+      expect(task.method).to.be.equal('refreshFinish')
+      expect(task.args.length).to.be.equal(0)
+    })
+
+    it('call refresh with refreshData function', () => {
+      app.vm.refreshData = function () {
+        app.vm.data = 'hello'
+      }
+      const data = { b: 'c' }
+      ctrl.refresh(app, data)
+      expect(app.vm.data).to.equal('hello')
+    })
+
+    it('error', () => {
+      const data = null
+      const result = ctrl.refresh(app, data)
+      expect(result).to.be.an.instanceof(Error)
+    })
+  })
+
+  describe('destory', () => {
+    it('the simple data', () => {
+      ctrl.destroy(app)
+      expect(app.id).to.be.empty
+      expect(app.blocks).to.be.null
+      expect(app.vm).to.be.null
+      expect(app.doc).to.be.null
+      expect(app.customComponentMap).to.be.null
+    })
+    it('the incomplete data', () => {
+      const appx = createApp()
+      delete appx.vm
+      ctrl.destroy(appx)
+      expect(appx.id).to.be.empty
+      expect(appx.blocks).to.be.null
+      expect(appx.vm).to.be.null
+      expect(appx.doc).to.be.null
+      expect(appx.customComponentMap).to.be.null
+    })
+    it('clear vms', () => {
+      const appy = createApp()
+      appy.vm = {
+        $emit () {},
+        _watchers: [{ teardown () {} }],
+        _childrenVms: [{ $emit () {} }]
+      }
+      ctrl.destroy(appy)
+      expect(appy.id).to.be.empty
+      expect(appy.blocks).to.be.null
+      expect(appy.vm).to.be.null
+      expect(appy.doc).to.be.null
+      expect(appy.customComponentMap).to.be.null
+    })
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/app/downgrade.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/app/downgrade.js b/test/js-framework/unit/default/app/downgrade.js
new file mode 100644
index 0000000..6c84378
--- /dev/null
+++ b/test/js-framework/unit/default/app/downgrade.js
@@ -0,0 +1,265 @@
+/*
+ * 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 chai from 'chai'
+const { expect } = chai
+
+import * as Downgrade from '../../../../../runtime/frameworks/legacy/app/downgrade'
+
+describe('downgrade', () => {
+  describe('normalizeVersion', () => {
+    it('should be normalize version format', () => {
+      const cases = [
+        ['', '0.0.0'],
+        ['1', '1.0.0'],
+        ['1.0', '1.0.0'],
+        ['1.0.0', '1.0.0'],
+        ['1.0.0.0', '1.0.0'],
+        ['1.3.4.1230', '1.3.4']
+      ]
+
+      cases.map(function (item, index) {
+        const input = item[0]
+        const should = item[1]
+        const test = Downgrade.normalizeVersion(input)
+
+        expect(test).to.be.equal(should)
+      })
+    })
+
+    it('invalid version format', () => {
+      expect(Downgrade.normalizeVersion([])).to.be.equal('0.0.0')
+      expect(Downgrade.normalizeVersion({})).to.be.equal('0.0.0')
+      expect(Downgrade.normalizeVersion(2.0)).to.be.equal('0.0.0')
+    })
+  })
+
+  describe('getError', () => {
+    it('should be get right error message', () => {
+      const cases = [
+        [1000, 'osName', 'iphone', 'iphone'],
+        [1001, 'osVersion', '1.0.0', '1.0.0'],
+        [1002, 'appVersion', '3.2.2', '>3.0.0'],
+        [1003, 'weexVersion', '1.2.3', '>=1.2.3'],
+        [1004, 'deviceModel', '1.0.0', ['1.0.0']]
+      ]
+
+      cases.map(function (item, index) {
+        const code = item[0]
+        const key = item[1]
+        const val = item[2]
+        const criteria = item[3]
+        const test = Downgrade.getError(key, val, criteria)
+
+        expect(test.code).to.be.equal(code)
+        expect(test.errorType).to.be.equal(1)
+        expect(test.isDowngrade).to.be.equal(true)
+      })
+    })
+  })
+
+  describe('check', () => {
+    it('should be match config', () => {
+      const deviceInfo = {
+        platform: 'ios',
+        osVersion: '9.2.1',
+        appVersion: '5.4.0',
+        weexVersion: '1.3.0',
+        deviceModel: 'iPhone6.2'
+      }
+
+      const cases = [
+        [{ ios: { osVersion: '>=9' }}, true],
+        [{ ios: { appVersion: '<10.0.0' }}, true],
+        [{ ios: { appVersion: '>10.0.0' }}, false],
+        [{ ios: { appVersion: '5.4.0' }}, true],
+        [{ ios: { deviceModel: 'ABC' }}, false],
+        [{ ios: { deviceModel: 'iPhone6.2' }}, true],
+        [{ ios: { deviceModel: ['iPhone6.2', 'ABC'] }}, true],
+        [{ ios: { osVersion: '>9', deviceModel: ['iPhone6.2', 'ABC'] }}, true],
+        [{ ios: { osVersion: '>=9', deviceModel: ['iPhone6.2', 'ABC'] }}, true],
+        [{ ios: { weexVersion: '<=9' }}, true]
+      ]
+
+      cases.map(function (item, index) {
+        const criteria = item[0]
+        const should = item[1]
+        const test = Downgrade.check(criteria, deviceInfo)
+
+        expect(test.isDowngrade).to.be.equal(should)
+      })
+    })
+
+    it('should be match config without app version', () => {
+      const deviceInfo = {
+        platform: 'iOS',
+        osVersion: '9.2',
+        weexVersion: '1.3.0',
+        deviceModel: 'iPhone6.2'
+      }
+
+      const cases = [
+        [{ ios: { osVersion: '>=9' }}, true],
+        [{ ios: { osVersion: '>8' }}, true],
+        [{ ios: { appVersion: '<10.0.0' }}, false],
+        [{ ios: { appVersion: '>10.0.0' }}, false],
+        [{ ios: { appVersion: '5.4.0' }}, false],
+        [{ ios: { deviceModel: 'ABC' }}, false],
+        [{ ios: { deviceModel: 'iPhone6.2' }}, true],
+        [{ ios: { deviceModel: ['MF353TA/A', 'ABC'] }}, false],
+        [{ ios: { osVersion: '>9', deviceModel: ['MF353TA/A', 'ABC'] }}, true],
+        [{ ios: { osVersion: '>=9', deviceModel: ['MF353TA/A', 'ABC'] }}, true],
+        [{ ios: { weexVersion: '<=9' }}, true],
+        [{ ios: { weexVersion: '>=1.3.0' }}, true],
+        [{ ios: { weexVersion: '>=1.3.0' }}, true]
+      ]
+
+      cases.map(function (item, index) {
+        const criteria = item[0]
+        const should = item[1]
+        const test = Downgrade.check(criteria, deviceInfo)
+
+        expect(test.isDowngrade).to.be.equal(should)
+      })
+    })
+
+    it('should be match config and deviceInfo from global var', () => {
+      global.WXEnvironment = {
+        platform: 'iOS',
+        osVersion: '9.2',
+        appVersion: '5.4.0',
+        weexVersion: '1.3.0',
+        deviceModel: 'iPhone6.2'
+      }
+
+      const cases = [
+        [{ ios: { osVersion: '>=9' }}, true],
+        [{ ios: { osVersion: '>8' }}, true],
+        [{ ios: { appVersion: '<10.0.0' }}, true],
+        [{ ios: { appVersion: '>10.0.0' }}, false],
+        [{ ios: { appVersion: '5.4.0' }}, true],
+        [{ ios: { deviceModel: 'ABC' }}, false],
+        [{ ios: { deviceModel: 'iPhone6.2' }}, true],
+        [{ ios: { deviceModel: ['MF353TA/A', 'ABC'] }}, false],
+        [{ ios: { osVersion: '>9', deviceModel: ['MF353TA/A', 'ABC'] }}, true],
+        [{ ios: { osVersion: '>=9', deviceModel: ['MF353TA/A', 'ABC'] }}, true],
+        [{ ios: { weexVersion: '<=9' }}, true],
+        [{ ios: { weexVersion: '>=1.3.0' }}, true],
+        [{ ios: { weexVersion: '>=1.3.0' }}, true]
+      ]
+
+      cases.map(function (item, index) {
+        const criteria = item[0]
+        const should = item[1]
+        const test = Downgrade.check(criteria)
+
+        expect(test.isDowngrade).to.be.equal(should)
+      })
+    })
+
+    it('should be using using custom check', () => {
+      const deviceInfo = {
+        platform: 'iOS',
+        osVersion: '9.2',
+        appVersion: '5.4.0',
+        weexVersion: '1.3.0',
+        deviceModel: 'iPhone6.2'
+      }
+
+      const cases = [
+        [
+          function () {
+
+          }, false
+        ],
+        [
+          function () {
+            return true
+          }, true
+        ],
+        [
+          function () {
+            return false
+          }, false
+        ],
+        [
+          function (deviceInfo) {
+            if (deviceInfo.platform === 'iOS') {
+              return true
+            }
+            return false
+          }, true
+        ],
+        [
+          function (deviceInfo, tools) {
+            if (deviceInfo.osVersion === '9.2') {
+              return true
+            }
+            return false
+          }, true
+        ],
+        [
+          function (deviceInfo, tools) {
+            if (tools.normalizeVersion(deviceInfo.osVersion) === '9.2.0') {
+              return true
+            }
+            return false
+          }, true
+        ],
+        [
+          function (deviceInfo, tools) {
+            return tools.semver.satisfies(tools.normalizeVersion(deviceInfo.osVersion), '9.2.0')
+          }, true
+        ],
+        [
+          function (deviceInfo, tools) {
+            return tools.semver.satisfies(deviceInfo.appVersion, '5.4.0')
+          }, true
+        ],
+        [
+          function (deviceInfo, tools) {
+            return tools.semver.satisfies(deviceInfo.weexVersion, '1.3.0')
+          }, true
+        ],
+        [
+          function (deviceInfo, tools) {
+            return deviceInfo.deviceModel === 'iPhone6.2'
+          }, true
+        ],
+        [
+          function (deviceInfo, tools) {
+            return deviceInfo.deviceModel === 'AndroidPad'
+          }, false
+        ],
+        [
+          function (deviceInfo, tools) {
+            return ['iPhone6.2', 'iPhone7.1'].indexOf(deviceInfo.deviceModel) >= 0
+          }, true
+        ]
+      ]
+
+      cases.map(function (item, index) {
+        const criteria = item[0]
+        const should = item[1]
+        const test = Downgrade.check(criteria, deviceInfo)
+
+        expect(test.isDowngrade).to.be.equal(should)
+      })
+    })
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/app/index.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/app/index.js b/test/js-framework/unit/default/app/index.js
new file mode 100644
index 0000000..fdfb9ac
--- /dev/null
+++ b/test/js-framework/unit/default/app/index.js
@@ -0,0 +1,147 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import App from '../../../../../runtime/frameworks/legacy/app'
+import { Element, Document } from '../../../../../runtime/vdom'
+
+describe('App Instance', () => {
+  const oriDocumentHandler = Document.handler
+  const sendTasksSpy = sinon.spy()
+  let app
+
+  beforeEach(() => {
+    Document.handler = sendTasksSpy
+    const id = Date.now() + ''
+    app = new App(id, {})
+  })
+
+  afterEach(() => {
+    Document.handler = oriDocumentHandler
+  })
+
+  describe('normal check', () => {
+    it('is a class', () => {
+      expect(App).to.be.an('function')
+    })
+
+    it('being created', () => {
+      expect(app).to.be.an('object')
+      expect(app).to.be.instanceof(App)
+    })
+
+    it('with some apis', () => {
+      expect(app.requireModule).a.function
+      expect(app.updateActions).a.function
+      expect(app.callTasks).a.function
+    })
+
+    it('run apis', () => {
+      expect(app.requireModule('stream')).to.deep.equal({})
+      expect(app.updateActions()).to.be.undefined
+      expect(app.callTasks([])).to.be.undefined
+    })
+  })
+
+  describe('call tasks', () => {
+    it('with no args & callback', () => {
+      const tasks = [{
+        module: 'dom',
+        method: 'createBody',
+        args: []
+      }]
+
+      app.callTasks(tasks)
+      expect(sendTasksSpy.lastCall.args[1]).to.deep.equal(tasks)
+    })
+
+    it('with callback', (done) => {
+      const tasks = [{
+        module: 'dom',
+        method: 'createBody',
+        args: []
+      }]
+
+      app.callTasks(tasks)
+      expect(sendTasksSpy.lastCall.args[1]).to.deep.equal(tasks)
+      done()
+    })
+
+    it('with function arg', (done) => {
+      const callbackId = '1'
+
+      const tasks = [{
+        module: 'dom',
+        method: 'createBody',
+        args: [() => {}]
+      }]
+
+      app.callTasks(tasks)
+      expect(sendTasksSpy.lastCall.args[1]).to.deep.equal([{
+        module: 'dom',
+        method: 'createBody',
+        args: [callbackId]
+      }])
+      done()
+    })
+
+    it('with node arg', (done) => {
+      const node = new Element()
+      node.ref = '1'
+
+      const tasks = [{
+        module: 'dom',
+        method: 'createBody',
+        args: [node]
+      }]
+
+      app.callTasks(tasks)
+      expect(sendTasksSpy.lastCall.args[1]).to.deep.equal([{
+        module: 'dom',
+        method: 'createBody',
+        args: [node.ref]
+      }])
+      done()
+    })
+
+    it('with callback after close', (done) => {
+      const callbackId = '1'
+
+      const tasks = [{
+        module: 'dom',
+        method: 'createBody',
+        args: [() => {}]
+      }]
+
+      app.doc.close()
+
+      app.callTasks(tasks)
+      expect(sendTasksSpy.lastCall.args[1]).to.deep.equal([{
+        module: 'dom',
+        method: 'createBody',
+        args: [callbackId]
+      }])
+      done()
+    })
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/app/register.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/app/register.js b/test/js-framework/unit/default/app/register.js
new file mode 100644
index 0000000..853b428
--- /dev/null
+++ b/test/js-framework/unit/default/app/register.js
@@ -0,0 +1,200 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import {
+  getModule,
+  clearModules,
+  initModules,
+  initMethods,
+  requireModule,
+  requireCustomComponent,
+  registerCustomComponent
+} from '../../../../../runtime/frameworks/legacy/app/register'
+
+function Ctx () {
+  this.customComponentMap = {}
+  this.callTasks = sinon.spy()
+}
+
+describe('register', () => {
+  let ctx
+
+  before(() => {
+    clearModules()
+    ctx = new Ctx()
+  })
+
+  after(() => {
+    clearModules()
+  })
+
+  describe('component', () => {
+    it('with exports', () => {
+      const def = {
+        a: 'b'
+      }
+      registerCustomComponent(ctx, 'componentA', def)
+      expect(requireCustomComponent(ctx, 'componentA')).to.deep.equal(def)
+    })
+
+    it('with a existing name', () => {
+      const def = {
+        a: 'b'
+      }
+      sinon.stub(console, 'error')
+      registerCustomComponent(ctx, 'componentA', def)
+      expect(console.error).callCount(1)
+      console.error.restore()
+    })
+  })
+
+  describe('module', () => {
+    it('with a old format', () => {
+      initModules({
+        dom: [
+          'createBody',
+          'addElement'
+        ]
+      })
+      expect(requireModule(ctx, 'dom')).to.have.any.keys('createBody', 'addElement')
+    })
+
+    it('with a new format', () => {
+      initModules({
+        dom: [
+          {
+            name: 'moveElement',
+            args: ['string', 'string']
+          }
+        ],
+        stream: [
+          {
+            name: 'sendMtop',
+            args: ['object', 'function']
+          }
+        ]
+      })
+      expect(requireModule(ctx, 'dom')).to.have.all.keys('createBody', 'addElement', 'moveElement')
+      expect(requireModule(ctx, 'stream')).to.have.all.keys('sendMtop')
+    })
+
+    it('with a existed module.method', () => {
+      initModules({
+        dom: [
+          {
+            name: 'moveElement',
+            args: ['string', 'string', 'string']
+          }
+        ]
+      }, true)
+      initModules({
+        stream: [
+          {
+            name: 'sendMtop',
+            args: ['object']
+          }
+        ]
+      })
+      expect(getModule('dom').moveElement).to.deep.equal({
+        name: 'moveElement',
+        args: ['string', 'string', 'string']
+      })
+
+      expect(getModule('stream').sendMtop).to.deep.equal({
+        name: 'sendMtop',
+        args: ['object', 'function']
+      })
+    })
+
+    it('run registered module', () => {
+      initModules({
+        event: [{
+          name: 'openURL',
+          args: ['string']
+        }]
+      })
+      const event = requireModule(ctx, 'event')
+      expect(event).to.have.keys('openURL')
+
+      event.openURL('http://test.com')
+      expect(ctx.callTasks.callCount).to.be.equal(1)
+    })
+  })
+
+  describe('api', () => {
+    it('a common api', () => {
+      initMethods(Ctx, {
+        $test1: function () {
+          return {
+            ctx: this,
+            value: 'test1'
+          }
+        },
+        $test2: function () {
+          return {
+            ctx: this,
+            value: 'test2'
+          }
+        }
+      })
+
+      expect(ctx.$test1).to.be.a('Function')
+      expect(ctx.$test1()).to.deep.equal({
+        ctx: ctx,
+        value: 'test1'
+      })
+      expect(ctx.$test2).to.be.an('Function')
+      expect(ctx.$test2()).to.deep.equal({
+        ctx: ctx,
+        value: 'test2'
+      })
+    })
+
+    it('override api', () => {
+      initMethods(Ctx, {
+        $override: function () {
+          return {
+            ctx: this,
+            value: 'first'
+          }
+        }
+      })
+
+      initMethods(Ctx, {
+        $override: function () {
+          return {
+            ctx: this,
+            value: 'ignored'
+          }
+        }
+      })
+
+      expect(ctx.$override).to.be.a('Function')
+      expect(ctx.$override()).to.deep.equal({
+        ctx: ctx,
+        value: 'first'
+      })
+    })
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/app/viewport.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/app/viewport.js b/test/js-framework/unit/default/app/viewport.js
new file mode 100644
index 0000000..2caf37c
--- /dev/null
+++ b/test/js-framework/unit/default/app/viewport.js
@@ -0,0 +1,79 @@
+/*
+ * 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 chai from 'chai'
+import sinon from 'sinon'
+import sinonChai from 'sinon-chai'
+const { expect } = chai
+chai.use(sinonChai)
+
+import * as viewport from '../../../../../runtime/frameworks/legacy/app/viewport'
+
+describe('viewport', function () {
+  const originalCallNative = global.callNative
+  const { setViewport, validateViewport } = viewport
+  const mockApp = {
+    id: 'mock',
+    callTasks (...args) {
+      global.callNative(...args)
+    }
+  }
+
+  before(() => {
+    sinon.stub(console, 'warn')
+  })
+
+  beforeEach(() => {
+    global.callNative = sinon.spy()
+  })
+  afterEach(() => {
+    global.callNative = originalCallNative
+    console.warn.reset()
+  })
+
+  it('invalid setViewport', () => {
+    setViewport()
+    expect(global.callNative.callCount).to.be.equal(0)
+    setViewport({})
+    expect(global.callNative.callCount).to.be.equal(0)
+  })
+
+  it('setViewport', () => {
+    setViewport(mockApp, {})
+    expect(global.callNative.callCount).to.be.equal(1)
+    setViewport(mockApp, { width: 640 })
+    expect(global.callNative.callCount).to.be.equal(2)
+    setViewport(mockApp, { width: 'device-width' })
+    expect(global.callNative.callCount).to.be.equal(3)
+  })
+
+  it('validateViewport', () => {
+    expect(validateViewport()).to.be.false
+    expect(console.warn.callCount).to.be.equal(1)
+    expect(validateViewport({})).to.be.false
+    expect(console.warn.callCount).to.be.equal(2)
+
+    expect(validateViewport({ width: 200 })).to.be.true
+    expect(console.warn.callCount).to.be.equal(2)
+    expect(validateViewport({ width: 'device-width' })).to.be.true
+    expect(console.warn.callCount).to.be.equal(2)
+
+    expect(validateViewport({ width: 'initial-width' })).to.be.false
+    expect(console.warn.callCount).to.be.equal(3)
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/util/index.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/util/index.js b/test/js-framework/unit/default/util/index.js
new file mode 100644
index 0000000..0d778f7
--- /dev/null
+++ b/test/js-framework/unit/default/util/index.js
@@ -0,0 +1,185 @@
+/*
+ * 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 chai from 'chai'
+const { expect } = chai
+
+import * as util from '../../../../../runtime/frameworks/legacy/util'
+
+describe('Util', () => {
+  describe('isReserved', () => {
+    it('should be first char is $ or _', () => {
+      expect(util.isReserved('$isReserved')).eql(true)
+      expect(util.isReserved('_isReserved')).eql(true)
+      expect(util.isReserved('isReserved$')).eql(false)
+      expect(util.isReserved('is$Reserved')).eql(false)
+      expect(util.isReserved('isReserved')).eql(false)
+    })
+  })
+
+  describe('camelize', () => {
+    it('should be camelize a hyphen-delmited string', () => {
+      expect(util.camelize('mini-camelize')).eql('miniCamelize')
+      expect(util.camelize('mini-camelize-test')).eql('miniCamelizeTest')
+    })
+  })
+
+  describe('toArray', () => {
+    it('should be convert an Array-like object to a real Array', () => {
+      const mockup = {
+        '0': 1,
+        '1': 2,
+        '2': 3,
+        length: 3
+      }
+
+      expect(util.toArray(mockup)).eql([1, 2, 3])
+      expect(util.toArray(mockup, 2)).eql([3])
+    })
+  })
+
+  describe('extend', () => {
+    it('should be extend object', () => {
+      const mockup = {
+        a: 1,
+        b: 2
+      }
+
+      expect(util.extend(mockup, { b: 4, c: 5 })).eql({
+        a: 1,
+        b: 4,
+        c: 5
+      })
+
+      expect(util.extend(mockup, { c: 5 })).eql({
+        a: 1,
+        b: 4,
+        c: 5
+      })
+
+      expect(util.extend(mockup, { c: 5 }, { d: 6 }, { e: 7 })).eql({
+        a: 1,
+        b: 4,
+        c: 5,
+        d: 6,
+        e: 7
+      })
+    })
+
+    it('polyfill Object.assign', () => {
+      const _assign = Object.assign
+      Object.assign = undefined
+
+      const mockup = {
+        a: 1,
+        b: 2
+      }
+
+      expect(util.extend(mockup, { b: 4, c: 5 })).eql({
+        a: 1,
+        b: 4,
+        c: 5
+      })
+
+      expect(util.extend(mockup, { c: 5 })).eql({
+        a: 1,
+        b: 4,
+        c: 5
+      })
+
+      expect(util.extend(mockup, { c: 5 }, { d: 6 }, { e: 7 })).eql({
+        a: 1,
+        b: 4,
+        c: 5,
+        d: 6,
+        e: 7
+      })
+
+      Object.assign = _assign
+    })
+  })
+
+  describe('isObject', () => {
+    it('should be an array or object', () => {
+      expect(util.isObject({})).eql(true)
+      expect(util.isObject([])).eql(true)
+      expect(util.isObject('a')).eql(false)
+      expect(util.isObject(1)).eql(false)
+      expect(util.isObject(true)).eql(false)
+      expect(util.isObject(null)).eql(false)
+      expect(util.isObject(undefined)).eql(false)
+      expect(util.isObject(function () {})).eql(false)
+      expect(util.isObject(/\w*/)).eql(true) // regexp is object
+      expect(util.isObject(new Date())).eql(true)
+    })
+  })
+
+  describe('isPlainObject', () => {
+    it('should be an real object', () => {
+      expect(util.isPlainObject({})).eql(true)
+      expect(util.isPlainObject([])).eql(false)
+      expect(util.isPlainObject('a')).eql(false)
+      expect(util.isPlainObject(1)).eql(false)
+      expect(util.isPlainObject(true)).eql(false)
+      expect(util.isPlainObject(null)).eql(false)
+      expect(util.isPlainObject(undefined)).eql(false)
+      expect(util.isPlainObject(function () {})).eql(false)
+      expect(util.isPlainObject(/\w*/)).eql(false)
+      expect(util.isPlainObject(new Date())).eql(false)
+    })
+  })
+
+  describe('typof', () => {
+    it('should be real type', () => {
+      expect(util.typof({})).eql('object')
+      expect(util.typof([])).eql('array')
+      expect(util.typof('a')).eql('string')
+      expect(util.typof(1)).eql('number')
+      expect(util.typof(true)).eql('boolean')
+      expect(util.typof(null)).eql('null')
+      expect(util.typof(undefined)).eql('undefined')
+      expect(util.typof(function () {})).eql('function')
+      expect(util.typof(/\w*/)).eql('regexp')
+      expect(util.typof(new Date())).eql('date')
+    })
+  })
+
+  describe('def', () => {
+    it('should be define a non-enumerable property', () => {
+      const obj = {}
+      util.def(obj, 'a', 1, true)
+      expect(obj).eql({ a: 1 })
+
+      util.def(obj, 'b', 1, false)
+      expect(obj).eql({ a: 1 })
+
+      util.def(obj, 'c', 1, true)
+      expect(obj).eql({ a: 1, c: 1 })
+
+      util.def(obj, 'd', 1)
+      expect(obj).eql({ a: 1, c: 1 })
+    })
+  })
+
+  describe('hyphenate', () => {
+    it('should hyphenate a camelCase string', () => {
+      const str = 'helloWorldWeex'
+      expect(util.hyphenate(str)).eql('hello-world-weex')
+    })
+  })
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/unit/default/util/shared.js
----------------------------------------------------------------------
diff --git a/test/js-framework/unit/default/util/shared.js b/test/js-framework/unit/default/util/shared.js
new file mode 100644
index 0000000..6778206
--- /dev/null
+++ b/test/js-framework/unit/default/util/shared.js
@@ -0,0 +1,143 @@
+/*
+ * 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 { expect } from 'chai'
+
+import * as utils from '../../../../../runtime/frameworks/legacy/util/shared'
+
+describe('test util functions', () => {
+  it('extend', () => {
+    expect(typeof utils.extend).to.be.equal('function')
+
+    const a = { name: 'A', a: true, c: true }
+    const b = { name: 'B', b: true }
+    utils.extend(a, b, { c: false })
+    expect(a).to.be.deep.equal({ name: 'B', a: true, b: true, c: false })
+    expect(b).to.be.deep.equal({ name: 'B', b: true })
+  })
+
+  it('def', () => {
+    expect(typeof utils.def).to.be.equal('function')
+    const obj = { type: 'none' }
+    utils.def(obj, 'count', 42, false)
+
+    expect(obj.hasOwnProperty('count')).to.be.true
+    expect(obj.propertyIsEnumerable('count')).to.be.false
+    expect(obj.type).to.be.equal('none')
+    expect(obj.count).to.be.equal(42)
+    obj.count = 24
+    expect(obj.count).to.be.equal(24)
+  })
+
+  it('remove', () => {
+    expect(typeof utils.remove).to.be.equal('function')
+
+    const arr = ['first', null, 42, null, 28]
+    const obj = { 0: 'first', 1: null, 2: 42 }
+
+    utils.remove(arr, null)
+    expect(arr).to.deep.equal(['first', 42, null, 28])
+    utils.remove(arr, null)
+    expect(arr).to.deep.equal(['first', 42, 28])
+    utils.remove(arr, null)
+    expect(arr).to.deep.equal(['first', 42, 28])
+    utils.remove(arr, 42)
+    expect(arr).to.deep.equal(['first', 28])
+
+    utils.remove(obj, 42)
+    expect(obj).to.deep.equal(obj)
+  })
+
+  it('hasOwn', () => {
+    expect(typeof utils.hasOwn).to.be.equal('function')
+
+    function Point () {
+      this.x = 0
+    }
+    Point.prototype.y = 1
+
+    const p = new Point()
+    expect(p.x).to.be.equal(0)
+    expect(p.y).to.be.equal(1)
+
+    expect(utils.hasOwn(p, 'x')).to.be.true
+    expect(utils.hasOwn(p, 'y')).to.be.false
+  })
+
+  it('bind', () => {
+    expect(typeof utils.bind).to.be.equal('function')
+
+    function who () {
+      return this.name
+    }
+    const obj = { name: 'fixed' }
+    const ctx = { name: 'changed' }
+    const askWho = utils.bind(who, obj)
+
+    expect(who()).to.be.undefined
+    expect(askWho()).to.be.equal('fixed')
+    expect(askWho.call(ctx)).to.be.equal('fixed')
+    expect(askWho.apply(ctx)).to.be.equal('fixed')
+    expect(who.apply(ctx)).to.be.equal('changed')
+  })
+
+  it('toArray', () => {
+    expect(typeof utils.toArray).to.be.equal('function')
+    const obj = { length: 2 }
+    const arr = utils.toArray(obj)
+
+    expect(arr).to.deep.equal([undefined, undefined])
+    expect(arr.hasOwnProperty('0')).to.be.true
+    expect(arr.hasOwnProperty('1')).to.be.true
+    expect(arr.hasOwnProperty('2')).to.be.false
+
+    const arrayLike = {
+      0: 'empty',
+      1: 'first',
+      2: 'second',
+      length: 3
+    }
+    expect(utils.toArray(arrayLike, 1)).to.deep.equal(['first', 'second'])
+  })
+
+  it('isObject', () => {
+    expect(typeof utils.isObject).to.be.equal('function')
+    expect(utils.isObject({})).to.be.true
+    expect(utils.isObject([])).to.be.true
+    expect(utils.isObject(new Date())).to.be.true
+    expect(utils.isObject()).to.be.false
+    expect(utils.isObject('')).to.be.false
+    expect(utils.isObject(42)).to.be.false
+    expect(utils.isObject(NaN)).to.be.false
+    expect(utils.isObject(null)).to.be.false
+    expect(utils.isObject(undefined)).to.be.false
+  })
+
+  it('isPlainObject', () => {
+    expect(typeof utils.isPlainObject).to.be.equal('function')
+    expect(utils.isPlainObject({})).to.be.true
+    expect(utils.isPlainObject([])).to.be.false
+    expect(utils.isPlainObject(new Date())).to.be.false
+    expect(utils.isPlainObject()).to.be.false
+    expect(utils.isPlainObject('')).to.be.false
+    expect(utils.isPlainObject(42)).to.be.false
+    expect(utils.isPlainObject(NaN)).to.be.false
+    expect(utils.isPlainObject(null)).to.be.false
+    expect(utils.isPlainObject(undefined)).to.be.false
+  })
+})



[13/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/signals/signals.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/signals/signals.source.js b/html5/test/case/signals/signals.source.js
deleted file mode 100644
index c16234e..0000000
--- a/html5/test/case/signals/signals.source.js
+++ /dev/null
@@ -1,84 +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.
- */
-define('@weex-component/signals-sub', function (require, exports, module) {
-  module.exports = {
-    data: function () {return {
-      thumb: '', title: ''
-    }},
-    style: {},
-    template: {
-      type: 'div',
-      children: [
-        {type: 'image', attr: {src: function () {return this.thumb}}},
-        {type: 'text', attr: {value: function () {return this.title}}}
-      ]
-    }
-  }
-})
-
-define('@weex-component/signals', function (require, exports, module) {
-  module.exports = {
-    data: function () {return {
-      list: [
-        {thumb: '<some image url 1>', title: 'Hello World 1'},
-        {thumb: '<some image url 2>', title: 'Hello World 2'},
-        {thumb: '<some image url 3>', title: 'Hello World 3'},
-        {thumb: '<some image url 4>', title: 'Hello World 4'},
-        {thumb: '<some image url 5>', title: 'Hello World 5'},
-        {thumb: '<some image url 6>', title: 'Hello World 6'},
-        {thumb: '<some image url 7>', title: 'Hello World 7'},
-        {thumb: '<some image url 8>', title: 'Hello World 8'},
-        {thumb: '<some image url 9>', title: 'Hello World 9'},
-        {thumb: '<some image url 10>', title: 'Hello World 10'},
-        {thumb: '<some image url 11>', title: 'Hello World 11'},
-        {thumb: '<some image url 12>', title: 'Hello World 12'},
-        {thumb: '<some image url 13>', title: 'Hello World 13'},
-        {thumb: '<some image url 14>', title: 'Hello World 14'},
-        {thumb: '<some image url 15>', title: 'Hello World 15'},
-        {thumb: '<some image url 16>', title: 'Hello World 16'},
-        {thumb: '<some image url 17>', title: 'Hello World 17'},
-        {thumb: '<some image url 18>', title: 'Hello World 18'},
-        {thumb: '<some image url 19>', title: 'Hello World 19'},
-        {thumb: '<some image url 20>', title: 'Hello World 20'}
-      ]
-    }},
-    style: {},
-    template: {
-      type: 'div',
-      children: [
-        {
-          type: 'signals-sub',
-          repeat: {
-            expression: function () {return this.list},
-            key: 'i',
-            value: 'item'
-          },
-          attr: {
-            thumb: function () {return this.item.thumb},
-            title: function () {return this.item.title}
-          }
-        }
-      ]
-    }
-  }
-})
-
-// // require module
-
-bootstrap('@weex-component/signals')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/signals/timer.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/signals/timer.output.js b/html5/test/case/signals/timer.output.js
deleted file mode 100644
index f645aa2..0000000
--- a/html5/test/case/signals/timer.output.js
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  type: 'div',
-  children: [{
-    type: 'text',
-    attr: {
-      value: 'foo'
-    }
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/signals/timer.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/signals/timer.source.js b/html5/test/case/signals/timer.source.js
deleted file mode 100644
index 4222d3e..0000000
--- a/html5/test/case/signals/timer.source.js
+++ /dev/null
@@ -1,54 +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.
- */
-define('@weex-component/timer', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {
-      return {
-        x: 'foo'
-      }
-    },
-    created: function () {
-      setTimeout(function () {
-        this.x = 'bar'
-      }.bind(this), 1500)
-    }
-  }
-
-
-;module.exports.template = {
-  "type": "div",
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "value": function () {
-          return this.x
-        }
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/timer')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/tester.js
----------------------------------------------------------------------
diff --git a/html5/test/case/tester.js b/html5/test/case/tester.js
deleted file mode 100644
index c885fef..0000000
--- a/html5/test/case/tester.js
+++ /dev/null
@@ -1,528 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-
-const expect = chai.expect
-chai.use(sinonChai)
-
-import { createRuntime, createApp, getCode } from './prepare'
-
-describe('test input and output', function () {
-  let runtime
-
-  before(() => {
-    runtime = createRuntime()
-    sinon.stub(console, 'info')
-    sinon.stub(console, 'warn')
-    sinon.stub(console, 'error')
-  })
-
-  after(() => {
-    console.info.restore()
-    console.warn.restore()
-    console.error.restore()
-  })
-
-  describe('one-time rendering cases', () => {
-    let app
-
-    beforeEach(() => {
-      app = createApp(runtime)
-      callNativeHandler = (...args) => app._target.callNative(...args)
-    })
-
-    afterEach(() => {
-      app = null
-      callNativeHandler = function () {}
-    })
-
-    function checkOutput (app, name) {
-      const source = getCode('basic/' + name + '.source.js')
-      const output = getCode('basic/' + name + '.output.js')
-      const result = eval('(' + output + ')')
-
-      app.$create(source)
-      expect(app.getRealRoot()).eql(result)
-      app.$destroy()
-    }
-
-    it('global Weex object', () => checkOutput(app, 'global-weex-object'))
-
-    it('single case', () => checkOutput(app, 'foo'))
-    it('foo2 case', () => checkOutput(app, 'foo2'))
-    it('foo3 case', () => checkOutput(app, 'foo3'))
-    it('foo4 case', () => checkOutput(app, 'foo4'))
-    it('foo5 case', () => checkOutput(app, 'foo5'))
-    it('foo6 case', () => checkOutput(app, 'foo6'))
-    it('foo7 case', () => checkOutput(app, 'foo7'))
-    it('foo8 case', () => checkOutput(app, 'foo8'))
-    it('foo9 case', () => checkOutput(app, 'foo9'))
-
-    it('static1 case', () => checkOutput(app, 'static1'))
-    it('static2 case', () => checkOutput(app, 'static2'))
-    it('static3 case', () => checkOutput(app, 'static3'))
-    it('static4 case', () => checkOutput(app, 'static4'))
-
-    it('computed in repeat case', () => checkOutput(app, 'computed-in-repeat'))
-    it('backward(register/render) case', () => checkOutput(app, 'backward1'))
-    it('backward(define/require) case', () => checkOutput(app, 'backward2'))
-    it('append case', () => checkOutput(app, 'append'))
-    it('append-root case', () => checkOutput(app, 'append-root'))
-
-    it('if case', () => checkOutput(app, 'if'))
-    it('if-repeat case', () => checkOutput(app, 'if-repeat'))
-    it('if in root element case', () => checkOutput(app, 'if-root'))
-    it('repeat in root element case', () => checkOutput(app, 'repeat-root'))
-    it('repeat with index case case', () => checkOutput(app, 'repeat-index'))
-    it('repeat with array v case', () => checkOutput(app, 'repeat-array-v'))
-    it('repeat with array kv case', () => checkOutput(app, 'repeat-array-kv'))
-    it('repeat with array no-kv case', () => checkOutput(app, 'repeat-array-no-kv'))
-    it('repeat with array non-obj case', () => checkOutput(app, 'repeat-array-non-obj'))
-    it('repeat watch case', () => checkOutput(app, 'repeat-watch'))
-
-    it('id case', () => checkOutput(app, 'id'))
-    it('dynamic id case', () => checkOutput(app, 'dynamic-id'))
-
-    it('reset style case', () => checkOutput(app, 'reset-style'))
-    it('dynamic type case', () => checkOutput(app, 'dynamic-type'))
-    it('dynamic property case', () => checkOutput(app, 'dynamic-property'))
-
-    it('bind subvm', () => checkOutput(app, 'subvm'))
-    it('components options', () => checkOutput(app, 'components'))
-    it('change data when created', () => checkOutput(app, 'created'))
-    it('change data when ready', () => checkOutput(app, 'ready'))
-
-    it('require 3rd', () => checkOutput(app, 'require'))
-    it('a correct transformer version', () => checkOutput(app, 'transformer1'))
-
-    it('promise case', () => checkOutput(app, 'promise'))
-  })
-
-  describe('invalid usage', function () {
-    describe('strict mode', () => {
-      const readSource = name => getCode('throws/' + name + '.source.js')
-
-      let app
-      beforeEach(() => { app = createApp(runtime) })
-      afterEach(() => { app = null })
-
-      it('global variable 1', () => {
-        const sourceCode = readSource('global-variable1')
-        expect(() => app.$create(sourceCode)).to.throw(ReferenceError)
-      })
-
-      it('global variable 2', () => {
-        const sourceCode = readSource('global-variable2')
-        expect(() => app.$create(sourceCode)).to.throw(ReferenceError)
-      })
-
-      it('global variable 3', () => {
-        const sourceCode = readSource('global-variable3')
-        expect(() => app.$create(sourceCode)).to.throw(ReferenceError)
-      })
-    })
-  })
-
-  describe('complex cases', function () {
-    let app
-
-    beforeEach(() => {
-      app = createApp(runtime)
-      callNativeHandler = (...args) => app._target.callNative(...args)
-    })
-
-    afterEach(() => {
-      app = null
-      callNativeHandler = function () {}
-    })
-
-    const readSource = name => getCode('complex/' + name + '.source.js')
-    const readOutput = name => getCode('complex/' + name + '.output.js')
-
-    it('computed case', () => {
-      const name = 'computed'
-      const sourceCode = readSource(name)
-      const outputCode = readOutput(name)
-
-      app.$create(sourceCode)
-      const expected = eval('(' + outputCode + ')')
-      expect(app.getRealRoot()).eql(expected)
-
-      app.$refresh({ x: 10 })
-      expected.children[0].attr.value = 12
-      expected.children[1].attr.value = 12
-      expect(app.getRealRoot()).eql(expected)
-
-      app.$refresh({ m: 10 })
-      expected.children[0].attr.value = 20
-      expected.children[1].attr.value = 20
-      expect(app.getRealRoot()).eql(expected)
-
-      app.$destroy()
-    })
-
-    it('append-root-event case', () => {
-      const name = 'append-root-event'
-      const sourceCode = readSource(name)
-      const outputCode = readOutput(name)
-
-      app.$create(sourceCode)
-      const expected = eval('(' + outputCode + ')')
-      const actual = app.getRealRoot()
-      expect(actual).eql(expected)
-
-      app.$fireEvent(app.doc.body.children[0].ref, 'click', {})
-      const actual2 = app.getRealRoot()
-      expect(actual2.children[0].attr.value).eql(2)
-
-      app.$destroy()
-    })
-
-    it('use append tree and sub components', (done) => {
-      const name = 'component-append-tree'
-      const sourceCode = readSource(name)
-      const outputCode = readOutput(name)
-
-      app.$create(sourceCode)
-      const expected = eval('(' + outputCode + ')')
-      const actual = app.getRealRoot()
-      expect(actual).eql(expected)
-
-      setTimeout(() => {
-        const actual2 = app.getRealRoot()
-        expect(actual2.children[0].children[0].attr.value).eql('timeout')
-        expect(actual2.children[1].children[0].attr.value).eql('timeout')
-        app.$destroy()
-        done()
-      }, 200)
-    })
-
-    it('repeat with array track-by case', () => {
-      const name = 'repeat-track-by'
-      const sourceCode = readSource(name)
-      const outputCode = readOutput(name)
-
-      app.$create(sourceCode)
-      app.$refresh({
-        titlelist: [
-          { text: 'Hello World2' },
-          { text: 'Hello World1' }
-        ]
-      })
-      const expected = eval('(' + outputCode + ')')
-      expect(app.getRealRoot()).eql(expected)
-
-      app.$destroy()
-    })
-
-    it('if-refresh case', () => {
-      const name = 'if-refresh'
-      const sourceCode = readSource(name)
-      const outputCode = readOutput(name)
-
-      app.$create(sourceCode)
-      app.$refresh({ showTitle: false })
-      const expected = eval('(' + outputCode + ')')
-      expect(app.getRealRoot()).eql(expected)
-
-      app.$destroy()
-    })
-
-    it('if-repeat-refresh case', () => {
-      const name = 'if-repeat-refresh'
-      const sourceCode = readSource(name)
-      const outputCode = readOutput(name)
-
-      app.$create(sourceCode)
-      app.$refresh({
-        titlelist: [
-          { showTitle: false, title: 'Hello World1' },
-          { showTitle: true, title: 'Hello World2' },
-          { showTitle: true, title: 'Hello World3' }
-        ]
-      })
-      const expected = eval('(' + outputCode + ')')
-      expect(app.getRealRoot()).eql(expected)
-
-      app.$destroy()
-    })
-
-    it('click case', () => {
-      const name = 'click'
-      const sourceCode = readSource(name)
-      const outputCode = readOutput(name)
-
-      app.$create(sourceCode)
-      const expected = eval('(' + outputCode + ')')
-      expect(app.getRealRoot()).eql(expected)
-
-      app.$fireEvent(app.doc.body.children[0].ref, 'click', {})
-
-      app.$destroy()
-    })
-
-    it('inline click case', () => {
-      const name = 'inline-click'
-      const sourceCode = readSource(name)
-      const outputCode = readOutput(name)
-
-      app.$create(sourceCode)
-      const expected = eval('(' + outputCode + ')')
-      expect(app.getRealRoot()).eql(expected)
-
-      app.$fireEvent(app.doc.body.children[0].ref, 'click', {})
-
-      expected.children[0].attr.value = 'Hello World2'
-      expect(app.getRealRoot()).eql(expected)
-
-      app.$destroy()
-    })
-
-    it('refresh twice', () => {
-      const name = 'refresh2'
-      const sourceCode = readSource(name)
-      const outputCode = readOutput(name)
-
-      app.$create(sourceCode)
-      expect(app.getRealRoot()).eql({ type: 'container' })
-
-      app.$refresh({ ext: { showbar1: false }})
-      app.$refresh({ ext: { showbar1: true }})
-      const expected = eval('(' + outputCode + ')')
-      expect(app.getRealRoot()).eql(expected)
-
-      app.$destroy()
-    })
-
-    it('a less wrong transformer version', () => {
-      const name = 'transformer2'
-      const sourceCode = readSource(name)
-
-      const result = app.$create(sourceCode)
-      expect(result).to.be.an.instanceof(Error)
-      app.$destroy()
-    })
-
-    it('a bigger wrong transformer version', () => {
-      const name = 'transformer3'
-      const sourceCode = readSource(name)
-
-      const result = app.$create(sourceCode)
-      expect(result).to.be.an.instanceof(Error)
-      app.$destroy()
-    })
-
-    it('input binding', () => {
-      const name = 'input-binding'
-      const sourceCode = readSource(name)
-      const outputCode = readOutput(name)
-
-      app.$create(sourceCode)
-      const expected = eval('(' + outputCode + ')')
-      expect(app.getRealRoot()).eql(expected)
-
-      app.doc.body.children[0].attr.value = 'abcdefg'
-      app.$fireEvent(app.doc.body.children[0].ref, 'change', {}, { attrs: { value: 'abcdefg' }})
-      expected.children[0].attr.value = 'abcdefg'
-      expected.children.push({ type: 'text', attr: { value: '1 - abcdefg' }})
-      expect(app.getRealRoot()).eql(expected)
-
-      app.doc.body.children[0].attr.value = '12345'
-      app.$fireEvent(app.doc.body.children[0].ref, 'change', {}, { attrs: { value: '12345' }})
-      expected.children[0].attr.value = '12345'
-      expected.children.push({ type: 'text', attr: { value: '2 - 12345' }})
-      expect(app.getRealRoot()).eql(expected)
-
-      app.$destroy()
-    })
-  })
-
-  describe('multi page cases', function () {
-    let appA
-    let appB
-
-    beforeEach(() => {
-      appA = createApp(runtime)
-      appB = createApp(runtime)
-
-      callNativeHandler = (id, ...args) => {
-        switch (true) {
-          case (appA.id === id) : { appA._target.callNative(id, ...args) } break
-          case (appB.id === id) : { appB._target.callNative(id, ...args) } break
-        }
-      }
-    })
-
-    afterEach(() => {
-      appA = null
-      appB = null
-      callNativeHandler = function () {}
-    })
-
-    const readSource = name => getCode('multi/' + name + '.source.js')
-    const readOutput = name => getCode('multi/' + name + '.output.js')
-
-    it('clear-module case', () => {
-      const nameA = 'clear-moduleA'
-      const nameB = 'clear-moduleB'
-      const sourceCodeA = readSource(nameA)
-      const sourceCodeB = readSource(nameB)
-      const expectedA = eval('(' + readOutput(nameA) + ')')
-      const expectedB = eval('(' + readOutput(nameB) + ')')
-
-      appA.$create(sourceCodeA)
-      appB.$create(sourceCodeB)
-
-      expect(appB.getRealRoot()).eql(expectedB)
-
-      appB.$destroy()
-      appA.$fireEvent(appA.doc.body.children[0].ref, 'click', {})
-      expect(appA.getRealRoot()).eql(expectedA)
-
-      appA.$destroy()
-    })
-
-    it('clear-dep-target case', () => {
-      const nameError = 'clear-dep-target-error'
-      const nameFine = 'clear-dep-target-fine'
-      const sourceCodeError = readSource(nameError)
-      const sourceCodeFine = readSource(nameFine)
-      const expectedFine = eval('(' + readOutput(nameFine) + ')')
-
-      // should throw
-      expect(() => { appA.$create(sourceCodeError) }).to.throw(TypeError)
-      appA.$destroy()
-
-      // not throw
-      appB.$create(sourceCodeFine)
-      expect(appB.getRealRoot()).eql(expectedFine)
-
-      appB.$destroy()
-    })
-  })
-
-  describe('timer & callNative signals', function () {
-    let app
-
-    const callNativeSpy = sinon.spy()
-    function genCallNativeWrapper (count) {
-      return (name, tasks, cbId) => {
-        callNativeSpy(tasks)
-        const length = callNativeSpy.callCount
-        if (length > count) {
-          return -1
-        }
-        return length
-      }
-    }
-    beforeEach(() => {
-      app = createApp(runtime)
-      callNativeHandler = (...args) => app._target.callNative(...args)
-    })
-
-    afterEach(() => {
-      app = null
-      callNativeSpy.reset()
-      callNativeHandler = function () {}
-    })
-
-    const readSource = name => getCode('signals/' + name + '.source.js')
-    const readOutput = name => getCode('signals/' + name + '.output.js')
-
-    it('use HTML5 timer API', function (done) {
-      this.timeout(5000)
-      const name = 'timer'
-      const sourceCode = readSource(name)
-      const outputCode = readOutput(name)
-
-      app.$create(sourceCode)
-      const expected = eval('(' + outputCode + ')')
-      expect(app.getRealRoot()).eql(expected)
-
-      setTimeout(_ => {
-        expect(app.getRealRoot()).eql(expected)
-        setTimeout(_ => {
-          expected.children[0].attr.value = 'bar'
-          expect(app.getRealRoot()).eql(expected)
-          app.$destroy()
-          done()
-        }, 1000)
-      }, 1000)
-    })
-
-    it('use modal API', function (done) {
-      this.timeout(5000)
-      const name = 'modal'
-      const sourceCode = readSource(name)
-      const outputCode = readOutput(name)
-
-      app.$create(sourceCode)
-      const expected = eval('(' + outputCode + ')')
-      expect(app.getRealRoot()).eql(expected)
-
-      // the test driver will hold the API callback about 1 sec
-      setTimeout(_ => {
-        expected.children[0].attr.value = 'bar'
-        expect(app.getRealRoot()).eql(expected)
-        app.$destroy()
-        done()
-      }, 1500)
-    })
-
-    it('signals control', function () {
-      this.timeout(15000)
-
-      const name = 'signals'
-      const sourceCode = readSource(name)
-
-      function run (calls) {
-        callNativeSpy.reset()
-        callNativeHandler = genCallNativeWrapper(calls)
-        app.$create(sourceCode)
-        app.$destroy()
-        expect(callNativeSpy.callCount).eql(calls + 2)
-      }
-
-      for (let i = 5; i < 60; i++) {
-        run(i)
-      }
-    })
-
-    it('long signals control', function () {
-      this.timeout(500000)
-
-      const name = 'signals-long'
-      const sourceCode = readSource(name)
-
-      function run (calls) {
-        callNativeSpy.reset()
-        callNativeHandler = genCallNativeWrapper(calls)
-        app.$create(sourceCode)
-        app.$destroy()
-        expect(callNativeSpy.callCount).eql(calls + 2)
-      }
-
-      run(10)
-      run(30)
-      run(90)
-    })
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/throws/global-variable1.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/throws/global-variable1.source.js b/html5/test/case/throws/global-variable1.source.js
deleted file mode 100644
index b60fc47..0000000
--- a/html5/test/case/throws/global-variable1.source.js
+++ /dev/null
@@ -1,35 +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.
- */
-something = 'whatever'
-
-define('@weex-component/global-variable1', function (require, exports, module) {
-
-  module.exports.template = { "type": "container" }
-
-  module.exports.style = {}
-
-  module.exports = {
-    ready: function () {
-      console.log('I an ready')
-    }
-  }
-
-})
-
-bootstrap('@weex-component/global-variable1')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/throws/global-variable2.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/throws/global-variable2.source.js b/html5/test/case/throws/global-variable2.source.js
deleted file mode 100644
index b1b4f4e..0000000
--- a/html5/test/case/throws/global-variable2.source.js
+++ /dev/null
@@ -1,33 +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.
- */
-define('@weex-component/global-variable2', function (require, exports, module) {
-
-  module.exports.template = { "type": "container" }
-
-  module.exports.style = {}
-
-  module.exports = {
-    ready: function () {
-      abc = 'abc'
-    }
-  }
-
-})
-
-bootstrap('@weex-component/global-variable2')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/throws/global-variable3.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/throws/global-variable3.source.js b/html5/test/case/throws/global-variable3.source.js
deleted file mode 100644
index e010ca1..0000000
--- a/html5/test/case/throws/global-variable3.source.js
+++ /dev/null
@@ -1,40 +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.
- */
-define('@weex-component/global-variable3', function (require, exports, module) {
-
-  module.exports.template = { "type": "container" }
-
-  module.exports.style = {}
-
-  module.exports = {
-    data: function() {
-      return {
-        lists: ['A', 'B', 'C']
-      }
-    },
-    ready: function () {
-      for (i = 0; i < this.lists.length; ++i) {
-        console.log(this.lists[i])
-      }
-    }
-  }
-
-})
-
-bootstrap('@weex-component/global-variable3')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/api/methods.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/api/methods.js b/html5/test/unit/default/api/methods.js
deleted file mode 100644
index ec458ff..0000000
--- a/html5/test/unit/default/api/methods.js
+++ /dev/null
@@ -1,190 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import '../../../../shared/console'
-import * as modules from '../../../../frameworks/legacy/api/modules'
-import * as methods from '../../../../frameworks/legacy/api/methods'
-import Differ from '../../../../frameworks/legacy/app/differ'
-import { initModules, requireModule, clearModules, initMethods } from '../../../../frameworks/legacy/app/register'
-
-function Vm () {
-}
-
-describe('built-in methods', () => {
-  let vm
-  const requireSpy = sinon.spy()
-  const moduleSpy = sinon.spy()
-
-  before(() => {
-    clearModules()
-
-    initModules(modules)
-    initMethods(Vm, methods)
-
-    vm = new Vm()
-
-    Object.assign(vm, {
-      _app: {
-        options: {
-          debug: true,
-          bundleUrl: 'path_to_bundleUrl'
-        },
-        differ: new Differ(),
-        requireModule: (name) => {
-          requireSpy(name)
-          const module = requireModule(this, name)
-          const mockModule = {}
-          for (const moduleName in module) {
-            mockModule[moduleName] = function (...args) {
-              moduleSpy(...args)
-              if (typeof args[args.length - 1] === 'function') {
-                args[args.length - 1]()
-              }
-            }
-          }
-          return mockModule
-        }
-      },
-      _setStyle: function () {},
-      _ids: {
-        a: {
-          vm: {},
-          el: {
-            ref: '_root'
-          }
-        }
-      }
-    })
-
-    for (const apiName in vm) {
-      if (apiName.indexOf('$') === 0) {
-        vm[apiName] = sinon.spy(vm, apiName)
-      }
-    }
-    sinon.stub(console, 'warn')
-  })
-
-  beforeEach(() => {
-    requireSpy.reset()
-    moduleSpy.reset()
-    console.warn.reset()
-  })
-
-  after(() => {
-    clearModules()
-    console.warn.restore()
-  })
-
-  it('$', () => {
-    expect(vm.$('a')).to.deep.equal(vm._ids.a.vm)
-    expect(vm.$('invalid')).to.be.undefined
-    expect(console.warn.callCount).to.be.equal(2)
-  })
-
-  it('$el', () => {
-    expect(vm.$el('a')).to.deep.equal(vm._ids.a.el)
-    expect(vm.$el('invalid')).to.be.undefined
-  })
-
-  it('$vm', () => {
-    expect(vm.$vm('a')).to.deep.equal(vm._ids.a.vm)
-    expect(vm.$vm('invalid')).to.be.undefined
-  })
-
-  it('$renderThen', () => {
-    const fnSpy = sinon.spy()
-    vm.$renderThen(fnSpy)
-    vm._app.differ.flush()
-    expect(fnSpy.callCount).to.be.equal(1)
-  })
-
-  it('$scrollTo', () => {
-    vm.$scrollTo('a', 100)
-    expect(vm.$scrollTo('invalid', 100)).to.be.undefined
-    expect(requireSpy.firstCall.args[0]).to.be.equal('dom')
-    expect(moduleSpy.firstCall.args.length).to.be.equal(2)
-  })
-
-  it('$transition', () => {
-    const callback = sinon.spy()
-    vm.$transition('a', { styles: { color: '#FF0000' }}, callback)
-    expect(vm.$transition('invalid', {})).to.be.undefined
-    expect(requireSpy.firstCall.args[0]).eql('animation')
-    expect(moduleSpy.firstCall.args.length).eql(3)
-    expect(moduleSpy.firstCall.args[0]).eql('_root')
-    expect(moduleSpy.firstCall.args[1]).eql({
-      styles: { color: '#FF0000' }
-    })
-    expect(callback.callCount).eql(1)
-  })
-
-  it('$getConfig', () => {
-    const config = vm.$getConfig()
-    expect(config).eql({
-      debug: true,
-      bundleUrl: 'path_to_bundleUrl'
-    })
-
-    const configSpy = sinon.spy()
-    vm.$getConfig(configSpy)
-    expect(console.warn.callCount).to.be.equal(1)
-    expect(configSpy.args.length).eql(1)
-    expect(configSpy.args[0][0]).eql({
-      debug: true,
-      bundleUrl: 'path_to_bundleUrl'
-    })
-  })
-
-  it('$sendHttp', () => {
-    const callback = sinon.spy()
-    vm.$sendHttp({ a: 1 }, callback)
-    expect(requireSpy.firstCall.args[0]).eql('stream')
-    expect(moduleSpy.firstCall.args.length).eql(2)
-    expect(moduleSpy.firstCall.args).eql([{ a: 1 }, callback])
-    expect(callback.callCount).eql(1)
-  })
-
-  it('$openURL', () => {
-    vm.$openURL('url')
-    expect(requireSpy.firstCall.args[0]).eql('event')
-    expect(moduleSpy.firstCall.args.length).eql(1)
-    expect(moduleSpy.firstCall.args).eql(['url'])
-  })
-
-  it('$setTitle', () => {
-    vm.$setTitle('title')
-    expect(requireSpy.firstCall.args[0]).eql('pageInfo')
-    expect(moduleSpy.firstCall.args.length).eql(1)
-    expect(moduleSpy.firstCall.args).eql(['title'])
-  })
-
-  it('$call', () => {
-    vm.$call('event', 'openURL', 'url')
-    expect(vm.$call('invalid', 'module')).to.be.undefined
-    expect(vm.$call('event', 'invalid')).to.be.undefined
-    expect(requireSpy.firstCall.args[0]).eql('event')
-    expect(moduleSpy.firstCall.args.length).eql(1)
-    expect(moduleSpy.firstCall.args[0]).eql('url')
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/api/modules.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/api/modules.js b/html5/test/unit/default/api/modules.js
deleted file mode 100644
index 1adf66b..0000000
--- a/html5/test/unit/default/api/modules.js
+++ /dev/null
@@ -1,43 +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 chai from 'chai'
-const { expect } = chai
-
-import * as modules from '../../../../frameworks/legacy/api/modules'
-import { initModules, requireModule, clearModules } from '../../../../frameworks/legacy/app/register'
-
-describe('built-in modules', () => {
-  before(() => {
-    clearModules()
-  })
-
-  after(() => {
-    clearModules()
-  })
-
-  it('have keys', () => {
-    const app = {}
-    initModules(modules)
-    expect(requireModule(app, 'dom')).to.have.all.keys('scrollToElement')
-    expect(requireModule(app, 'stream')).to.have.all.keys('sendHttp')
-    expect(requireModule(app, 'event')).to.have.all.keys('openURL')
-    expect(requireModule(app, 'pageInfo')).to.have.all.keys('setTitle')
-    expect(requireModule(app, 'animation')).to.have.all.keys('transition')
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/app/bundle.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/app/bundle.js b/html5/test/unit/default/app/bundle.js
deleted file mode 100644
index f44b2f6..0000000
--- a/html5/test/unit/default/app/bundle.js
+++ /dev/null
@@ -1,409 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import * as bundle from '../../../../frameworks/legacy/app/bundle'
-import * as register from '../../../../frameworks/legacy/app/register'
-import { removeWeexPrefix } from '../../../../frameworks/legacy/util'
-import { Document } from '../../../../runtime/vdom'
-import Listener from '../../../../runtime/bridge/Listener'
-
-describe('parsing a bundle file', () => {
-  const componentTemplate = {
-    type: 'container',
-    children: [{
-      type: 'text',
-      attr: {
-        value: 'Hello World'
-      }
-    }]
-  }
-
-  before(() => {
-    sinon.stub(console, 'info')
-    sinon.stub(console, 'warn')
-    sinon.stub(console, 'error')
-    sinon.stub(console, 'debug')
-  })
-
-  after(() => {
-    console.info.restore()
-    console.warn.restore()
-    console.error.restore()
-    console.debug.restore()
-  })
-
-  describe('use define/bootstrap', () => {
-    let app
-    let callTasksSpy
-
-    before(() => {
-      const id = Date.now()
-      callTasksSpy = sinon.spy()
-
-      const doc = new Document(id, '', (tasks) => {
-        app.callTasks(tasks)
-      })
-
-      app = {
-        id, doc,
-        customComponentMap: {},
-        commonModules: {},
-        callbacks: {},
-        callTasks: callTasksSpy,
-        requireModule: function (name) {
-          return register.requireModule(this, name)
-        }
-      }
-    })
-
-    afterEach(() => {
-      callTasksSpy.reset()
-    })
-
-    describe('define', () => {
-      it('a weex component with factory and deps', () => {
-        bundle.defineFn(app, '@weex-component/a', (require, exports, module) => {
-          module.exports = {
-            template: componentTemplate
-          }
-        })
-        bundle.defineFn(app, '@weex-component/aa', [], (require, exports, module) => {
-          module.exports = {
-            template: componentTemplate
-          }
-        })
-
-        expect(app.customComponentMap['a'].template).to.deep.equal(componentTemplate)
-        expect(app.customComponentMap['aa'].template).to.deep.equal(componentTemplate)
-      })
-
-      it('a weex component with definition object and deps', () => {
-        bundle.defineFn(app, '@weex-component/aaa', {
-          template: componentTemplate
-        })
-        bundle.defineFn(app, '@weex-component/aaaa', [], {
-          template: componentTemplate
-        })
-
-        expect(app.customComponentMap['aaa'].template).to.deep.equal(componentTemplate)
-        expect(app.customComponentMap['aaaa'].template).to.deep.equal(componentTemplate)
-      })
-
-      it('a weex module', () => {
-        const methods = [{
-          name: 'createBody',
-          args: []
-        }]
-
-        bundle.defineFn(app, '@weex-module/dom', (require, exports, module) => {
-          module.exports = methods
-        })
-      })
-
-      it('a normal module', () => {
-        bundle.defineFn(app, './a', (require, exports, module) => {
-          exports.version = '0.1'
-        })
-      })
-
-      it('a npm module', () => {
-        bundle.defineFn(app, 'lib-httpurl', (require, exports, module) => {
-          exports.version = '0.2'
-        })
-      })
-
-      it('a CMD module', () => {
-        bundle.defineFn(app, 'kg/base', [], (require, exports, module) => {
-          exports.version = '0.3'
-        })
-      })
-    })
-
-    describe('require', () => {
-      it('a weex component', (done) => {
-        bundle.defineFn(app, '@weex-component/b', (require, exports, module) => {
-          const componentA = require('@weex-component/a')
-
-          expect(componentA.template).to.be.equal(componentTemplate)
-          done()
-        })
-      })
-
-      it('a weex module', (done) => {
-        // eslint-disable-next-line camelcase
-        const __weex_require__ = name => register.requireModule(app, removeWeexPrefix(name))
-        bundle.defineFn(app, '@weex-component/c', (require, exports, module) => {
-          const dom = require('@weex-module/dom')
-          expect(dom.createBody).to.be.a('function')
-          const dom2 = __weex_require__('dom')
-          expect(dom2.createBody).to.be.a('function')
-          const dom3 = __weex_require__('@weex-module/dom')
-          expect(dom3.createBody).to.be.a('function')
-          done()
-        })
-      })
-
-      it('a normal module', (done) => {
-        bundle.defineFn(app, '@weex-component/d', (require, exports, module) => {
-          const a = require('./a')
-
-          expect(a.version).to.be.equal('0.1')
-          done()
-        })
-      })
-
-      it('a npm module', (done) => {
-        bundle.defineFn(app, '@weex-component/e', (require, exports, module) => {
-          const HttpUrl = require('lib-httpurl')
-
-          expect(HttpUrl.version).to.be.equal('0.2')
-          done()
-        })
-      })
-
-      it('a CMD module', (done) => {
-        bundle.defineFn(app, 'kg/sample', ['kg/base'], (require, exports, module) => {
-          const base = require('kg/base')
-
-          expect(base.version).to.be.equal('0.3')
-          done()
-        })
-      })
-    })
-
-    describe('bootstrap', () => {
-      const ready = sinon.spy()
-
-      before(() => {
-        global.transformerVersion = '>=0.1 <1.0'
-        bundle.defineFn(app, '@weex-component/main', (require, exports, module) => {
-          module.exports = {
-            template: componentTemplate,
-            ready: ready
-          }
-        })
-      })
-
-      after(() => {
-        global.transformerVersion = undefined
-      })
-
-      it('not a weex component', () => {
-        const result = bundle.bootstrap(app, '@weex-module/dom')
-        expect(result).instanceof(Error)
-      })
-
-      it('a weex component', () => {
-        const result = bundle.bootstrap(
-          app,
-          '@weex-component/main',
-          { transformerVersion: '0.1.99' }
-        )
-
-        expect(result).not.instanceof(Error)
-        expect(callTasksSpy.calledTwice).to.be.true
-
-        expect(ready.calledOnce).to.be.true
-        const task1 = callTasksSpy.firstCall.args[0][0]
-        expect(task1.module).to.be.equal('dom')
-        expect(task1.method).to.be.equal('createBody')
-        expect(task1.args[0]).to.deep.equal({
-          type: 'container',
-          ref: '_root',
-          attr: {},
-          style: {}
-        })
-
-        const task2 = callTasksSpy.secondCall.args[0][0]
-        expect(task2.module).to.be.equal('dom')
-        expect(task2.method).to.be.equal('addElement')
-        expect(task2.args[1]).to.deep.equal({
-          type: 'text',
-          ref: app.doc.body.children[0].ref,
-          attr: {
-            value: 'Hello World'
-          },
-          style: {}
-        })
-        expect(task2.args[0]).to.be.equal('_root')
-        expect(task2.args[2]).to.be.equal(-1)
-      })
-
-      it('with a less wrong transformer version', () => {
-        const result = bundle.bootstrap(
-          app,
-          '@weex-component/main',
-          { transformerVersion: '0.0.1' }
-        )
-        expect(result).instanceof(Error)
-      })
-
-      it('with a bigger wrong transformer version', () => {
-        const result = bundle.bootstrap(
-          app,
-          '@weex-component/main',
-          { transformerVersion: '9.9.9' }
-        )
-        expect(result).instanceof(Error)
-      })
-
-      it('with viewport config', () => {
-        bundle.bootstrap(
-          app,
-          '@weex-component/undefined',
-          {
-            viewport: { width: 640 }
-          }
-        )
-        expect(callTasksSpy.callCount).to.be.equal(1)
-        const tasks = callTasksSpy.lastCall.args[0]
-        expect(tasks[0].module).to.be.equal('meta')
-        expect(tasks[0].method).to.be.equal('setViewport')
-        expect(tasks[0].args).to.deep.equal([{ width: 640 }])
-      })
-    })
-  })
-
-  describe('use regsiter/render(backward compatibility)', () => {
-    let app
-    let callTasksSpy
-    let readySpy
-    const template = {
-      type: 'container',
-      children: [{
-        type: 'text',
-        attr: {
-          value: 'Hello World'
-        }
-      }, {
-        type: 'custom'
-      }]
-    }
-
-    before(() => {
-      const id = Date.now()
-      callTasksSpy = sinon.spy()
-      readySpy = sinon.spy()
-
-      const doc = new Document(id, '', (tasks, callback) => {
-        app.callTasks(tasks, callback)
-      }, Listener)
-
-      app = {
-        id, doc,
-        customComponentMap: {},
-        callbacks: {},
-        callTasks: (tasks, callback) => {
-          callTasksSpy(tasks)
-          callback && callback()
-        },
-        requireModule: function (name) {
-          return register.requireModule(this, name)
-        }
-      }
-
-      Object.assign(app, bundle)
-    })
-
-    afterEach(() => {
-      callTasksSpy.reset()
-    })
-
-    describe('register', () => {
-      const readyfn = function () {
-        readySpy(this)
-      }
-
-      it('a component', () => {
-        bundle.register(app, 'custom', {
-          template: componentTemplate,
-          data: {
-            b: 'c'
-          },
-          methods: {
-            ready: readyfn
-          }
-        })
-
-        bundle.register(app, 'main', {
-          template: template,
-          data: {
-            a: 'b'
-          },
-          methods: {
-            ready: readyfn
-          }
-        })
-
-        expect(app.customComponentMap['custom'].template)
-          .to.deep.equal(componentTemplate)
-        expect(app.customComponentMap['main'].template)
-          .to.deep.equal(template)
-      })
-    })
-  })
-
-  describe('use define/require(backward compatibility)', () => {
-    let app
-    let callTasksSpy
-
-    before(() => {
-      const id = Date.now()
-      callTasksSpy = sinon.spy()
-
-      const doc = new Document(id, '', (tasks, callback) => {
-        app.callTasks(tasks, callback)
-      }, Listener)
-
-      app = {
-        id, doc,
-        customComponentMap: {},
-        callbacks: {},
-        callTasks: (tasks, callback) => {
-          callTasksSpy(tasks)
-          callback && callback()
-        },
-        requireModule: function (name) {
-          return register.requireModule(this, name)
-        }
-      }
-    })
-
-    afterEach(() => {
-      callTasksSpy.reset()
-    })
-
-    describe('define(old)', () => {
-      it('a component', () => {
-        bundle.defineFn(app, 'main', (require, exports, module) => {
-          module.exports = {
-            template: componentTemplate
-          }
-        })
-        expect(app.customComponentMap['main'].template)
-          .to.deep.equal(componentTemplate)
-      })
-    })
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/app/ctrl.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/app/ctrl.js b/html5/test/unit/default/app/ctrl.js
deleted file mode 100644
index 4e3163b..0000000
--- a/html5/test/unit/default/app/ctrl.js
+++ /dev/null
@@ -1,230 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import * as ctrl from '../../../../frameworks/legacy/app/ctrl'
-import Differ from '../../../../frameworks/legacy/app/differ'
-import { Document } from '../../../../runtime/vdom'
-
-describe('the api of app', () => {
-  let app
-  let spy1
-  let spy2
-
-  function createApp () {
-    const id = Date.now()
-
-    const app = {
-      id: id,
-      customComponentMap: {},
-      registerComponent: function () {},
-      // define: sinon.spy(),
-      // bootstrap: sinon.stub(),
-      vm: {},
-      differ: new Differ(id)
-    }
-
-    app.doc = new Document(id, '', spy1)
-    app.doc.createBody('div')
-
-    app.doc.taskCenter.callbackManager.add(spy2)
-    // app.bootstrap.returns()
-
-    return app
-  }
-
-  before(() => {
-    spy1 = sinon.spy()
-    spy2 = sinon.spy()
-    app = createApp()
-    !console.debug && (console.debug = function () {})
-  })
-
-  afterEach(() => {
-    spy1.reset()
-    // spy2.reset()
-  })
-
-  describe('init', () => {
-    before(() => {
-      global.transformerVersion = '0.1.3'
-    })
-
-    after(() => {
-      global.transformerVersion = undefined
-    })
-
-    it('a simple bundle', () => {
-      app.requireModule = () => {}
-      ctrl.init(app, `
-        define('main', function (r, e, m) {
-          e.template = {
-            "type": "container",
-            "children": [{
-              "type": "text",
-              "attr": {
-                "value": "Hello World"
-              }
-            }]
-          }
-        })
-
-        bootstrap('main')
-      `)
-
-      // expect(app.define.calledOnce).to.be.true
-      // expect(app.bootstrap.calledOnce).to.be.true
-
-      const task = spy1.lastCall.args[0][0]
-      expect(task.module).to.be.equal('dom')
-      expect(task.method).to.be.equal('createFinish')
-      expect(task.args.length).to.be.equal(0)
-    })
-  })
-
-  describe('getRootElement', () => {
-    it('from a simple', () => {
-      const json = ctrl.getRootElement(app)
-      expect(json.ref).eql('_root')
-      expect(json.type).eql('div')
-      expect(json.children.length).eql(1)
-    })
-    it('from empty object', () => {
-      expect(ctrl.getRootElement({})).to.deep.equal({})
-    })
-  })
-
-  describe('fireEvent', () => {
-    it('click on root', () => {
-      ctrl.fireEvent(app, '_root', 'click')
-      const task = spy1.lastCall.args[0][0]
-      expect(task.module).to.be.equal('dom')
-      expect(task.method).to.be.equal('updateFinish')
-      expect(task.args.length).to.be.equal(0)
-    })
-
-    it('error', () => {
-      const result = ctrl.fireEvent(app, '_rootTest', 'click')
-      expect(result).to.be.an.instanceof(Error)
-    })
-
-    it('click on both root & _rootTest', () => {
-      ctrl.fireEvent(app, ['_root', '_rootTest'], 'click')
-      const task = spy1.lastCall.args[0][0]
-      expect(task.module).to.be.equal('dom')
-      expect(task.method).to.be.equal('updateFinish')
-      expect(task.args.length).to.be.equal(0)
-    })
-  })
-
-  describe('callback', () => {
-    it('with a simple data', () => {
-      const data = { a: 'b' }
-      ctrl.callback(app, '1', data, true)
-      expect(spy2.calledOnce).to.be.true
-
-      const task = spy1.firstCall.args[0][0]
-      expect(task.module).to.be.equal('dom')
-      expect(task.method).to.be.equal('updateFinish')
-      expect(task.args.length).to.be.equal(0)
-    })
-
-    it('multiple called', () => {
-      const data = { a: 'b' }
-      ctrl.callback(app, '1', data, true)
-      expect(spy2.calledTwice).to.be.true
-
-      ctrl.callback(app, '1', data, false)
-      expect(spy2.calledThrice).to.be.true
-    })
-
-    it('error', () => {
-      const data = null
-      const result = ctrl.callback(app, '1', data, true)
-      expect(result).to.be.an.instanceof(Error)
-    })
-  })
-
-  describe('refreshData', () => {
-    it('a simple data', () => {
-      const data = { b: 'c' }
-      ctrl.refresh(app, data)
-      expect(app.vm.b).to.deep.equal(data.b)
-
-      const task = spy1.lastCall.args[0][0]
-      expect(task.module).to.be.equal('dom')
-      expect(task.method).to.be.equal('refreshFinish')
-      expect(task.args.length).to.be.equal(0)
-    })
-
-    it('call refresh with refreshData function', () => {
-      app.vm.refreshData = function () {
-        app.vm.data = 'hello'
-      }
-      const data = { b: 'c' }
-      ctrl.refresh(app, data)
-      expect(app.vm.data).to.equal('hello')
-    })
-
-    it('error', () => {
-      const data = null
-      const result = ctrl.refresh(app, data)
-      expect(result).to.be.an.instanceof(Error)
-    })
-  })
-
-  describe('destory', () => {
-    it('the simple data', () => {
-      ctrl.destroy(app)
-      expect(app.id).to.be.empty
-      expect(app.blocks).to.be.null
-      expect(app.vm).to.be.null
-      expect(app.doc).to.be.null
-      expect(app.customComponentMap).to.be.null
-    })
-    it('the incomplete data', () => {
-      const appx = createApp()
-      delete appx.vm
-      ctrl.destroy(appx)
-      expect(appx.id).to.be.empty
-      expect(appx.blocks).to.be.null
-      expect(appx.vm).to.be.null
-      expect(appx.doc).to.be.null
-      expect(appx.customComponentMap).to.be.null
-    })
-    it('clear vms', () => {
-      const appy = createApp()
-      appy.vm = {
-        $emit () {},
-        _watchers: [{ teardown () {} }],
-        _childrenVms: [{ $emit () {} }]
-      }
-      ctrl.destroy(appy)
-      expect(appy.id).to.be.empty
-      expect(appy.blocks).to.be.null
-      expect(appy.vm).to.be.null
-      expect(appy.doc).to.be.null
-      expect(appy.customComponentMap).to.be.null
-    })
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/app/downgrade.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/app/downgrade.js b/html5/test/unit/default/app/downgrade.js
deleted file mode 100644
index 562d0d7..0000000
--- a/html5/test/unit/default/app/downgrade.js
+++ /dev/null
@@ -1,265 +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 chai from 'chai'
-const { expect } = chai
-
-import * as Downgrade from '../../../../frameworks/legacy/app/downgrade'
-
-describe('downgrade', () => {
-  describe('normalizeVersion', () => {
-    it('should be normalize version format', () => {
-      const cases = [
-        ['', '0.0.0'],
-        ['1', '1.0.0'],
-        ['1.0', '1.0.0'],
-        ['1.0.0', '1.0.0'],
-        ['1.0.0.0', '1.0.0'],
-        ['1.3.4.1230', '1.3.4']
-      ]
-
-      cases.map(function (item, index) {
-        const input = item[0]
-        const should = item[1]
-        const test = Downgrade.normalizeVersion(input)
-
-        expect(test).to.be.equal(should)
-      })
-    })
-
-    it('invalid version format', () => {
-      expect(Downgrade.normalizeVersion([])).to.be.equal('0.0.0')
-      expect(Downgrade.normalizeVersion({})).to.be.equal('0.0.0')
-      expect(Downgrade.normalizeVersion(2.0)).to.be.equal('0.0.0')
-    })
-  })
-
-  describe('getError', () => {
-    it('should be get right error message', () => {
-      const cases = [
-        [1000, 'osName', 'iphone', 'iphone'],
-        [1001, 'osVersion', '1.0.0', '1.0.0'],
-        [1002, 'appVersion', '3.2.2', '>3.0.0'],
-        [1003, 'weexVersion', '1.2.3', '>=1.2.3'],
-        [1004, 'deviceModel', '1.0.0', ['1.0.0']]
-      ]
-
-      cases.map(function (item, index) {
-        const code = item[0]
-        const key = item[1]
-        const val = item[2]
-        const criteria = item[3]
-        const test = Downgrade.getError(key, val, criteria)
-
-        expect(test.code).to.be.equal(code)
-        expect(test.errorType).to.be.equal(1)
-        expect(test.isDowngrade).to.be.equal(true)
-      })
-    })
-  })
-
-  describe('check', () => {
-    it('should be match config', () => {
-      const deviceInfo = {
-        platform: 'ios',
-        osVersion: '9.2.1',
-        appVersion: '5.4.0',
-        weexVersion: '1.3.0',
-        deviceModel: 'iPhone6.2'
-      }
-
-      const cases = [
-        [{ ios: { osVersion: '>=9' }}, true],
-        [{ ios: { appVersion: '<10.0.0' }}, true],
-        [{ ios: { appVersion: '>10.0.0' }}, false],
-        [{ ios: { appVersion: '5.4.0' }}, true],
-        [{ ios: { deviceModel: 'ABC' }}, false],
-        [{ ios: { deviceModel: 'iPhone6.2' }}, true],
-        [{ ios: { deviceModel: ['iPhone6.2', 'ABC'] }}, true],
-        [{ ios: { osVersion: '>9', deviceModel: ['iPhone6.2', 'ABC'] }}, true],
-        [{ ios: { osVersion: '>=9', deviceModel: ['iPhone6.2', 'ABC'] }}, true],
-        [{ ios: { weexVersion: '<=9' }}, true]
-      ]
-
-      cases.map(function (item, index) {
-        const criteria = item[0]
-        const should = item[1]
-        const test = Downgrade.check(criteria, deviceInfo)
-
-        expect(test.isDowngrade).to.be.equal(should)
-      })
-    })
-
-    it('should be match config without app version', () => {
-      const deviceInfo = {
-        platform: 'iOS',
-        osVersion: '9.2',
-        weexVersion: '1.3.0',
-        deviceModel: 'iPhone6.2'
-      }
-
-      const cases = [
-        [{ ios: { osVersion: '>=9' }}, true],
-        [{ ios: { osVersion: '>8' }}, true],
-        [{ ios: { appVersion: '<10.0.0' }}, false],
-        [{ ios: { appVersion: '>10.0.0' }}, false],
-        [{ ios: { appVersion: '5.4.0' }}, false],
-        [{ ios: { deviceModel: 'ABC' }}, false],
-        [{ ios: { deviceModel: 'iPhone6.2' }}, true],
-        [{ ios: { deviceModel: ['MF353TA/A', 'ABC'] }}, false],
-        [{ ios: { osVersion: '>9', deviceModel: ['MF353TA/A', 'ABC'] }}, true],
-        [{ ios: { osVersion: '>=9', deviceModel: ['MF353TA/A', 'ABC'] }}, true],
-        [{ ios: { weexVersion: '<=9' }}, true],
-        [{ ios: { weexVersion: '>=1.3.0' }}, true],
-        [{ ios: { weexVersion: '>=1.3.0' }}, true]
-      ]
-
-      cases.map(function (item, index) {
-        const criteria = item[0]
-        const should = item[1]
-        const test = Downgrade.check(criteria, deviceInfo)
-
-        expect(test.isDowngrade).to.be.equal(should)
-      })
-    })
-
-    it('should be match config and deviceInfo from global var', () => {
-      global.WXEnvironment = {
-        platform: 'iOS',
-        osVersion: '9.2',
-        appVersion: '5.4.0',
-        weexVersion: '1.3.0',
-        deviceModel: 'iPhone6.2'
-      }
-
-      const cases = [
-        [{ ios: { osVersion: '>=9' }}, true],
-        [{ ios: { osVersion: '>8' }}, true],
-        [{ ios: { appVersion: '<10.0.0' }}, true],
-        [{ ios: { appVersion: '>10.0.0' }}, false],
-        [{ ios: { appVersion: '5.4.0' }}, true],
-        [{ ios: { deviceModel: 'ABC' }}, false],
-        [{ ios: { deviceModel: 'iPhone6.2' }}, true],
-        [{ ios: { deviceModel: ['MF353TA/A', 'ABC'] }}, false],
-        [{ ios: { osVersion: '>9', deviceModel: ['MF353TA/A', 'ABC'] }}, true],
-        [{ ios: { osVersion: '>=9', deviceModel: ['MF353TA/A', 'ABC'] }}, true],
-        [{ ios: { weexVersion: '<=9' }}, true],
-        [{ ios: { weexVersion: '>=1.3.0' }}, true],
-        [{ ios: { weexVersion: '>=1.3.0' }}, true]
-      ]
-
-      cases.map(function (item, index) {
-        const criteria = item[0]
-        const should = item[1]
-        const test = Downgrade.check(criteria)
-
-        expect(test.isDowngrade).to.be.equal(should)
-      })
-    })
-
-    it('should be using using custom check', () => {
-      const deviceInfo = {
-        platform: 'iOS',
-        osVersion: '9.2',
-        appVersion: '5.4.0',
-        weexVersion: '1.3.0',
-        deviceModel: 'iPhone6.2'
-      }
-
-      const cases = [
-        [
-          function () {
-
-          }, false
-        ],
-        [
-          function () {
-            return true
-          }, true
-        ],
-        [
-          function () {
-            return false
-          }, false
-        ],
-        [
-          function (deviceInfo) {
-            if (deviceInfo.platform === 'iOS') {
-              return true
-            }
-            return false
-          }, true
-        ],
-        [
-          function (deviceInfo, tools) {
-            if (deviceInfo.osVersion === '9.2') {
-              return true
-            }
-            return false
-          }, true
-        ],
-        [
-          function (deviceInfo, tools) {
-            if (tools.normalizeVersion(deviceInfo.osVersion) === '9.2.0') {
-              return true
-            }
-            return false
-          }, true
-        ],
-        [
-          function (deviceInfo, tools) {
-            return tools.semver.satisfies(tools.normalizeVersion(deviceInfo.osVersion), '9.2.0')
-          }, true
-        ],
-        [
-          function (deviceInfo, tools) {
-            return tools.semver.satisfies(deviceInfo.appVersion, '5.4.0')
-          }, true
-        ],
-        [
-          function (deviceInfo, tools) {
-            return tools.semver.satisfies(deviceInfo.weexVersion, '1.3.0')
-          }, true
-        ],
-        [
-          function (deviceInfo, tools) {
-            return deviceInfo.deviceModel === 'iPhone6.2'
-          }, true
-        ],
-        [
-          function (deviceInfo, tools) {
-            return deviceInfo.deviceModel === 'AndroidPad'
-          }, false
-        ],
-        [
-          function (deviceInfo, tools) {
-            return ['iPhone6.2', 'iPhone7.1'].indexOf(deviceInfo.deviceModel) >= 0
-          }, true
-        ]
-      ]
-
-      cases.map(function (item, index) {
-        const criteria = item[0]
-        const should = item[1]
-        const test = Downgrade.check(criteria, deviceInfo)
-
-        expect(test.isDowngrade).to.be.equal(should)
-      })
-    })
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/app/index.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/app/index.js b/html5/test/unit/default/app/index.js
deleted file mode 100644
index e0e2c3f..0000000
--- a/html5/test/unit/default/app/index.js
+++ /dev/null
@@ -1,147 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import App from '../../../../frameworks/legacy/app'
-import { Element, Document } from '../../../../runtime/vdom'
-
-describe('App Instance', () => {
-  const oriDocumentHandler = Document.handler
-  const sendTasksSpy = sinon.spy()
-  let app
-
-  beforeEach(() => {
-    Document.handler = sendTasksSpy
-    const id = Date.now() + ''
-    app = new App(id, {})
-  })
-
-  afterEach(() => {
-    Document.handler = oriDocumentHandler
-  })
-
-  describe('normal check', () => {
-    it('is a class', () => {
-      expect(App).to.be.an('function')
-    })
-
-    it('being created', () => {
-      expect(app).to.be.an('object')
-      expect(app).to.be.instanceof(App)
-    })
-
-    it('with some apis', () => {
-      expect(app.requireModule).a.function
-      expect(app.updateActions).a.function
-      expect(app.callTasks).a.function
-    })
-
-    it('run apis', () => {
-      expect(app.requireModule('stream')).to.deep.equal({})
-      expect(app.updateActions()).to.be.undefined
-      expect(app.callTasks([])).to.be.undefined
-    })
-  })
-
-  describe('call tasks', () => {
-    it('with no args & callback', () => {
-      const tasks = [{
-        module: 'dom',
-        method: 'createBody',
-        args: []
-      }]
-
-      app.callTasks(tasks)
-      expect(sendTasksSpy.lastCall.args[1]).to.deep.equal(tasks)
-    })
-
-    it('with callback', (done) => {
-      const tasks = [{
-        module: 'dom',
-        method: 'createBody',
-        args: []
-      }]
-
-      app.callTasks(tasks)
-      expect(sendTasksSpy.lastCall.args[1]).to.deep.equal(tasks)
-      done()
-    })
-
-    it('with function arg', (done) => {
-      const callbackId = '1'
-
-      const tasks = [{
-        module: 'dom',
-        method: 'createBody',
-        args: [() => {}]
-      }]
-
-      app.callTasks(tasks)
-      expect(sendTasksSpy.lastCall.args[1]).to.deep.equal([{
-        module: 'dom',
-        method: 'createBody',
-        args: [callbackId]
-      }])
-      done()
-    })
-
-    it('with node arg', (done) => {
-      const node = new Element()
-      node.ref = '1'
-
-      const tasks = [{
-        module: 'dom',
-        method: 'createBody',
-        args: [node]
-      }]
-
-      app.callTasks(tasks)
-      expect(sendTasksSpy.lastCall.args[1]).to.deep.equal([{
-        module: 'dom',
-        method: 'createBody',
-        args: [node.ref]
-      }])
-      done()
-    })
-
-    it('with callback after close', (done) => {
-      const callbackId = '1'
-
-      const tasks = [{
-        module: 'dom',
-        method: 'createBody',
-        args: [() => {}]
-      }]
-
-      app.doc.close()
-
-      app.callTasks(tasks)
-      expect(sendTasksSpy.lastCall.args[1]).to.deep.equal([{
-        module: 'dom',
-        method: 'createBody',
-        args: [callbackId]
-      }])
-      done()
-    })
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/app/register.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/app/register.js b/html5/test/unit/default/app/register.js
deleted file mode 100644
index 6c23982..0000000
--- a/html5/test/unit/default/app/register.js
+++ /dev/null
@@ -1,200 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import {
-  getModule,
-  clearModules,
-  initModules,
-  initMethods,
-  requireModule,
-  requireCustomComponent,
-  registerCustomComponent
-} from '../../../../frameworks/legacy/app/register'
-
-function Ctx () {
-  this.customComponentMap = {}
-  this.callTasks = sinon.spy()
-}
-
-describe('register', () => {
-  let ctx
-
-  before(() => {
-    clearModules()
-    ctx = new Ctx()
-  })
-
-  after(() => {
-    clearModules()
-  })
-
-  describe('component', () => {
-    it('with exports', () => {
-      const def = {
-        a: 'b'
-      }
-      registerCustomComponent(ctx, 'componentA', def)
-      expect(requireCustomComponent(ctx, 'componentA')).to.deep.equal(def)
-    })
-
-    it('with a existing name', () => {
-      const def = {
-        a: 'b'
-      }
-      sinon.stub(console, 'error')
-      registerCustomComponent(ctx, 'componentA', def)
-      expect(console.error).callCount(1)
-      console.error.restore()
-    })
-  })
-
-  describe('module', () => {
-    it('with a old format', () => {
-      initModules({
-        dom: [
-          'createBody',
-          'addElement'
-        ]
-      })
-      expect(requireModule(ctx, 'dom')).to.have.any.keys('createBody', 'addElement')
-    })
-
-    it('with a new format', () => {
-      initModules({
-        dom: [
-          {
-            name: 'moveElement',
-            args: ['string', 'string']
-          }
-        ],
-        stream: [
-          {
-            name: 'sendMtop',
-            args: ['object', 'function']
-          }
-        ]
-      })
-      expect(requireModule(ctx, 'dom')).to.have.all.keys('createBody', 'addElement', 'moveElement')
-      expect(requireModule(ctx, 'stream')).to.have.all.keys('sendMtop')
-    })
-
-    it('with a existed module.method', () => {
-      initModules({
-        dom: [
-          {
-            name: 'moveElement',
-            args: ['string', 'string', 'string']
-          }
-        ]
-      }, true)
-      initModules({
-        stream: [
-          {
-            name: 'sendMtop',
-            args: ['object']
-          }
-        ]
-      })
-      expect(getModule('dom').moveElement).to.deep.equal({
-        name: 'moveElement',
-        args: ['string', 'string', 'string']
-      })
-
-      expect(getModule('stream').sendMtop).to.deep.equal({
-        name: 'sendMtop',
-        args: ['object', 'function']
-      })
-    })
-
-    it('run registered module', () => {
-      initModules({
-        event: [{
-          name: 'openURL',
-          args: ['string']
-        }]
-      })
-      const event = requireModule(ctx, 'event')
-      expect(event).to.have.keys('openURL')
-
-      event.openURL('http://test.com')
-      expect(ctx.callTasks.callCount).to.be.equal(1)
-    })
-  })
-
-  describe('api', () => {
-    it('a common api', () => {
-      initMethods(Ctx, {
-        $test1: function () {
-          return {
-            ctx: this,
-            value: 'test1'
-          }
-        },
-        $test2: function () {
-          return {
-            ctx: this,
-            value: 'test2'
-          }
-        }
-      })
-
-      expect(ctx.$test1).to.be.a('Function')
-      expect(ctx.$test1()).to.deep.equal({
-        ctx: ctx,
-        value: 'test1'
-      })
-      expect(ctx.$test2).to.be.an('Function')
-      expect(ctx.$test2()).to.deep.equal({
-        ctx: ctx,
-        value: 'test2'
-      })
-    })
-
-    it('override api', () => {
-      initMethods(Ctx, {
-        $override: function () {
-          return {
-            ctx: this,
-            value: 'first'
-          }
-        }
-      })
-
-      initMethods(Ctx, {
-        $override: function () {
-          return {
-            ctx: this,
-            value: 'ignored'
-          }
-        }
-      })
-
-      expect(ctx.$override).to.be.a('Function')
-      expect(ctx.$override()).to.deep.equal({
-        ctx: ctx,
-        value: 'first'
-      })
-    })
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/app/viewport.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/app/viewport.js b/html5/test/unit/default/app/viewport.js
deleted file mode 100644
index 0b0d122..0000000
--- a/html5/test/unit/default/app/viewport.js
+++ /dev/null
@@ -1,79 +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 chai from 'chai'
-import sinon from 'sinon'
-import sinonChai from 'sinon-chai'
-const { expect } = chai
-chai.use(sinonChai)
-
-import * as viewport from '../../../../frameworks/legacy/app/viewport'
-
-describe('viewport', function () {
-  const originalCallNative = global.callNative
-  const { setViewport, validateViewport } = viewport
-  const mockApp = {
-    id: 'mock',
-    callTasks (...args) {
-      global.callNative(...args)
-    }
-  }
-
-  before(() => {
-    sinon.stub(console, 'warn')
-  })
-
-  beforeEach(() => {
-    global.callNative = sinon.spy()
-  })
-  afterEach(() => {
-    global.callNative = originalCallNative
-    console.warn.reset()
-  })
-
-  it('invalid setViewport', () => {
-    setViewport()
-    expect(global.callNative.callCount).to.be.equal(0)
-    setViewport({})
-    expect(global.callNative.callCount).to.be.equal(0)
-  })
-
-  it('setViewport', () => {
-    setViewport(mockApp, {})
-    expect(global.callNative.callCount).to.be.equal(1)
-    setViewport(mockApp, { width: 640 })
-    expect(global.callNative.callCount).to.be.equal(2)
-    setViewport(mockApp, { width: 'device-width' })
-    expect(global.callNative.callCount).to.be.equal(3)
-  })
-
-  it('validateViewport', () => {
-    expect(validateViewport()).to.be.false
-    expect(console.warn.callCount).to.be.equal(1)
-    expect(validateViewport({})).to.be.false
-    expect(console.warn.callCount).to.be.equal(2)
-
-    expect(validateViewport({ width: 200 })).to.be.true
-    expect(console.warn.callCount).to.be.equal(2)
-    expect(validateViewport({ width: 'device-width' })).to.be.true
-    expect(console.warn.callCount).to.be.equal(2)
-
-    expect(validateViewport({ width: 'initial-width' })).to.be.false
-    expect(console.warn.callCount).to.be.equal(3)
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/util/index.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/util/index.js b/html5/test/unit/default/util/index.js
deleted file mode 100644
index a33be27..0000000
--- a/html5/test/unit/default/util/index.js
+++ /dev/null
@@ -1,185 +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 chai from 'chai'
-const { expect } = chai
-
-import * as util from '../../../../frameworks/legacy/util'
-
-describe('Util', () => {
-  describe('isReserved', () => {
-    it('should be first char is $ or _', () => {
-      expect(util.isReserved('$isReserved')).eql(true)
-      expect(util.isReserved('_isReserved')).eql(true)
-      expect(util.isReserved('isReserved$')).eql(false)
-      expect(util.isReserved('is$Reserved')).eql(false)
-      expect(util.isReserved('isReserved')).eql(false)
-    })
-  })
-
-  describe('camelize', () => {
-    it('should be camelize a hyphen-delmited string', () => {
-      expect(util.camelize('mini-camelize')).eql('miniCamelize')
-      expect(util.camelize('mini-camelize-test')).eql('miniCamelizeTest')
-    })
-  })
-
-  describe('toArray', () => {
-    it('should be convert an Array-like object to a real Array', () => {
-      const mockup = {
-        '0': 1,
-        '1': 2,
-        '2': 3,
-        length: 3
-      }
-
-      expect(util.toArray(mockup)).eql([1, 2, 3])
-      expect(util.toArray(mockup, 2)).eql([3])
-    })
-  })
-
-  describe('extend', () => {
-    it('should be extend object', () => {
-      const mockup = {
-        a: 1,
-        b: 2
-      }
-
-      expect(util.extend(mockup, { b: 4, c: 5 })).eql({
-        a: 1,
-        b: 4,
-        c: 5
-      })
-
-      expect(util.extend(mockup, { c: 5 })).eql({
-        a: 1,
-        b: 4,
-        c: 5
-      })
-
-      expect(util.extend(mockup, { c: 5 }, { d: 6 }, { e: 7 })).eql({
-        a: 1,
-        b: 4,
-        c: 5,
-        d: 6,
-        e: 7
-      })
-    })
-
-    it('polyfill Object.assign', () => {
-      const _assign = Object.assign
-      Object.assign = undefined
-
-      const mockup = {
-        a: 1,
-        b: 2
-      }
-
-      expect(util.extend(mockup, { b: 4, c: 5 })).eql({
-        a: 1,
-        b: 4,
-        c: 5
-      })
-
-      expect(util.extend(mockup, { c: 5 })).eql({
-        a: 1,
-        b: 4,
-        c: 5
-      })
-
-      expect(util.extend(mockup, { c: 5 }, { d: 6 }, { e: 7 })).eql({
-        a: 1,
-        b: 4,
-        c: 5,
-        d: 6,
-        e: 7
-      })
-
-      Object.assign = _assign
-    })
-  })
-
-  describe('isObject', () => {
-    it('should be an array or object', () => {
-      expect(util.isObject({})).eql(true)
-      expect(util.isObject([])).eql(true)
-      expect(util.isObject('a')).eql(false)
-      expect(util.isObject(1)).eql(false)
-      expect(util.isObject(true)).eql(false)
-      expect(util.isObject(null)).eql(false)
-      expect(util.isObject(undefined)).eql(false)
-      expect(util.isObject(function () {})).eql(false)
-      expect(util.isObject(/\w*/)).eql(true) // regexp is object
-      expect(util.isObject(new Date())).eql(true)
-    })
-  })
-
-  describe('isPlainObject', () => {
-    it('should be an real object', () => {
-      expect(util.isPlainObject({})).eql(true)
-      expect(util.isPlainObject([])).eql(false)
-      expect(util.isPlainObject('a')).eql(false)
-      expect(util.isPlainObject(1)).eql(false)
-      expect(util.isPlainObject(true)).eql(false)
-      expect(util.isPlainObject(null)).eql(false)
-      expect(util.isPlainObject(undefined)).eql(false)
-      expect(util.isPlainObject(function () {})).eql(false)
-      expect(util.isPlainObject(/\w*/)).eql(false)
-      expect(util.isPlainObject(new Date())).eql(false)
-    })
-  })
-
-  describe('typof', () => {
-    it('should be real type', () => {
-      expect(util.typof({})).eql('object')
-      expect(util.typof([])).eql('array')
-      expect(util.typof('a')).eql('string')
-      expect(util.typof(1)).eql('number')
-      expect(util.typof(true)).eql('boolean')
-      expect(util.typof(null)).eql('null')
-      expect(util.typof(undefined)).eql('undefined')
-      expect(util.typof(function () {})).eql('function')
-      expect(util.typof(/\w*/)).eql('regexp')
-      expect(util.typof(new Date())).eql('date')
-    })
-  })
-
-  describe('def', () => {
-    it('should be define a non-enumerable property', () => {
-      const obj = {}
-      util.def(obj, 'a', 1, true)
-      expect(obj).eql({ a: 1 })
-
-      util.def(obj, 'b', 1, false)
-      expect(obj).eql({ a: 1 })
-
-      util.def(obj, 'c', 1, true)
-      expect(obj).eql({ a: 1, c: 1 })
-
-      util.def(obj, 'd', 1)
-      expect(obj).eql({ a: 1, c: 1 })
-    })
-  })
-
-  describe('hyphenate', () => {
-    it('should hyphenate a camelCase string', () => {
-      const str = 'helloWorldWeex'
-      expect(util.hyphenate(str)).eql('hello-world-weex')
-    })
-  })
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/unit/default/util/shared.js
----------------------------------------------------------------------
diff --git a/html5/test/unit/default/util/shared.js b/html5/test/unit/default/util/shared.js
deleted file mode 100644
index bf24684..0000000
--- a/html5/test/unit/default/util/shared.js
+++ /dev/null
@@ -1,143 +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 { expect } from 'chai'
-
-import * as utils from '../../../../frameworks/legacy/util/shared'
-
-describe('test util functions', () => {
-  it('extend', () => {
-    expect(typeof utils.extend).to.be.equal('function')
-
-    const a = { name: 'A', a: true, c: true }
-    const b = { name: 'B', b: true }
-    utils.extend(a, b, { c: false })
-    expect(a).to.be.deep.equal({ name: 'B', a: true, b: true, c: false })
-    expect(b).to.be.deep.equal({ name: 'B', b: true })
-  })
-
-  it('def', () => {
-    expect(typeof utils.def).to.be.equal('function')
-    const obj = { type: 'none' }
-    utils.def(obj, 'count', 42, false)
-
-    expect(obj.hasOwnProperty('count')).to.be.true
-    expect(obj.propertyIsEnumerable('count')).to.be.false
-    expect(obj.type).to.be.equal('none')
-    expect(obj.count).to.be.equal(42)
-    obj.count = 24
-    expect(obj.count).to.be.equal(24)
-  })
-
-  it('remove', () => {
-    expect(typeof utils.remove).to.be.equal('function')
-
-    const arr = ['first', null, 42, null, 28]
-    const obj = { 0: 'first', 1: null, 2: 42 }
-
-    utils.remove(arr, null)
-    expect(arr).to.deep.equal(['first', 42, null, 28])
-    utils.remove(arr, null)
-    expect(arr).to.deep.equal(['first', 42, 28])
-    utils.remove(arr, null)
-    expect(arr).to.deep.equal(['first', 42, 28])
-    utils.remove(arr, 42)
-    expect(arr).to.deep.equal(['first', 28])
-
-    utils.remove(obj, 42)
-    expect(obj).to.deep.equal(obj)
-  })
-
-  it('hasOwn', () => {
-    expect(typeof utils.hasOwn).to.be.equal('function')
-
-    function Point () {
-      this.x = 0
-    }
-    Point.prototype.y = 1
-
-    const p = new Point()
-    expect(p.x).to.be.equal(0)
-    expect(p.y).to.be.equal(1)
-
-    expect(utils.hasOwn(p, 'x')).to.be.true
-    expect(utils.hasOwn(p, 'y')).to.be.false
-  })
-
-  it('bind', () => {
-    expect(typeof utils.bind).to.be.equal('function')
-
-    function who () {
-      return this.name
-    }
-    const obj = { name: 'fixed' }
-    const ctx = { name: 'changed' }
-    const askWho = utils.bind(who, obj)
-
-    expect(who()).to.be.undefined
-    expect(askWho()).to.be.equal('fixed')
-    expect(askWho.call(ctx)).to.be.equal('fixed')
-    expect(askWho.apply(ctx)).to.be.equal('fixed')
-    expect(who.apply(ctx)).to.be.equal('changed')
-  })
-
-  it('toArray', () => {
-    expect(typeof utils.toArray).to.be.equal('function')
-    const obj = { length: 2 }
-    const arr = utils.toArray(obj)
-
-    expect(arr).to.deep.equal([undefined, undefined])
-    expect(arr.hasOwnProperty('0')).to.be.true
-    expect(arr.hasOwnProperty('1')).to.be.true
-    expect(arr.hasOwnProperty('2')).to.be.false
-
-    const arrayLike = {
-      0: 'empty',
-      1: 'first',
-      2: 'second',
-      length: 3
-    }
-    expect(utils.toArray(arrayLike, 1)).to.deep.equal(['first', 'second'])
-  })
-
-  it('isObject', () => {
-    expect(typeof utils.isObject).to.be.equal('function')
-    expect(utils.isObject({})).to.be.true
-    expect(utils.isObject([])).to.be.true
-    expect(utils.isObject(new Date())).to.be.true
-    expect(utils.isObject()).to.be.false
-    expect(utils.isObject('')).to.be.false
-    expect(utils.isObject(42)).to.be.false
-    expect(utils.isObject(NaN)).to.be.false
-    expect(utils.isObject(null)).to.be.false
-    expect(utils.isObject(undefined)).to.be.false
-  })
-
-  it('isPlainObject', () => {
-    expect(typeof utils.isPlainObject).to.be.equal('function')
-    expect(utils.isPlainObject({})).to.be.true
-    expect(utils.isPlainObject([])).to.be.false
-    expect(utils.isPlainObject(new Date())).to.be.false
-    expect(utils.isPlainObject()).to.be.false
-    expect(utils.isPlainObject('')).to.be.false
-    expect(utils.isPlainObject(42)).to.be.false
-    expect(utils.isPlainObject(NaN)).to.be.false
-    expect(utils.isPlainObject(null)).to.be.false
-    expect(utils.isPlainObject(undefined)).to.be.false
-  })
-})


[15/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/if-repeat-refresh.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/if-repeat-refresh.source.js b/html5/test/case/complex/if-repeat-refresh.source.js
deleted file mode 100644
index aa62931..0000000
--- a/html5/test/case/complex/if-repeat-refresh.source.js
+++ /dev/null
@@ -1,60 +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.
- */
-define('@weex-component/if-repeat', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      titlelist: [
-        {showTitle: true, title: 'Hello World1'},
-        {showTitle: false, title: 'Hello World2'}
-      ]
-    }}
-  }
-
-
-;module.exports.style = {
-  "title": {
-    "fontSize": 26,
-    "color": "#FF0000"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "repeat": function () {return this.titlelist},
-      "shown": function () {return this.showTitle},
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": function () {return this.title}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/if-repeat')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/inline-click.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/inline-click.output.js b/html5/test/case/complex/inline-click.output.js
deleted file mode 100644
index bf3208e..0000000
--- a/html5/test/case/complex/inline-click.output.js
+++ /dev/null
@@ -1,14 +0,0 @@
-{
- "type": "div",
- "children": [
-  {
-   "type": "text",
-   "attr": {
-    "value": "Hello World1"
-   },
-   "event": [
-    "click"
-   ]
-  }
- ]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/inline-click.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/inline-click.source.js b/html5/test/case/complex/inline-click.source.js
deleted file mode 100644
index e447c9e..0000000
--- a/html5/test/case/complex/inline-click.source.js
+++ /dev/null
@@ -1,54 +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.
- */
-define('@weex-component/test', function (require, exports, module) {
-
-;
-module.exports = {
-    data: function () {return {
-        text: 'Hello World1'
-    }},
-    methods: {
-        clicked: function(text, e) {
-            this.text = text
-        }
-    }
-}
-
-
-;module.exports.style = {}
-
-;module.exports.template = {
-  "type": "div",
-  "children": [
-    {
-      "type": "text",
-      "events": {
-        "click": function ($event) {this.clicked('Hello World2', $event)}
-      },
-      "attr": {
-        "value": function () {return this.text}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-bootstrap('@weex-component/test', {"transformerVersion":"0.3.0"})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/input-binding.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/input-binding.output.js b/html5/test/case/complex/input-binding.output.js
deleted file mode 100644
index 30b9957..0000000
--- a/html5/test/case/complex/input-binding.output.js
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "children": [
-    {
-      "attr": {
-        "value": "abcde"
-      },
-      "event": ["change"],
-      "type": "input"
-    }
-  ],
-  "type": "div"
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/input-binding.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/input-binding.source.js b/html5/test/case/complex/input-binding.source.js
deleted file mode 100644
index ae96760..0000000
--- a/html5/test/case/complex/input-binding.source.js
+++ /dev/null
@@ -1,78 +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.
- */
-define('@weex-component/d6b20355da35c77d07f0a11ec5256f42', function(require, exports, module) {
-
-    ;
-    module.exports = {
-        data: function() {
-            return {
-                x: 'abcde',
-                changes: []
-            }
-        },
-        methods: {
-            change: function(e) {
-                var value = e.target.attr.value
-                this.x = value
-                this.changes.push(value)
-            }
-        }
-    }
-
-
-    ;
-    module.exports.style = {}
-
-    ;
-    module.exports.template = {
-        "type": "div",
-        "children": [{
-            "type": "input",
-            "attr": {
-                "value": function() {
-                    return this.x
-                }
-            },
-            "events": {
-                "change": "change"
-            }
-        }, {
-            "type": "text",
-            "repeat": {
-                "expression": function() {
-                    return this.changes
-                },
-                "key": "k",
-                "value": "v"
-            },
-            "attr": {
-                "value": function() {
-                    return (this.k + 1) + ' - ' + (this.v)
-                }
-            }
-        }]
-    }
-
-    ;
-})
-
-// require module
-bootstrap('@weex-component/d6b20355da35c77d07f0a11ec5256f42', {
-    "transformerVersion": "0.3.1"
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/refresh2.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/refresh2.output.js b/html5/test/case/complex/refresh2.output.js
deleted file mode 100644
index 823fda9..0000000
--- a/html5/test/case/complex/refresh2.output.js
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-    "type": "container",
-    "children": [{
-        "type": "container",
-        "children": [{
-            "type": "text",
-            "attr": {
-                "value": "bar1"
-            }
-        }, {
-            "type": "container",
-            "children": [{
-                "type": "text",
-                "attr": {
-                    "value": "bar2-refresh"
-                }
-            }]
-        }]
-    }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/refresh2.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/refresh2.source.js b/html5/test/case/complex/refresh2.source.js
deleted file mode 100644
index 6fa770c..0000000
--- a/html5/test/case/complex/refresh2.source.js
+++ /dev/null
@@ -1,133 +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.
- */
-
-define('@weex-component/bar1', function(require, exports, module) {;;
-  module.exports.template = {
-    type: 'container',
-    children: [{
-      type: 'text',
-      attr: {
-        value: 'bar1'
-      }
-    }, {
-      type: 'bar2',
-      id: 'bar2',
-      component: true
-    }]
-  }
-
-  ;
-  module.exports.methods = {
-    ready: function(isRefresh) {
-      if (isRefresh) {
-        this.$vm('bar2').ready(true)
-      }
-    }
-  }
-
-})
-
-define('@weex-component/bar2', function(require, exports, module) {;;
-  module.exports.template = {
-    type: 'container',
-    children: [{
-      type: 'text',
-      attr: {
-        value: function() {
-          return this.text
-        }
-      }
-    }]
-  }
-
-  ;
-  module.exports.data = function() {
-    return {
-      text: 'bar2'
-    }
-  }
-
-  ;
-  module.exports.methods = {
-    ready: function(isRefresh) {
-      if (isRefresh) {
-        this.text = 'bar2-refresh'
-      }
-    }
-  }
-
-
-})
-
-define('@weex-component/foo', function(require, exports, module) {
-
-  ;
-  module.exports.style = {}
-
-  ;
-  module.exports.template = {
-    type: 'container',
-    children: [{
-      type: 'bar1',
-      shown: function() {
-        return this.showbar1
-      },
-      id: 'bar1',
-      component: true
-    }]
-  }
-
-  ;
-  module.exports.data = function() {
-    return {
-      showbar1: false,
-      ext: {
-        showbar1: false
-      }
-    }
-  }
-
-  ;module.exports.methods = {
-    refreshData: function(data) {
-      for (var key in data) {
-        this[key] = data[key]
-      }
-      this.ready(true)
-    },
-    ready: function(isRefresh) {
-      var self = this
-      this.showbar1 = this.ext.showbar1
-
-      if (isRefresh) {
-        if (this.showbar1) {
-          this.$renderThen(() => {
-            this.$vm('bar1').$on('hook:ready', function() {
-              self.$vm('bar1').ready(true)
-            })
-          })
-        }
-      }
-    }
-  }
-
-})
-
-// require module
-
-bootstrap('@weex-component/foo')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/repeat-track-by.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/repeat-track-by.output.js b/html5/test/case/complex/repeat-track-by.output.js
deleted file mode 100644
index 7e64c10..0000000
--- a/html5/test/case/complex/repeat-track-by.output.js
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      trackBy: 'text',
-      value: '0: Hello World2'
-    }
-  }, {
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      trackBy: 'text',
-      value: '1: Hello World1'
-    }
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/repeat-track-by.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/repeat-track-by.source.js b/html5/test/case/complex/repeat-track-by.source.js
deleted file mode 100644
index 127acb0..0000000
--- a/html5/test/case/complex/repeat-track-by.source.js
+++ /dev/null
@@ -1,64 +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.
- */
-define('@weex-component/repeat-track-by', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      titlelist: [
-        {text: 'Hello World1'},
-        {text: 'Hello World2'}
-      ]
-    }}
-  }
-
-
-;module.exports.style = {
-  "title": {
-    "fontSize": 26,
-    "color": "#FF0000"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "repeat": {
-        expression: function() { return this.titlelist },
-        key: 'index',
-        value: 'title'
-      },
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "trackBy": "text",
-        "value": function () {return this.index + ': ' + this.title.text}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/repeat-track-by')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/transformer2.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/transformer2.output.js b/html5/test/case/complex/transformer2.output.js
deleted file mode 100644
index 1372f91..0000000
--- a/html5/test/case/complex/transformer2.output.js
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    type: 'container'
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/transformer2.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/transformer2.source.js b/html5/test/case/complex/transformer2.source.js
deleted file mode 100644
index af31348..0000000
--- a/html5/test/case/complex/transformer2.source.js
+++ /dev/null
@@ -1,46 +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.
- */
-define('@weex-component/foo', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {
-      return {
-        x: 1
-      }
-    }
-  }
-
-
-;module.exports.style = {
-  "a": {
-    "fontSize": 12
-  }
-}
-
-;module.exports.template = {
-  "type": "container"
-}
-
-;})
-
-
-bootstrap('@weex-component/foo', {
-  'transformerVersion': '0.0.1'
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/transformer3.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/transformer3.output.js b/html5/test/case/complex/transformer3.output.js
deleted file mode 100644
index 1372f91..0000000
--- a/html5/test/case/complex/transformer3.output.js
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    type: 'container'
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/transformer3.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/transformer3.source.js b/html5/test/case/complex/transformer3.source.js
deleted file mode 100644
index 2ca15a3..0000000
--- a/html5/test/case/complex/transformer3.source.js
+++ /dev/null
@@ -1,46 +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.
- */
-define('@weex-component/foo', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {
-      return {
-        x: 1
-      }
-    }
-  }
-
-
-;module.exports.style = {
-  "a": {
-    "fontSize": 12
-  }
-}
-
-;module.exports.template = {
-  "type": "container"
-}
-
-;})
-
-
-bootstrap('@weex-component/foo', {
-  'transformerVersion': '9.9.9'
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/multi/clear-dep-target-error.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/multi/clear-dep-target-error.source.js b/html5/test/case/multi/clear-dep-target-error.source.js
deleted file mode 100644
index 0001d1c..0000000
--- a/html5/test/case/multi/clear-dep-target-error.source.js
+++ /dev/null
@@ -1,45 +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.
- */
-
-define('@weex-component/DepTargetError', function(require, exports, module) {
-
-  ;module.exports = {
-    data: function () {return {
-      a: {}
-    }}
-  }
-
-
-  ;module.exports.style = {}
-
-  ;module.exports.template = {
-	  "type": "div",
-	  "children": [
-	    {
-	      "type": "text",
-	      "attr": {
-	        "value": function () { return this.a.x.y }
-	      }
-	    }
-	  ]
-  }
-
-})
-
-bootstrap('@weex-component/DepTargetError', { "transformerVersion": "0.3.1" })

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/multi/clear-dep-target-fine.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/multi/clear-dep-target-fine.output.js b/html5/test/case/multi/clear-dep-target-fine.output.js
deleted file mode 100644
index e129423..0000000
--- a/html5/test/case/multi/clear-dep-target-fine.output.js
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "type": "div",
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "value": "correct"
-      }
-    }
-  ]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/multi/clear-dep-target-fine.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/multi/clear-dep-target-fine.source.js b/html5/test/case/multi/clear-dep-target-fine.source.js
deleted file mode 100644
index fd7c7a2..0000000
--- a/html5/test/case/multi/clear-dep-target-fine.source.js
+++ /dev/null
@@ -1,48 +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.
- */
-
-define('@weex-component/DepTargetFine', function(require, exports, module) {
-
-  ;module.exports = {
-    data: function () {return {
-      lists: []
-    }},
-    created: function() {
-      this.lists.$set(0, 'correct')
-    }
-  }
-
-
-  ;module.exports.style = {}
-
-  ;module.exports.template = {
-	  "type": "div",
-	  "children": [
-	    {
-	      "type": "text",
-	      "attr": {
-	        "value": function () { return this.lists[0] }
-	      }
-	    }
-	  ]
-  }
-
-})
-
-bootstrap('@weex-component/DepTargetFine', { "transformerVersion": "0.3.1" })

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/multi/clear-moduleA.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/multi/clear-moduleA.output.js b/html5/test/case/multi/clear-moduleA.output.js
deleted file mode 100644
index 5e7eac3..0000000
--- a/html5/test/case/multi/clear-moduleA.output.js
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "type": "div",
-  "children": [
-    {
-      "type": "text",
-      "event": ["click"],
-      "attr": {
-        "value": "sample"
-      }
-    }
-  ]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/multi/clear-moduleA.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/multi/clear-moduleA.source.js b/html5/test/case/multi/clear-moduleA.source.js
deleted file mode 100644
index 190b36f..0000000
--- a/html5/test/case/multi/clear-moduleA.source.js
+++ /dev/null
@@ -1,61 +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.
- */
-
-define('@weex-component/moduleA', function(require, exports, module) {
-
-  define('sample', function(require, exports, module) {
-    module.exports = { name: 'sample' }
-  })
-
-  ;module.exports = {
-    data: {
-      text: ''
-    },
-    methods: {
-      update: function() {
-        var outer = require('sample')
-        this.text = outer ? outer.name : 'nothing'
-      }
-    }
-  }
-
-
-  ;module.exports.style = {}
-
-  ;module.exports.template = {
-	  "type": "div",
-	  "children": [
-	    {
-	      "type": "text",
-    	  "events": {
-    	    "click": "update"
-    	  },
-	      "attr": {
-	        "value": function() { return this.text }
-	      }
-	    }
-	  ]
-  }
-
-})
-
-// require module
-bootstrap('@weex-component/moduleA', {
-    "transformerVersion": "0.3.1"
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/multi/clear-moduleB.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/multi/clear-moduleB.output.js b/html5/test/case/multi/clear-moduleB.output.js
deleted file mode 100644
index c31de94..0000000
--- a/html5/test/case/multi/clear-moduleB.output.js
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "type": "div",
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "value": "nothing"
-      }
-    }
-  ]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/multi/clear-moduleB.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/multi/clear-moduleB.source.js b/html5/test/case/multi/clear-moduleB.source.js
deleted file mode 100644
index c6338fd..0000000
--- a/html5/test/case/multi/clear-moduleB.source.js
+++ /dev/null
@@ -1,50 +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.
- */
-define('@weex-component/moduleB', function(require, exports, module) {
-
-  ;module.exports = {
-    data: {
-      text: ''
-    },
-    created: function() {
-      var smp = require('sample')
-      this.text = smp ? smp.name : 'nothing'
-    }
-  }
-
-  ;module.exports.style = {}
-
-  ;module.exports.template = {
-	  "type": "div",
-	  "children": [
-	    {
-	      "type": "text",
-	      "attr": {
-	        "value": function() { return this.text }
-	      }
-	    }
-	  ]
-  }
-
-})
-
-// require module
-bootstrap('@weex-component/moduleB', {
-    "transformerVersion": "0.3.1"
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/prepare.js
----------------------------------------------------------------------
diff --git a/html5/test/case/prepare.js b/html5/test/case/prepare.js
deleted file mode 100644
index d787c42..0000000
--- a/html5/test/case/prepare.js
+++ /dev/null
@@ -1,83 +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 fs from 'fs'
-import path from 'path'
-
-// load test driver
-import {
-  Runtime,
-  Instance
-} from 'weex-vdom-tester'
-
-// load env
-import shared from '../../shared'
-import { Document, Element, Comment } from '../../runtime/vdom'
-import Listener from '../../runtime/bridge/Listener'
-import { TaskCenter, init } from '../../runtime/bridge/TaskCenter'
-
-// load framework
-import * as defaultFramework from '../../frameworks/legacy'
-import { subversion } from '../../../package.json'
-
-shared.setNativeConsole()
-
-// mock config & global APIs
-global.callNativeHandler = function () {}
-
-// init special API called `callAddElement()`
-// which is supported temporarily in native render
-global.callAddElement = function (id, ref, json, index) {
-  return callNativeHandler(id, [{ module: 'dom', method: 'addElement', args: [ref, json, index] }])
-}
-
-init()
-
-// create test driver runtime
-export function createRuntime () {
-  const config = {
-    Document, Element, Comment, Listener,
-    TaskCenter,
-    sendTasks (...args) {
-      return callNativeHandler(...args)
-    }
-  }
-
-  Document.Listener = Listener
-  Document.handler = config.sendTasks
-
-  defaultFramework.init(config)
-
-  Object.assign(global, {
-    frameworkVersion: subversion.native,
-    transformerVersion: subversion.transformer
-  })
-
-  return new Runtime(defaultFramework)
-}
-
-// create framework instance
-export function createApp (runtime) {
-  return new Instance(runtime)
-}
-
-// read source code from file
-export function getCode (filePath) {
-  const absolutPath = path.join(__dirname, filePath)
-  return fs.readFileSync(absolutPath, 'utf8')
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/signals/modal.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/signals/modal.output.js b/html5/test/case/signals/modal.output.js
deleted file mode 100644
index f645aa2..0000000
--- a/html5/test/case/signals/modal.output.js
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-  type: 'div',
-  children: [{
-    type: 'text',
-    attr: {
-      value: 'foo'
-    }
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/signals/modal.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/signals/modal.source.js b/html5/test/case/signals/modal.source.js
deleted file mode 100644
index bdef1b8..0000000
--- a/html5/test/case/signals/modal.source.js
+++ /dev/null
@@ -1,60 +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.
- */
-define('@weex-component/modal', function (require, exports, module) {
-
-;
-  var modal = require('@weex-module/modal')
-  module.exports = {
-    data: function () {
-      return {
-        x: 'foo'
-      }
-    },
-    created: function () {
-      var message = 'Here is a message for you!'
-      var okTitle = 'OK'
-      modal.alert({
-        message: message,
-        okTitle: okTitle
-      }, function() {
-        this.x = 'bar'
-      }.bind(this))
-    }
-  }
-
-
-;module.exports.template = {
-  "type": "div",
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "value": function () {
-          return this.x
-        }
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/modal')


[16/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo6.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo6.source.js b/html5/test/case/basic/foo6.source.js
deleted file mode 100644
index 4301a50..0000000
--- a/html5/test/case/basic/foo6.source.js
+++ /dev/null
@@ -1,71 +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.
- */
-define('@weex-component/foo6', function (require, exports, module) {
-
-;
-
-;module.exports.style = {
-  "container": {
-    "flexDirection": "row"
-  },
-  "thumb": {
-    "width": 200,
-    "height": 200
-  },
-  "title": {
-    "flex": 1,
-    "color": "#ff0000",
-    "fontSize": 48,
-    "fontWeight": "bold",
-    "backgroundColor": "#eeeeee"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "classList": [
-    "container"
-  ],
-  "children": [
-    {
-      "type": "image",
-      "classList": [
-        "thumb"
-      ],
-      "attr": {
-        "src": "http://gw.alicdn.com/tfscom/TB1OMfAJFXXXXbfXXXXqVMCNVXX-400-400.jpg"
-      }
-    },
-    {
-      "type": "text",
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": "一个超赞的宝贝标题"
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/foo6')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo7.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo7.output.js b/html5/test/case/basic/foo7.output.js
deleted file mode 100644
index d19c92f..0000000
--- a/html5/test/case/basic/foo7.output.js
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  type: 'container',
-  event: ['click'],
-  children: [{
-    type: 'image',
-    attr: {
-      src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
-    },
-    style: {
-      width: 200,
-      height: 200
-    }
-  }, {
-    type: 'text',
-    attr: {
-      value: '一个超赞的宝贝标题'
-    },
-    style: {
-      flex: 1,
-      color: '#ff0000',
-      fontSize: 48,
-      fontWeight: 'bold',
-      backgroundColor: '#eeeeee'
-    }
-  }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo7.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo7.source.js b/html5/test/case/basic/foo7.source.js
deleted file mode 100644
index 2d9b43f..0000000
--- a/html5/test/case/basic/foo7.source.js
+++ /dev/null
@@ -1,76 +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.
- */
-define('@weex-component/foo7', function (require, exports, module) {
-
-;
-  module.exports = {
-    methods: {
-      gotoDetail: function () {
-        this.$openURL('https://item.taobao.com/item.htm?id=520421163634')
-      }
-    }
-  }
-
-
-;module.exports.style = {
-  "thumb": {
-    "width": 200,
-    "height": 200
-  },
-  "title": {
-    "flex": 1,
-    "color": "#ff0000",
-    "fontSize": 48,
-    "fontWeight": "bold",
-    "backgroundColor": "#eeeeee"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "events": {
-    "click": "gotoDetail"
-  },
-  "children": [
-    {
-      "type": "image",
-      "classList": [
-        "thumb"
-      ],
-      "attr": {
-        "src": "https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg"
-      }
-    },
-    {
-      "type": "text",
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": "一个超赞的宝贝标题"
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/foo7')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo8.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo8.output.js b/html5/test/case/basic/foo8.output.js
deleted file mode 100644
index 8ed4bb1..0000000
--- a/html5/test/case/basic/foo8.output.js
+++ /dev/null
@@ -1,60 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'container',
-    style: {
-      flexDirection: 'row'
-    },
-    event: ['click'],
-    children: [{
-      type: 'image',
-      attr: {
-        src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
-      },
-      style: {
-        width: 200,
-        height: 200
-      }
-    }, {
-      type: 'text',
-      attr: {
-        value: 'title1'
-      },
-      style: {
-        flex: 1,
-        color: '#ff0000',
-        fontSize: 48,
-        fontWeight: 'bold',
-        backgroundColor: '#eeeeee'
-      }
-    }]
-  }, {
-    type: 'container',
-    style: {
-      flexDirection: 'row'
-    },
-    event: ['click'],
-    children: [{
-      type: 'image',
-      attr: {
-        src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
-      },
-      style: {
-        width: 200,
-        height: 200
-      }
-    }, {
-      type: 'text',
-      attr: {
-        value: 'title2'
-      },
-      style: {
-        flex: 1,
-        color: '#ff0000',
-        fontSize: 48,
-        fontWeight: 'bold',
-        backgroundColor: '#eeeeee'
-      }
-    }]
-  }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo8.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo8.source.js b/html5/test/case/basic/foo8.source.js
deleted file mode 100644
index d2512f2..0000000
--- a/html5/test/case/basic/foo8.source.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.
- */
-define('@weex-component/foo8', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      itemList: [
-        {itemId: '520421163634', title: 'title1', pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'},
-        {itemId: '522076777462', title: 'title2', pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'}
-      ]
-    }},
-    methods: {
-      gotoDetail: function () {
-        this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId)
-      }
-    }
-  }
-
-
-;module.exports.style = {
-  "thumb": {
-    "width": 200,
-    "height": 200
-  },
-  "title": {
-    "flex": 1,
-    "color": "#ff0000",
-    "fontSize": 48,
-    "fontWeight": "bold",
-    "backgroundColor": "#eeeeee"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "container",
-      "repeat": function () {return this.itemList},
-      "events": {
-        "click": "gotoDetail"
-      },
-      "style": {
-        "flexDirection": "row"
-      },
-      "children": [
-        {
-          "type": "image",
-          "classList": [
-            "thumb"
-          ],
-          "attr": {
-            "src": function () {return this.pictureUrl}
-          }
-        },
-        {
-          "type": "text",
-          "classList": [
-            "title"
-          ],
-          "attr": {
-            "value": function () {return this.title}
-          }
-        }
-      ]
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/foo8')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo9.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo9.output.js b/html5/test/case/basic/foo9.output.js
deleted file mode 100644
index 8f6d086..0000000
--- a/html5/test/case/basic/foo9.output.js
+++ /dev/null
@@ -1,57 +0,0 @@
-{
-  type: 'container',
-  style: {
-    flexDirection: 'column'
-  },
-  children: [{
-    type: 'container',
-    event: ['click'],
-    children: [{
-      type: 'image',
-      attr: {
-        src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
-      },
-      style: {
-        width: 200,
-        height: 200
-      }
-    }, {
-      type: 'text',
-      attr: {
-        value: 'title1'
-      },
-      style: {
-        flex: 1,
-        color: '#ff0000',
-        fontSize: 48,
-        fontWeight: 'bold',
-        backgroundColor: '#eeeeee'
-      }
-    }]
-  }, {
-    type: 'container',
-    event: ['click'],
-    children: [{
-      type: 'image',
-      attr: {
-        src: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'
-      },
-      style: {
-        width: 200,
-        height: 200
-      }
-    }, {
-      type: 'text',
-      attr: {
-        value: 'title2'
-      },
-      style: {
-        flex: 1,
-        color: '#ff0000',
-        fontSize: 48,
-        fontWeight: 'bold',
-        backgroundColor: '#eeeeee'
-      }
-    }]
-  }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/foo9.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/foo9.source.js b/html5/test/case/basic/foo9.source.js
deleted file mode 100644
index da534e9..0000000
--- a/html5/test/case/basic/foo9.source.js
+++ /dev/null
@@ -1,113 +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.
- */
-define('@weex-component/taobao-item', function (require, exports, module) {
-
-;
-    module.exports = {
-      data: function () {return {
-        itemId: '',
-        title: '',
-        pictureUrl: ''
-      }},
-      methods: {
-        gotoDetail: function () {
-          this.$openURL('https://item.taobao.com/item.htm?id=' + this.itemId)
-        }
-      }
-    }
-  
-
-;module.exports.style = {
-  "thumb": {
-    "width": 200,
-    "height": 200
-  },
-  "title": {
-    "flex": 1,
-    "color": "#ff0000",
-    "fontSize": 48,
-    "fontWeight": "bold",
-    "backgroundColor": "#eeeeee"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "events": {
-    "click": "gotoDetail"
-  },
-  "children": [
-    {
-      "type": "image",
-      "classList": [
-        "thumb"
-      ],
-      "attr": {
-        "src": function () {return this.pictureUrl}
-      }
-    },
-    {
-      "type": "text",
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": function () {return this.title}
-      }
-    }
-  ]
-}
-
-;})
-
-// module
-
-define('@weex-component/foo9', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      itemList: [
-        {itemId: '520421163634', title: 'title1', pictureUrl: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'},
-        {itemId: '522076777462', title: 'title2', pictureUrl: 'https://gd1.alicdn.com/bao/uploaded/i1/TB1PXJCJFXXXXciXFXXXXXXXXXX_!!0-item_pic.jpg'}
-      ]
-    }}
-  }
-
-
-;module.exports.style = {}
-
-;module.exports.template = {
-  "type": "container",
-  "style": {
-    "flexDirection": "column"
-  },
-  "children": [
-    {
-      "type": "taobao-item",
-      "repeat": function () {return this.itemList}
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/foo9')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/global-weex-object.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/global-weex-object.output.js b/html5/test/case/basic/global-weex-object.output.js
deleted file mode 100644
index 3e1fa19..0000000
--- a/html5/test/case/basic/global-weex-object.output.js
+++ /dev/null
@@ -1,6 +0,0 @@
-{
-    type: 'div',
-    attr: {
-        a: 'WeexDemo'
-    }
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/global-weex-object.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/global-weex-object.source.js b/html5/test/case/basic/global-weex-object.source.js
deleted file mode 100644
index 252af7e..0000000
--- a/html5/test/case/basic/global-weex-object.source.js
+++ /dev/null
@@ -1,37 +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.
- */
-weex.define('@weex-component/foo', function (require, exports, module) {
-  module.exports = {
-    data: function () {
-      return {
-        x: weex.config.env.appName
-      }
-    },
-    template: {
-      "type": "div",
-      "attr": {
-        "a": function () {
-          return this.x
-        }
-      }
-    }
-  }
-})
-
-weex.bootstrap('@weex-component/foo')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/id.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/id.output.js b/html5/test/case/basic/id.output.js
deleted file mode 100644
index 7ce073c..0000000
--- a/html5/test/case/basic/id.output.js
+++ /dev/null
@@ -1,32 +0,0 @@
-{
-  type: 'div',
-  children: [
-    {
-      type: 'text',
-      attr: {
-        value: 'Hello',
-        a: 1
-      }
-    },
-    {
-      type: 'text',
-      attr: {
-        value: 'Hello',
-        a: 1,
-        b: 1
-      }
-    },
-    {
-      type: 'text',
-      attr: {
-        value: 'Hello'
-      }
-    },
-    {
-      type: 'text',
-      attr: {
-        value: 'Hello'
-      }
-    }
-  ]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/id.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/id.source.js b/html5/test/case/basic/id.source.js
deleted file mode 100644
index 3c85886..0000000
--- a/html5/test/case/basic/id.source.js
+++ /dev/null
@@ -1,80 +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.
- */
-define('@weex-component/id', function (require, exports, module) {
-
-;
-  module.exports = {
-    ready: function () {
-      if (this.$el('x')) {
-        this.$el('x').setAttr('a', 1)
-      }
-      if (this.$el('y')) {
-        this.$el('y').setAttr('a', 1)
-      }
-      if (this.$el('')) {
-        this.$el('').setAttr('a', 1)
-      }
-      if (this.$el('0')) {
-        this.$el('0').setAttr('a', 1)
-      }
-      if (this.$el(0)) {
-        this.$el(0).setAttr('b', 1)
-      }
-    }
-  }
-
-;module.exports.template = {
-  "type": "div",
-  "children": [
-    {
-      "type": "text",
-      "id": "x",
-      "attr": {
-        "value": "Hello"
-      }
-    },
-    {
-      "type": "text",
-      "id": "0",
-      "attr": {
-        "value": "Hello"
-      }
-    },
-    {
-      "type": "text",
-      "id": '',
-      "attr": {
-        "value": "Hello"
-      }
-    },
-    {
-      "type": "text",
-      "id": null,
-      "attr": {
-        "value": "Hello"
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/id')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/if-repeat.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/if-repeat.output.js b/html5/test/case/basic/if-repeat.output.js
deleted file mode 100644
index 5265727..0000000
--- a/html5/test/case/basic/if-repeat.output.js
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: 'Hello World1'
-    }
-  }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/if-repeat.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/if-repeat.source.js b/html5/test/case/basic/if-repeat.source.js
deleted file mode 100644
index aa62931..0000000
--- a/html5/test/case/basic/if-repeat.source.js
+++ /dev/null
@@ -1,60 +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.
- */
-define('@weex-component/if-repeat', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      titlelist: [
-        {showTitle: true, title: 'Hello World1'},
-        {showTitle: false, title: 'Hello World2'}
-      ]
-    }}
-  }
-
-
-;module.exports.style = {
-  "title": {
-    "fontSize": 26,
-    "color": "#FF0000"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "repeat": function () {return this.titlelist},
-      "shown": function () {return this.showTitle},
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": function () {return this.title}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/if-repeat')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/if-root.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/if-root.output.js b/html5/test/case/basic/if-root.output.js
deleted file mode 100644
index 0967ef4..0000000
--- a/html5/test/case/basic/if-root.output.js
+++ /dev/null
@@ -1 +0,0 @@
-{}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/if-root.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/if-root.source.js b/html5/test/case/basic/if-root.source.js
deleted file mode 100644
index ca94316..0000000
--- a/html5/test/case/basic/if-root.source.js
+++ /dev/null
@@ -1,44 +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.
- */
-define('@weex-component/if-root', function (require, exports, module) {
-
-;module.exports = {
-  data: function () {return {
-	  visible: true,
-  }}
-}
-
-;module.exports.template = {
-  "type": "container",
-  "shown": function () {return this.visible},
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "value": "container"
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/if-root')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/if.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/if.output.js b/html5/test/case/basic/if.output.js
deleted file mode 100644
index 03dbeae..0000000
--- a/html5/test/case/basic/if.output.js
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: 'Hello World'
-    }
-  }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/if.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/if.source.js b/html5/test/case/basic/if.source.js
deleted file mode 100644
index 3ebb80c..0000000
--- a/html5/test/case/basic/if.source.js
+++ /dev/null
@@ -1,57 +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.
- */
-define('@weex-component/if', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      showTitle: true,
-      title: 'Hello World'
-    }}
-  }
-
-
-;module.exports.style = {
-  "title": {
-    "fontSize": 26,
-    "color": "#FF0000"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "shown": function () {return this.showTitle},
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": function () {return this.title}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/if')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/promise.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/promise.output.js b/html5/test/case/basic/promise.output.js
deleted file mode 100644
index 07b17c9..0000000
--- a/html5/test/case/basic/promise.output.js
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    type: 'container'
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/promise.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/promise.source.js b/html5/test/case/basic/promise.source.js
deleted file mode 100644
index 67554ba..0000000
--- a/html5/test/case/basic/promise.source.js
+++ /dev/null
@@ -1,47 +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.
- */
-define('@weex-component/foo', function (require, exports, module) {
-new Promise(function (fulfill, reject){
-  fulfill(1)
-})
-Promise.all([Promise.resolve(1)])
-;
-  module.exports = {
-    data: function () {
-      return {
-        x: 1
-      }
-    }
-  }
-
-
-;module.exports.style = {
-  "a": {
-    "fontSize": 12
-  }
-}
-
-;module.exports.template = {
-  "type": "container"
-}
-
-;})
-
-
-bootstrap('@weex-component/foo')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/ready.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/ready.output.js b/html5/test/case/basic/ready.output.js
deleted file mode 100644
index 39c15f3..0000000
--- a/html5/test/case/basic/ready.output.js
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "children": [
-    {
-      "attr": {
-        "value": "1"
-      },
-      "type": "text"
-    }
-  ],
-  "type": "container"
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/ready.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/ready.source.js b/html5/test/case/basic/ready.source.js
deleted file mode 100644
index 08ee893..0000000
--- a/html5/test/case/basic/ready.source.js
+++ /dev/null
@@ -1,56 +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.
- */
-define('@weex-component/created', function(require, exports, module) {
-
-    ;
-    module.exports = {
-        data: function() {
-            return {
-                text: '',
-            }
-        },
-        ready: function() {
-            this.text = '1'
-        }
-    }
-
-
-    ;
-    module.exports.style = {}
-
-    ;
-    module.exports.template = {
-        "type": "container",
-        "children": [{
-            "type": "text",
-            "attr": {
-                "value": function() {
-                    return this.text
-                }
-            }
-        }]
-    }
-
-    ;
-})
-
-// require module
-bootstrap('@weex-component/created', {
-    "transformerVersion": "0.3.1"
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-array-kv.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-array-kv.output.js b/html5/test/case/basic/repeat-array-kv.output.js
deleted file mode 100644
index 4d4991a..0000000
--- a/html5/test/case/basic/repeat-array-kv.output.js
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: '0: Hello World1'
-    }
-  }, {
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: '1: Hello World2'
-    }
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-array-kv.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-array-kv.source.js b/html5/test/case/basic/repeat-array-kv.source.js
deleted file mode 100644
index 21f26c7..0000000
--- a/html5/test/case/basic/repeat-array-kv.source.js
+++ /dev/null
@@ -1,63 +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.
- */
-define('@weex-component/repeat-array-kv', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      titlelist: [
-        {text: 'Hello World1'},
-        {text: 'Hello World2'}
-      ]
-    }}
-  }
-
-
-;module.exports.style = {
-  "title": {
-    "fontSize": 26,
-    "color": "#FF0000"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "repeat": {
-        expression: function() { return this.titlelist },
-        key: 'index',
-        value: 'title'
-      },
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": function () {return this.index + ': ' + this.title.text}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/repeat-array-kv')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-array-no-kv.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-array-no-kv.output.js b/html5/test/case/basic/repeat-array-no-kv.output.js
deleted file mode 100644
index 4d4991a..0000000
--- a/html5/test/case/basic/repeat-array-no-kv.output.js
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: '0: Hello World1'
-    }
-  }, {
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: '1: Hello World2'
-    }
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-array-no-kv.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-array-no-kv.source.js b/html5/test/case/basic/repeat-array-no-kv.source.js
deleted file mode 100644
index 910bf04..0000000
--- a/html5/test/case/basic/repeat-array-no-kv.source.js
+++ /dev/null
@@ -1,61 +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.
- */
-define('@weex-component/repeat-array-no-kv', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      titlelist: [
-        {text: 'Hello World1'},
-        {text: 'Hello World2'}
-      ]
-    }}
-  }
-
-
-;module.exports.style = {
-  "title": {
-    "fontSize": 26,
-    "color": "#FF0000"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "repeat": {
-        expression: function() { return this.titlelist }
-      },
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": function () {return this.$index + ': ' + this.$value.text}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/repeat-array-no-kv')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-array-non-obj.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-array-non-obj.output.js b/html5/test/case/basic/repeat-array-non-obj.output.js
deleted file mode 100644
index 4d4991a..0000000
--- a/html5/test/case/basic/repeat-array-non-obj.output.js
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: '0: Hello World1'
-    }
-  }, {
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: '1: Hello World2'
-    }
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-array-non-obj.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-array-non-obj.source.js b/html5/test/case/basic/repeat-array-non-obj.source.js
deleted file mode 100644
index 71de5d2..0000000
--- a/html5/test/case/basic/repeat-array-non-obj.source.js
+++ /dev/null
@@ -1,62 +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.
- */
-define('@weex-component/repeat-array-non-obj', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      titlelist: [
-        'Hello World1',
-        'Hello World2'
-      ]
-    }}
-  }
-
-
-;module.exports.style = {
-  "title": {
-    "fontSize": 26,
-    "color": "#FF0000"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "repeat": {
-        expression: function() { return this.titlelist },
-        value: 'title'
-      },
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": function () {return this.$index + ': ' + this.title}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/repeat-array-non-obj')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-array-v.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-array-v.output.js b/html5/test/case/basic/repeat-array-v.output.js
deleted file mode 100644
index 4d4991a..0000000
--- a/html5/test/case/basic/repeat-array-v.output.js
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: '0: Hello World1'
-    }
-  }, {
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: '1: Hello World2'
-    }
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-array-v.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-array-v.source.js b/html5/test/case/basic/repeat-array-v.source.js
deleted file mode 100644
index 0fecb04..0000000
--- a/html5/test/case/basic/repeat-array-v.source.js
+++ /dev/null
@@ -1,62 +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.
- */
-define('@weex-component/repeat-array-v', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      titlelist: [
-        {text: 'Hello World1'},
-        {text: 'Hello World2'}
-      ]
-    }}
-  }
-
-
-;module.exports.style = {
-  "title": {
-    "fontSize": 26,
-    "color": "#FF0000"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "repeat": {
-        expression: function() { return this.titlelist },
-        value: 'title'
-      },
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": function () {return this.$index + ': ' + this.title.text}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/repeat-array-v')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-index.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-index.output.js b/html5/test/case/basic/repeat-index.output.js
deleted file mode 100644
index 4d4991a..0000000
--- a/html5/test/case/basic/repeat-index.output.js
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: '0: Hello World1'
-    }
-  }, {
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: '1: Hello World2'
-    }
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-index.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-index.source.js b/html5/test/case/basic/repeat-index.source.js
deleted file mode 100644
index 5bdc4ea..0000000
--- a/html5/test/case/basic/repeat-index.source.js
+++ /dev/null
@@ -1,59 +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.
- */
-define('@weex-component/repeat-index', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      titlelist: [
-        {title: 'Hello World1'},
-        {title: 'Hello World2'}
-      ]
-    }}
-  }
-
-
-;module.exports.style = {
-  "title": {
-    "fontSize": 26,
-    "color": "#FF0000"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "repeat": function () {return this.titlelist},
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": function () {return this.$index + ': ' + this.title}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/repeat-index')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-root.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-root.output.js b/html5/test/case/basic/repeat-root.output.js
deleted file mode 100644
index 0967ef4..0000000
--- a/html5/test/case/basic/repeat-root.output.js
+++ /dev/null
@@ -1 +0,0 @@
-{}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-root.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-root.source.js b/html5/test/case/basic/repeat-root.source.js
deleted file mode 100644
index ac79ae4..0000000
--- a/html5/test/case/basic/repeat-root.source.js
+++ /dev/null
@@ -1,47 +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.
- */
-define('@weex-component/repeat-root', function (require, exports, module) {
-
-;module.exports = {
-  data: function () {return {
-    containers: [
-      {name: 'a'},
-      {name: 'b'}
-    ]
-  }}
-}
-
-;module.exports.template = {
-  "type": "container",
-  "repeat": function () {return this.containers},
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "value": "container"
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/repeat-root')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-watch.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-watch.output.js b/html5/test/case/basic/repeat-watch.output.js
deleted file mode 100644
index 1170f8b..0000000
--- a/html5/test/case/basic/repeat-watch.output.js
+++ /dev/null
@@ -1,46 +0,0 @@
-{
-    "type": "list",
-    "children": [{
-        "type": "cell",
-        "attr": {
-            "append": "tree"
-        },
-        "children": [{
-            "type": "text",
-            "attr": {
-                "value": 1
-            },
-            "style": {
-                "height": 600
-            }
-        }]
-    }, {
-        "type": "cell",
-        "attr": {
-            "append": "tree"
-        },
-        "children": [{
-            "type": "text",
-            "attr": {
-                "value": 2
-            },
-            "style": {
-                "height": 600
-            }
-        }]
-    }, {
-        "type": "cell",
-        "attr": {
-            "append": "tree"
-        },
-        "children": [{
-            "type": "text",
-            "attr": {
-                "value": 3
-            },
-            "style": {
-                "height": 600
-            }
-        }]
-    }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/repeat-watch.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/repeat-watch.source.js b/html5/test/case/basic/repeat-watch.source.js
deleted file mode 100644
index 0f989d8..0000000
--- a/html5/test/case/basic/repeat-watch.source.js
+++ /dev/null
@@ -1,75 +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.
- */
-define('@weex-component/04f1048c50eca3454bc70de93d3213d4', function(require, exports, module) {
-
-    ;
-    module.exports = {
-        data: function() {
-            return {
-                list: [{
-                    text: 1
-                }, {
-                    text: 2
-                }],
-                more: [{
-                    text: 3
-                }]
-            }
-        },
-        ready: function() {
-            for (var i = 0; i < this.more.length; i++) {
-                this.list.push(this.more[i]);
-            }
-        }
-    }
-
-
-    ;
-    module.exports.style = {}
-
-    ;
-    module.exports.template = {
-        "type": "list",
-        "children": [{
-            "type": "cell",
-            "append": "tree",
-            "repeat": function() {
-                return this.list
-            },
-            "children": [{
-                "type": "text",
-                "style": {
-                    "height": 600
-                },
-                "attr": {
-                    "value": function() {
-                        return this.text
-                    }
-                }
-            }]
-        }]
-    }
-
-    ;
-})
-
-// require module
-bootstrap('@weex-component/04f1048c50eca3454bc70de93d3213d4', {
-    "transformerVersion": "0.3.1"
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/require.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/require.output.js b/html5/test/case/basic/require.output.js
deleted file mode 100644
index 51c886e..0000000
--- a/html5/test/case/basic/require.output.js
+++ /dev/null
@@ -1,9 +0,0 @@
-{
-    type: 'container',
-    children: [{
-        type: 'text',
-        attr: {
-            value: 'Hello World'
-        }
-    }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/require.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/require.source.js b/html5/test/case/basic/require.source.js
deleted file mode 100644
index db1fed7..0000000
--- a/html5/test/case/basic/require.source.js
+++ /dev/null
@@ -1,52 +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.
- */
-var browserifyRequire=function r(e,n,o){function t(u,i){if(!n[u]){if(!e[u]){var a="function"==typeof browserifyRequire&&browserifyRequire;if(!i&&a)return a(u,!0);if(f)return f(u,!0);var c=new Error("Cannot find module '"+u+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[u]={exports:{}};e[u][0].call(l.exports,function(r){var n=e[u][1][r];return t(n?n:r)},l,l.exports,r,e,n,o)}return n[u].exports}for(var f="function"==typeof browserifyRequire&&browserifyRequire,u=0;u<o.length;u++)t(o[u]);return t}({"1ba80324acff072bbb2b768911a5e4f1":[function(r,e,n){e.exports="Hello World"},{}]},{},[]);
-
-
-define('@weex-component/foo', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      text: ''
-    }},
-    created: function() {
-      this.text = browserifyRequire("1ba80324acff072bbb2b768911a5e4f1")
-    }
-  }
-
-
-;module.exports.style = {}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "value": function () {return this.text}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-bootstrap('@weex-component/foo', {"transformerVersion":"0.3.0"})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/reset-style.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/reset-style.output.js b/html5/test/case/basic/reset-style.output.js
deleted file mode 100644
index ed6bbaf..0000000
--- a/html5/test/case/basic/reset-style.output.js
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "value": "Hello Weex"
-      },
-      "style": {
-        "fontSize": 40,
-        "color": ""
-      }
-    }
-  ]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/reset-style.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/reset-style.source.js b/html5/test/case/basic/reset-style.source.js
deleted file mode 100644
index a96b891..0000000
--- a/html5/test/case/basic/reset-style.source.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.
- */
-define('@weex-component/reset-style', function (require, exports, module) {
-
-;
-  module.exports = {
-	  data: function () {return {
-	    className: 'highlight'
-	  }},
-    ready: function() {
-      this.className = 'normal'
-    }
-  }
-
-
-;module.exports.style = {
-  "normal": {
-    "fontSize": 40
-  },
-  "highlight": {
-    "fontSize": 60,
-    "color": "#FF0000"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "classList": function () {return [this.className]},
-      "attr": {
-        "value": "Hello Weex"
-      }
-    }
-  ]
-}
-
-;})
-
-
-bootstrap('@weex-component/reset-style')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/static1.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/static1.output.js b/html5/test/case/basic/static1.output.js
deleted file mode 100644
index e400730..0000000
--- a/html5/test/case/basic/static1.output.js
+++ /dev/null
@@ -1,12 +0,0 @@
-{
-  "type": "div",
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "value": "hello",
-        "static": ""
-      }
-    }
-  ]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/static1.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/static1.source.js b/html5/test/case/basic/static1.source.js
deleted file mode 100644
index 575ef66..0000000
--- a/html5/test/case/basic/static1.source.js
+++ /dev/null
@@ -1,51 +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.
- */
-define('@weex-component/static1', function (require, exports, module) {
-
-;
-  module.exports = {
-    ready: function() {
-	      this.x = 'world'
-	    },
-	    data: function () {return {
-	      x: 'hello'
-	    }}
-  }
-
-
-;module.exports.style = {
-}
-
-;module.exports.template = {
-  "type": "div",
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "static": "",
-        "value": function () {return this.x}
-      }
-    }
-  ]
-}
-
-;})
-
-
-bootstrap('@weex-component/static1')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/static2.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/static2.output.js b/html5/test/case/basic/static2.output.js
deleted file mode 100644
index fd5ee57..0000000
--- a/html5/test/case/basic/static2.output.js
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "type": "div",
-  "attr": {
-    "static": ""
-  },
-  "children": [
-    {
-      "type": "div",
-      "children": [
-        {
-          "type": "text",
-          "attr": {
-            "value": 1
-          }
-        }
-      ]
-    },
-    {
-      "type": "div",
-      "children": [
-        {
-          "type": "text",
-          "attr": {
-            "value": 2
-          }
-        }
-      ]
-    }
-  ]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/static2.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/static2.source.js b/html5/test/case/basic/static2.source.js
deleted file mode 100644
index 93e1cf6..0000000
--- a/html5/test/case/basic/static2.source.js
+++ /dev/null
@@ -1,62 +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.
- */
-define('@weex-component/static2', function (require, exports, module) {
-
-;
-  module.exports = {
-    ready: function() {
-	      this.arr.$set(2, 3)
-	  },
-    data: function () {return {
-      arr: [1, 2]
-    }}
-  }
-
-
-;module.exports.style = {
-}
-
-;module.exports.template = {
-  "type": "div",
-  "attr": {
-    "static": ""
-  },
-  "children": [
-    {
-      "type": "div",
-      "repeat": {
-        "expression": function () {return this.arr},
-        "value": "x"
-      },
-      "children": [
-        {
-          "type": "text",
-          "attr": {
-            "value": function () {return this.x}
-          }
-        }
-      ]
-    }
-  ]
-}
-
-;})
-
-
-bootstrap('@weex-component/static2')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/static3.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/static3.output.js b/html5/test/case/basic/static3.output.js
deleted file mode 100644
index 73eaaab..0000000
--- a/html5/test/case/basic/static3.output.js
+++ /dev/null
@@ -1,33 +0,0 @@
-{
-  "type": "div",
-  "children": [
-    {
-      "type": "div",
-      "attr": {
-        "static": ""
-      },
-      "children": [
-        {
-          "type": "text",
-          "attr": {
-            "value": 1
-          }
-        }
-      ]
-    },
-    {
-      "type": "div",
-      "attr": {
-        "static": ""
-      },
-      "children": [
-        {
-          "type": "text",
-          "attr": {
-            "value": 2
-          }
-        }
-      ]
-    }
-  ]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/static3.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/static3.source.js b/html5/test/case/basic/static3.source.js
deleted file mode 100644
index 9179ea7..0000000
--- a/html5/test/case/basic/static3.source.js
+++ /dev/null
@@ -1,62 +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.
- */
-define('@weex-component/static3', function (require, exports, module) {
-
-;
-  module.exports = {
-    ready: function() {
-	      this.arr.$set(2, 3)
-	    },
-	    data: function () {return {
-	      arr: [1, 2]
-	    }}
-  }
-
-
-;module.exports.style = {
-}
-
-;module.exports.template = {
-  "type": "div",
-  "children": [
-    {
-      "type": "div",
-      "repeat": {
-        "expression": function () {return this.arr},
-        "value": "x"
-      },
-      "attr": {
-        "static": ""
-      },
-      "children": [
-        {
-          "type": "text",
-          "attr": {
-            "value": function () {return this.x}
-          }
-        }
-      ]
-    }
-  ]
-}
-
-;})
-
-
-bootstrap('@weex-component/static3')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/static4.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/static4.output.js b/html5/test/case/basic/static4.output.js
deleted file mode 100644
index a53e484..0000000
--- a/html5/test/case/basic/static4.output.js
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  "type": "div",
-  "attr": {
-    "static": ""
-  },
-  "children": [
-    {
-      "type": "div",
-      "children": [
-        {
-          "type": "div",
-          "children": [
-            {
-              "type": "text",
-              "attr": {
-                "value": 1
-              }
-            }
-          ]
-        },
-        {
-          "type": "div",
-          "children": [
-            {
-              "type": "text",
-              "attr": {
-                "value": 2
-              }
-            }
-          ]
-        }
-      ]
-    }
-  ]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/static4.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/static4.source.js b/html5/test/case/basic/static4.source.js
deleted file mode 100644
index fe31bcc..0000000
--- a/html5/test/case/basic/static4.source.js
+++ /dev/null
@@ -1,93 +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.
- */
-function otherNameFunction () {
-  var module = {}
-  var exports = {}
-  module.exports = exports
-
-  ;module.exports.style = {}
-
-  ;module.exports.template = {
-    "type": "div",
-	  "children": [
-	    {
-	      "type": "div",
-	      "repeat": {
-	        "expression": function () {return this.outer},
-	        "value": "x"
-	      },
-	      "children": [
-	        {
-	          "type": "text",
-	          "attr": {
-	            "value": function () {return this.x}
-	          }
-	        }
-	      ]
-	    }
-	  ]
-  }
-
-  ;module.exports.data = function() {return {
-    outer: null
-  }}
-
-  return module.exports
-}
-
-// module
-
-define('@weex-component/static4', function (require, exports, module) {
-
-;
-  module.exports = {
-    "components": {
-      innertpl: otherNameFunction()
-    },
-    ready: function() {
-      this.arr.$set(2, 3)
-    },
-    data: function () {return {
-      arr: [1, 2]
-    }}
-  }
-
-
-;module.exports.style = {}
-
-;module.exports.template = {
-  "type": "div",
-  "attr": {
-    "static": ""
-  },
-  "children": [
-    {
-      "type": "innertpl",
-      "attr": {
-        "outer": function () {return this.arr}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/static4')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/subvm.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/subvm.output.js b/html5/test/case/basic/subvm.output.js
deleted file mode 100644
index b7acaa4..0000000
--- a/html5/test/case/basic/subvm.output.js
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-    "type": "container",
-    "children": [{
-        "type": "container",
-        "style": {
-            "marginTop": 10
-        },
-        "children": [{
-            "type": "text",
-            "attr": {
-                "value": "aa"
-            }
-        },{
-            "type": "text",
-            "attr": {
-                "value": "bb"
-            }
-        }]
-    }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/subvm.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/subvm.source.js b/html5/test/case/basic/subvm.source.js
deleted file mode 100644
index 1b054a5..0000000
--- a/html5/test/case/basic/subvm.source.js
+++ /dev/null
@@ -1,98 +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.
- */
-define('@weex-component/innertpl', function (require, exports, module) {
-
-;
-
-;module.exports.style = {}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "value": function () {return this.outerData.a}
-      }
-    },
-    {
-      "type": "text",
-      "attr": {
-        "value": function () {return this.outerData.b}
-      }
-    }
-  ]
-}
-
-;module.exports.created = function() {
-  this.outerData.a = 'aa'
-}
-
-;module.exports.methods = {
-  "ready": function() {
-    this.outerData.b = 'bb'
-  }
-}
-
-;})
-
-// module
-
-define('@weex-component/subvm', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      item: {a: 'a', b: 'b'},
-      className: 'fromOuter',
-      marginTop: 10
-    }}
-  }
-
-
-;module.exports.style = {}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "innertpl",
-      "classList": function() {
-        return this.className
-      },
-      "style": {
-        marginTop: function() {
-          return this.marginTop
-        }
-      },
-      "events": {
-          "click": "clicked"
-      },
-      "attr": {
-        "outerData": function () {return this.item}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/subvm')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/transformer1.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/transformer1.output.js b/html5/test/case/basic/transformer1.output.js
deleted file mode 100644
index 1372f91..0000000
--- a/html5/test/case/basic/transformer1.output.js
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    type: 'container'
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/basic/transformer1.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/basic/transformer1.source.js b/html5/test/case/basic/transformer1.source.js
deleted file mode 100644
index 7985e28..0000000
--- a/html5/test/case/basic/transformer1.source.js
+++ /dev/null
@@ -1,46 +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.
- */
-define('@weex-component/foo', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {
-      return {
-        x: 1
-      }
-    }
-  }
-
-
-;module.exports.style = {
-  "a": {
-    "fontSize": 12
-  }
-}
-
-;module.exports.template = {
-  "type": "container"
-}
-
-;})
-
-
-bootstrap('@weex-component/foo', {
-  'transformerVersion': '0.3.0'
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/append-root-event.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/append-root-event.output.js b/html5/test/case/complex/append-root-event.output.js
deleted file mode 100644
index 2a1b77c..0000000
--- a/html5/test/case/complex/append-root-event.output.js
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  type: 'div',
-  attr: {
-    append: "tree"
-  },
-  children: [{
-    type: 'text',
-    event: ['click'],
-    attr: {
-      value: 1
-    }
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/append-root-event.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/append-root-event.source.js b/html5/test/case/complex/append-root-event.source.js
deleted file mode 100644
index 49455e5..0000000
--- a/html5/test/case/complex/append-root-event.source.js
+++ /dev/null
@@ -1,63 +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.
- */
-define('@weex-component/append-root-event', function(require, exports, module) {
-
-    ;
-    module.exports = {
-        data: function() {
-            return {
-                x: 1
-            }
-        },
-        methods: {
-            update: function() {
-                this.x = 2
-            }
-        }
-    }
-
-
-    ;
-    module.exports.style = {}
-
-    ;
-    module.exports.template = {
-        "type": "div",
-        "append": "tree",
-        "children": [{
-            "type": "text",
-            "events": {
-                "click": "update"
-            },
-            "attr": {
-                "value": function() {
-                    return this.x
-                }
-            }
-        }]
-    }
-
-    ;
-})
-
-// require module
-
-bootstrap('@weex-component/append-root-event', {
-    "transformerVersion": "0.3.1"
-})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/click.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/click.output.js b/html5/test/case/complex/click.output.js
deleted file mode 100644
index d19c92f..0000000
--- a/html5/test/case/complex/click.output.js
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  type: 'container',
-  event: ['click'],
-  children: [{
-    type: 'image',
-    attr: {
-      src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
-    },
-    style: {
-      width: 200,
-      height: 200
-    }
-  }, {
-    type: 'text',
-    attr: {
-      value: '一个超赞的宝贝标题'
-    },
-    style: {
-      flex: 1,
-      color: '#ff0000',
-      fontSize: 48,
-      fontWeight: 'bold',
-      backgroundColor: '#eeeeee'
-    }
-  }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/click.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/click.source.js b/html5/test/case/complex/click.source.js
deleted file mode 100644
index 039c98b..0000000
--- a/html5/test/case/complex/click.source.js
+++ /dev/null
@@ -1,76 +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.
- */
-define('@weex-component/click', function (require, exports, module) {
-
-;
-  module.exports = {
-    methods: {
-      gotoDetail: function () {
-        this.$openURL('https://item.taobao.com/item.htm?id=520421163634')
-      }
-    }
-  }
-
-
-;module.exports.style = {
-  "thumb": {
-    "width": 200,
-    "height": 200
-  },
-  "title": {
-    "flex": 1,
-    "color": "#ff0000",
-    "fontSize": 48,
-    "fontWeight": "bold",
-    "backgroundColor": "#eeeeee"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "events": {
-    "click": "gotoDetail"
-  },
-  "children": [
-    {
-      "type": "image",
-      "classList": [
-        "thumb"
-      ],
-      "attr": {
-        "src": "https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg"
-      }
-    },
-    {
-      "type": "text",
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": "一个超赞的宝贝标题"
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/click')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/component-append-tree.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/component-append-tree.output.js b/html5/test/case/complex/component-append-tree.output.js
deleted file mode 100644
index 6c036ec..0000000
--- a/html5/test/case/complex/component-append-tree.output.js
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  type: 'scroller',
-  children: [{
-    type: 'div',
-    attr: { "append": "node" },
-    children: [{
-      type: 'text',
-      attr: {
-        value: 'ready'
-      }
-    }]
-  }, {
-    type: 'div',
-    attr: { "append": "tree" },
-    children: [{
-      type: 'text',
-      attr: {
-        value: 'ready'
-      }
-    }]
-  }]
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/component-append-tree.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/component-append-tree.source.js b/html5/test/case/complex/component-append-tree.source.js
deleted file mode 100644
index 7088eea..0000000
--- a/html5/test/case/complex/component-append-tree.source.js
+++ /dev/null
@@ -1,87 +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.
- */
-
-define('@weex-component/sub', function(require, exports, module) {
-  
-      ;
-      module.exports = {
-          data: function() {
-              return {
-                  str: '-----'
-              }
-          },
-          ready: function() {
-              this.str = 'ready'
-              setTimeout(() => {
-                  this.str = 'timeout'
-              }, 100)
-          }
-      }
-  
-  
-      ;
-      module.exports.style = {}
-  
-      ;
-      module.exports.template = {
-          "type": "text",
-          "attr": {
-              "value": function() {
-                  return this.str
-              }
-          }
-      }
-  
-      ;
-  })
-  
-  // module
-  
-  define('@weex-component/ad164d40f42747f598b56eccfd0eae8e', function(require, exports, module) {
-  
-      ;
-  
-      ;
-      module.exports.style = {}
-  
-      ;
-      module.exports.template = {
-          "type": "scroller",
-          "children": [{
-              "type": "div",
-              "append": "node",
-              "children": [{
-                  "type": "sub"
-              }]
-          }, {
-              "type": "div",
-              "append": "tree",
-              "children": [{
-                  "type": "sub"
-              }]
-          }]
-      }
-  
-      ;
-  })
-  
-  // require module
-  bootstrap('@weex-component/ad164d40f42747f598b56eccfd0eae8e', {
-      "transformerVersion": "0.4.3"
-  })
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/computed.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/computed.output.js b/html5/test/case/complex/computed.output.js
deleted file mode 100644
index 8c5534d..0000000
--- a/html5/test/case/complex/computed.output.js
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  type: 'container',
-  event: ['click'],
-  children: [{
-    type: 'text',
-    attr: {
-      value: 3
-    },
-  }, {
-    type: 'text',
-    attr: {
-      value: 3
-    }
-  }]
-}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/computed.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/computed.source.js b/html5/test/case/complex/computed.source.js
deleted file mode 100644
index 8d59ca3..0000000
--- a/html5/test/case/complex/computed.source.js
+++ /dev/null
@@ -1,72 +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.
- */
-define('@weex-component/computed', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {
-      return {x: 1, y: 2}
-    },
-    computed: {
-      n: function () {
-        return this.x + this.y
-      },
-      m: {
-        get: function () {
-          return this.x + this.y
-        },
-        set: function (v) {
-          this.y = v
-        }
-      }
-    },
-    methods: {
-      foo: function () {
-        this.m = 10
-      }
-    }
-  }
-
-
-;module.exports.template = {
-  "type": "container",
-  "events": {
-    "click": "foo"
-  },
-  "children": [
-    {
-      "type": "text",
-      "attr": {
-        "value": function () {return this.n}
-      }
-    },
-    {
-      "type": "text",
-      "attr": {
-        "value": function () {return this.m}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/computed')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/if-refresh.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/if-refresh.output.js b/html5/test/case/complex/if-refresh.output.js
deleted file mode 100644
index 1372f91..0000000
--- a/html5/test/case/complex/if-refresh.output.js
+++ /dev/null
@@ -1,3 +0,0 @@
-{
-    type: 'container'
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/if-refresh.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/if-refresh.source.js b/html5/test/case/complex/if-refresh.source.js
deleted file mode 100644
index 3ebb80c..0000000
--- a/html5/test/case/complex/if-refresh.source.js
+++ /dev/null
@@ -1,57 +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.
- */
-define('@weex-component/if', function (require, exports, module) {
-
-;
-  module.exports = {
-    data: function () {return {
-      showTitle: true,
-      title: 'Hello World'
-    }}
-  }
-
-
-;module.exports.style = {
-  "title": {
-    "fontSize": 26,
-    "color": "#FF0000"
-  }
-}
-
-;module.exports.template = {
-  "type": "container",
-  "children": [
-    {
-      "type": "text",
-      "shown": function () {return this.showTitle},
-      "classList": [
-        "title"
-      ],
-      "attr": {
-        "value": function () {return this.title}
-      }
-    }
-  ]
-}
-
-;})
-
-// require module
-
-bootstrap('@weex-component/if')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/complex/if-repeat-refresh.output.js
----------------------------------------------------------------------
diff --git a/html5/test/case/complex/if-repeat-refresh.output.js b/html5/test/case/complex/if-repeat-refresh.output.js
deleted file mode 100644
index d4831cc..0000000
--- a/html5/test/case/complex/if-repeat-refresh.output.js
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  type: 'container',
-  children: [{
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: 'Hello World2'
-    }
-  }, {
-    type: 'text',
-    style: {
-      color: '#FF0000',
-      fontSize: 26
-    },
-    attr: {
-      value: 'Hello World3'
-    }
-  }]
-}
\ No newline at end of file



[14/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/html5/test/case/signals/signals-long.source.js
----------------------------------------------------------------------
diff --git a/html5/test/case/signals/signals-long.source.js b/html5/test/case/signals/signals-long.source.js
deleted file mode 100644
index 06b541f..0000000
--- a/html5/test/case/signals/signals-long.source.js
+++ /dev/null
@@ -1,1223 +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.
- */
-new Promise(function () {})
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-;define('systemConfig', function (require, exports, module) {
-    module.exports = {"isAliIntranet":false,"serverTime":"2016-07-04 17:00:00"};
-    module.exports.serverTime = new Date(module.exports.serverTime).getTime();
-    module.exports.clientTime = new Date().getTime();
-});
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-;define('zebraConfig', function (require, exports, module) {
-    module.exports = {"spma":"0","spmb":"0","name":"zebra-act-item-240x240","title":"zebra-act-item-240x240","pageStatus":"online","modules":[{"name":"zebra-act-item-240x240","rmcId":"zebra-act-item-240x240","zebraId":"zebra-act-item-240x240","moduleId":"zebra-act-item-240x240","meta":{"size":"10px"}}],"closeCustom":true};
-});
-
-;define('zebraDoms', function (require, exports, module) {
-    var els = [
-      {"isZebraModule":true,"zebraId":"zebra-act-item-240x240","name":"zebra-act-item-240x240","data":{"$config":{"$dynamic":true,"mobileNoLogo":false},"item":[{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemIm
 g":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png",
 "itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/",
 "itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpX
 XQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":
 567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","
 brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.ali
 cdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.al
 icdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXX
 XJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.
 com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com
 /tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子
 短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短
 裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","bra
 ndLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"item
 MPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicd
 n.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXX
 XGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg"
 :"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","i
 temDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","i
 temTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQ
 U6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":56
 7,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","br
 andLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicd
 n.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alic
 dn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJ
 XFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.co
 m/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/t
 ps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子�
 �裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短�
 �连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brand
 Logo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMP
 rice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.
 com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXG
 XFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"
 http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","ite
 mDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","ite
 mTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6
 CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,
 "itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","bran
 dLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.
 com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn
 .com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXF
 XXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/
 tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps
 /i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短�
 ��连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙�
 ��衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLo
 go":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPri
 ce":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.co
 m/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXF
 XXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"ht
 tp://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemD
 esc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemT
 itle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CG
 XXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"i
 temActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandL
 ogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.co
 m/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.c
 om/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXX
 mqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tp
 s/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i
 1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙
 连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连
 衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo
 ":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice
 ":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/
 tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXX
 G9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http
 ://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDes
 c":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTit
 le":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXX
 X-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"ite
 mActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLog
 o":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/
 tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com
 /tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmq
 jTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/
 i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/
 TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙�
 �衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连�
 �裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":
 "http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":
 12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tp
 s/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9
 gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http:/
 /gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc"
 :"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle
 ":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-
 348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemA
 ctPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo"
 :"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","itemTagPrice":567,"itemActPrice":356,"itemMPrice":123456,"itemActPriceTitle":"狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tp
 s/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","itemTitle":"套头格子短裙连衣裙","couponValue":300,"couponUrl":"http://www.tmall.com/","itemTagPrice":567,"itemActPrice":356,"itemMPrice":12345.6,"itemActPriceTitle":"双11狂欢价","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/tps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","itemDesc":"全店满500送130!","btnText":""},{"itemImg":"http://gtms01.alicdn.com/tps/i1/TB1JQtzJFXXXXciXpXXQU6CGXXX-348-428.png","itemUrl":"http://www.tmall.com/","couponValue":10,"couponUrl":"http://www.tmall.com/","brandLogo":"http://gtms03.alicdn.com/tps/i3/TB1V8lkJFXXXXXGXFXXG9gsFXXX-140-70.png","iconImg":"http://gtms04.alicdn.com/t
 ps/i4/TB1bURwJFXXXXXJXFXXmqjTHFXX-60-60.png","btnText":"快来买"},{"i

<TRUNCATED>


[06/22] incubator-weex git commit: * [jsfm] Adjust the file structure of js framework

Posted by ha...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/ready.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/ready.source.js b/test/js-framework/case/basic/ready.source.js
new file mode 100644
index 0000000..08ee893
--- /dev/null
+++ b/test/js-framework/case/basic/ready.source.js
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+define('@weex-component/created', function(require, exports, module) {
+
+    ;
+    module.exports = {
+        data: function() {
+            return {
+                text: '',
+            }
+        },
+        ready: function() {
+            this.text = '1'
+        }
+    }
+
+
+    ;
+    module.exports.style = {}
+
+    ;
+    module.exports.template = {
+        "type": "container",
+        "children": [{
+            "type": "text",
+            "attr": {
+                "value": function() {
+                    return this.text
+                }
+            }
+        }]
+    }
+
+    ;
+})
+
+// require module
+bootstrap('@weex-component/created', {
+    "transformerVersion": "0.3.1"
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-array-kv.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-array-kv.output.js b/test/js-framework/case/basic/repeat-array-kv.output.js
new file mode 100644
index 0000000..4d4991a
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-array-kv.output.js
@@ -0,0 +1,22 @@
+{
+  type: 'container',
+  children: [{
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: '0: Hello World1'
+    }
+  }, {
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: '1: Hello World2'
+    }
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-array-kv.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-array-kv.source.js b/test/js-framework/case/basic/repeat-array-kv.source.js
new file mode 100644
index 0000000..21f26c7
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-array-kv.source.js
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+define('@weex-component/repeat-array-kv', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      titlelist: [
+        {text: 'Hello World1'},
+        {text: 'Hello World2'}
+      ]
+    }}
+  }
+
+
+;module.exports.style = {
+  "title": {
+    "fontSize": 26,
+    "color": "#FF0000"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "repeat": {
+        expression: function() { return this.titlelist },
+        key: 'index',
+        value: 'title'
+      },
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": function () {return this.index + ': ' + this.title.text}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/repeat-array-kv')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-array-no-kv.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-array-no-kv.output.js b/test/js-framework/case/basic/repeat-array-no-kv.output.js
new file mode 100644
index 0000000..4d4991a
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-array-no-kv.output.js
@@ -0,0 +1,22 @@
+{
+  type: 'container',
+  children: [{
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: '0: Hello World1'
+    }
+  }, {
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: '1: Hello World2'
+    }
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-array-no-kv.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-array-no-kv.source.js b/test/js-framework/case/basic/repeat-array-no-kv.source.js
new file mode 100644
index 0000000..910bf04
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-array-no-kv.source.js
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+define('@weex-component/repeat-array-no-kv', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      titlelist: [
+        {text: 'Hello World1'},
+        {text: 'Hello World2'}
+      ]
+    }}
+  }
+
+
+;module.exports.style = {
+  "title": {
+    "fontSize": 26,
+    "color": "#FF0000"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "repeat": {
+        expression: function() { return this.titlelist }
+      },
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": function () {return this.$index + ': ' + this.$value.text}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/repeat-array-no-kv')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-array-non-obj.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-array-non-obj.output.js b/test/js-framework/case/basic/repeat-array-non-obj.output.js
new file mode 100644
index 0000000..4d4991a
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-array-non-obj.output.js
@@ -0,0 +1,22 @@
+{
+  type: 'container',
+  children: [{
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: '0: Hello World1'
+    }
+  }, {
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: '1: Hello World2'
+    }
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-array-non-obj.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-array-non-obj.source.js b/test/js-framework/case/basic/repeat-array-non-obj.source.js
new file mode 100644
index 0000000..71de5d2
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-array-non-obj.source.js
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+define('@weex-component/repeat-array-non-obj', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      titlelist: [
+        'Hello World1',
+        'Hello World2'
+      ]
+    }}
+  }
+
+
+;module.exports.style = {
+  "title": {
+    "fontSize": 26,
+    "color": "#FF0000"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "repeat": {
+        expression: function() { return this.titlelist },
+        value: 'title'
+      },
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": function () {return this.$index + ': ' + this.title}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/repeat-array-non-obj')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-array-v.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-array-v.output.js b/test/js-framework/case/basic/repeat-array-v.output.js
new file mode 100644
index 0000000..4d4991a
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-array-v.output.js
@@ -0,0 +1,22 @@
+{
+  type: 'container',
+  children: [{
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: '0: Hello World1'
+    }
+  }, {
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: '1: Hello World2'
+    }
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-array-v.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-array-v.source.js b/test/js-framework/case/basic/repeat-array-v.source.js
new file mode 100644
index 0000000..0fecb04
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-array-v.source.js
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+define('@weex-component/repeat-array-v', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      titlelist: [
+        {text: 'Hello World1'},
+        {text: 'Hello World2'}
+      ]
+    }}
+  }
+
+
+;module.exports.style = {
+  "title": {
+    "fontSize": 26,
+    "color": "#FF0000"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "repeat": {
+        expression: function() { return this.titlelist },
+        value: 'title'
+      },
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": function () {return this.$index + ': ' + this.title.text}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/repeat-array-v')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-index.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-index.output.js b/test/js-framework/case/basic/repeat-index.output.js
new file mode 100644
index 0000000..4d4991a
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-index.output.js
@@ -0,0 +1,22 @@
+{
+  type: 'container',
+  children: [{
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: '0: Hello World1'
+    }
+  }, {
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: '1: Hello World2'
+    }
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-index.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-index.source.js b/test/js-framework/case/basic/repeat-index.source.js
new file mode 100644
index 0000000..5bdc4ea
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-index.source.js
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+define('@weex-component/repeat-index', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      titlelist: [
+        {title: 'Hello World1'},
+        {title: 'Hello World2'}
+      ]
+    }}
+  }
+
+
+;module.exports.style = {
+  "title": {
+    "fontSize": 26,
+    "color": "#FF0000"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "repeat": function () {return this.titlelist},
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": function () {return this.$index + ': ' + this.title}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/repeat-index')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-root.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-root.output.js b/test/js-framework/case/basic/repeat-root.output.js
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-root.output.js
@@ -0,0 +1 @@
+{}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-root.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-root.source.js b/test/js-framework/case/basic/repeat-root.source.js
new file mode 100644
index 0000000..ac79ae4
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-root.source.js
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+define('@weex-component/repeat-root', function (require, exports, module) {
+
+;module.exports = {
+  data: function () {return {
+    containers: [
+      {name: 'a'},
+      {name: 'b'}
+    ]
+  }}
+}
+
+;module.exports.template = {
+  "type": "container",
+  "repeat": function () {return this.containers},
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "value": "container"
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/repeat-root')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-watch.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-watch.output.js b/test/js-framework/case/basic/repeat-watch.output.js
new file mode 100644
index 0000000..1170f8b
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-watch.output.js
@@ -0,0 +1,46 @@
+{
+    "type": "list",
+    "children": [{
+        "type": "cell",
+        "attr": {
+            "append": "tree"
+        },
+        "children": [{
+            "type": "text",
+            "attr": {
+                "value": 1
+            },
+            "style": {
+                "height": 600
+            }
+        }]
+    }, {
+        "type": "cell",
+        "attr": {
+            "append": "tree"
+        },
+        "children": [{
+            "type": "text",
+            "attr": {
+                "value": 2
+            },
+            "style": {
+                "height": 600
+            }
+        }]
+    }, {
+        "type": "cell",
+        "attr": {
+            "append": "tree"
+        },
+        "children": [{
+            "type": "text",
+            "attr": {
+                "value": 3
+            },
+            "style": {
+                "height": 600
+            }
+        }]
+    }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/repeat-watch.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/repeat-watch.source.js b/test/js-framework/case/basic/repeat-watch.source.js
new file mode 100644
index 0000000..0f989d8
--- /dev/null
+++ b/test/js-framework/case/basic/repeat-watch.source.js
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+define('@weex-component/04f1048c50eca3454bc70de93d3213d4', function(require, exports, module) {
+
+    ;
+    module.exports = {
+        data: function() {
+            return {
+                list: [{
+                    text: 1
+                }, {
+                    text: 2
+                }],
+                more: [{
+                    text: 3
+                }]
+            }
+        },
+        ready: function() {
+            for (var i = 0; i < this.more.length; i++) {
+                this.list.push(this.more[i]);
+            }
+        }
+    }
+
+
+    ;
+    module.exports.style = {}
+
+    ;
+    module.exports.template = {
+        "type": "list",
+        "children": [{
+            "type": "cell",
+            "append": "tree",
+            "repeat": function() {
+                return this.list
+            },
+            "children": [{
+                "type": "text",
+                "style": {
+                    "height": 600
+                },
+                "attr": {
+                    "value": function() {
+                        return this.text
+                    }
+                }
+            }]
+        }]
+    }
+
+    ;
+})
+
+// require module
+bootstrap('@weex-component/04f1048c50eca3454bc70de93d3213d4', {
+    "transformerVersion": "0.3.1"
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/require.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/require.output.js b/test/js-framework/case/basic/require.output.js
new file mode 100644
index 0000000..51c886e
--- /dev/null
+++ b/test/js-framework/case/basic/require.output.js
@@ -0,0 +1,9 @@
+{
+    type: 'container',
+    children: [{
+        type: 'text',
+        attr: {
+            value: 'Hello World'
+        }
+    }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/require.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/require.source.js b/test/js-framework/case/basic/require.source.js
new file mode 100644
index 0000000..db1fed7
--- /dev/null
+++ b/test/js-framework/case/basic/require.source.js
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+var browserifyRequire=function r(e,n,o){function t(u,i){if(!n[u]){if(!e[u]){var a="function"==typeof browserifyRequire&&browserifyRequire;if(!i&&a)return a(u,!0);if(f)return f(u,!0);var c=new Error("Cannot find module '"+u+"'");throw c.code="MODULE_NOT_FOUND",c}var l=n[u]={exports:{}};e[u][0].call(l.exports,function(r){var n=e[u][1][r];return t(n?n:r)},l,l.exports,r,e,n,o)}return n[u].exports}for(var f="function"==typeof browserifyRequire&&browserifyRequire,u=0;u<o.length;u++)t(o[u]);return t}({"1ba80324acff072bbb2b768911a5e4f1":[function(r,e,n){e.exports="Hello World"},{}]},{},[]);
+
+
+define('@weex-component/foo', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      text: ''
+    }},
+    created: function() {
+      this.text = browserifyRequire("1ba80324acff072bbb2b768911a5e4f1")
+    }
+  }
+
+
+;module.exports.style = {}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "value": function () {return this.text}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+bootstrap('@weex-component/foo', {"transformerVersion":"0.3.0"})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/reset-style.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/reset-style.output.js b/test/js-framework/case/basic/reset-style.output.js
new file mode 100644
index 0000000..ed6bbaf
--- /dev/null
+++ b/test/js-framework/case/basic/reset-style.output.js
@@ -0,0 +1,15 @@
+{
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "value": "Hello Weex"
+      },
+      "style": {
+        "fontSize": 40,
+        "color": ""
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/reset-style.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/reset-style.source.js b/test/js-framework/case/basic/reset-style.source.js
new file mode 100644
index 0000000..a96b891
--- /dev/null
+++ b/test/js-framework/case/basic/reset-style.source.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.
+ */
+define('@weex-component/reset-style', function (require, exports, module) {
+
+;
+  module.exports = {
+	  data: function () {return {
+	    className: 'highlight'
+	  }},
+    ready: function() {
+      this.className = 'normal'
+    }
+  }
+
+
+;module.exports.style = {
+  "normal": {
+    "fontSize": 40
+  },
+  "highlight": {
+    "fontSize": 60,
+    "color": "#FF0000"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "classList": function () {return [this.className]},
+      "attr": {
+        "value": "Hello Weex"
+      }
+    }
+  ]
+}
+
+;})
+
+
+bootstrap('@weex-component/reset-style')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/static1.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/static1.output.js b/test/js-framework/case/basic/static1.output.js
new file mode 100644
index 0000000..e400730
--- /dev/null
+++ b/test/js-framework/case/basic/static1.output.js
@@ -0,0 +1,12 @@
+{
+  "type": "div",
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "value": "hello",
+        "static": ""
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/static1.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/static1.source.js b/test/js-framework/case/basic/static1.source.js
new file mode 100644
index 0000000..575ef66
--- /dev/null
+++ b/test/js-framework/case/basic/static1.source.js
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+define('@weex-component/static1', function (require, exports, module) {
+
+;
+  module.exports = {
+    ready: function() {
+	      this.x = 'world'
+	    },
+	    data: function () {return {
+	      x: 'hello'
+	    }}
+  }
+
+
+;module.exports.style = {
+}
+
+;module.exports.template = {
+  "type": "div",
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "static": "",
+        "value": function () {return this.x}
+      }
+    }
+  ]
+}
+
+;})
+
+
+bootstrap('@weex-component/static1')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/static2.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/static2.output.js b/test/js-framework/case/basic/static2.output.js
new file mode 100644
index 0000000..fd5ee57
--- /dev/null
+++ b/test/js-framework/case/basic/static2.output.js
@@ -0,0 +1,30 @@
+{
+  "type": "div",
+  "attr": {
+    "static": ""
+  },
+  "children": [
+    {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "attr": {
+            "value": 1
+          }
+        }
+      ]
+    },
+    {
+      "type": "div",
+      "children": [
+        {
+          "type": "text",
+          "attr": {
+            "value": 2
+          }
+        }
+      ]
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/static2.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/static2.source.js b/test/js-framework/case/basic/static2.source.js
new file mode 100644
index 0000000..93e1cf6
--- /dev/null
+++ b/test/js-framework/case/basic/static2.source.js
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+define('@weex-component/static2', function (require, exports, module) {
+
+;
+  module.exports = {
+    ready: function() {
+	      this.arr.$set(2, 3)
+	  },
+    data: function () {return {
+      arr: [1, 2]
+    }}
+  }
+
+
+;module.exports.style = {
+}
+
+;module.exports.template = {
+  "type": "div",
+  "attr": {
+    "static": ""
+  },
+  "children": [
+    {
+      "type": "div",
+      "repeat": {
+        "expression": function () {return this.arr},
+        "value": "x"
+      },
+      "children": [
+        {
+          "type": "text",
+          "attr": {
+            "value": function () {return this.x}
+          }
+        }
+      ]
+    }
+  ]
+}
+
+;})
+
+
+bootstrap('@weex-component/static2')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/static3.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/static3.output.js b/test/js-framework/case/basic/static3.output.js
new file mode 100644
index 0000000..73eaaab
--- /dev/null
+++ b/test/js-framework/case/basic/static3.output.js
@@ -0,0 +1,33 @@
+{
+  "type": "div",
+  "children": [
+    {
+      "type": "div",
+      "attr": {
+        "static": ""
+      },
+      "children": [
+        {
+          "type": "text",
+          "attr": {
+            "value": 1
+          }
+        }
+      ]
+    },
+    {
+      "type": "div",
+      "attr": {
+        "static": ""
+      },
+      "children": [
+        {
+          "type": "text",
+          "attr": {
+            "value": 2
+          }
+        }
+      ]
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/static3.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/static3.source.js b/test/js-framework/case/basic/static3.source.js
new file mode 100644
index 0000000..9179ea7
--- /dev/null
+++ b/test/js-framework/case/basic/static3.source.js
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+define('@weex-component/static3', function (require, exports, module) {
+
+;
+  module.exports = {
+    ready: function() {
+	      this.arr.$set(2, 3)
+	    },
+	    data: function () {return {
+	      arr: [1, 2]
+	    }}
+  }
+
+
+;module.exports.style = {
+}
+
+;module.exports.template = {
+  "type": "div",
+  "children": [
+    {
+      "type": "div",
+      "repeat": {
+        "expression": function () {return this.arr},
+        "value": "x"
+      },
+      "attr": {
+        "static": ""
+      },
+      "children": [
+        {
+          "type": "text",
+          "attr": {
+            "value": function () {return this.x}
+          }
+        }
+      ]
+    }
+  ]
+}
+
+;})
+
+
+bootstrap('@weex-component/static3')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/static4.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/static4.output.js b/test/js-framework/case/basic/static4.output.js
new file mode 100644
index 0000000..a53e484
--- /dev/null
+++ b/test/js-framework/case/basic/static4.output.js
@@ -0,0 +1,35 @@
+{
+  "type": "div",
+  "attr": {
+    "static": ""
+  },
+  "children": [
+    {
+      "type": "div",
+      "children": [
+        {
+          "type": "div",
+          "children": [
+            {
+              "type": "text",
+              "attr": {
+                "value": 1
+              }
+            }
+          ]
+        },
+        {
+          "type": "div",
+          "children": [
+            {
+              "type": "text",
+              "attr": {
+                "value": 2
+              }
+            }
+          ]
+        }
+      ]
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/static4.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/static4.source.js b/test/js-framework/case/basic/static4.source.js
new file mode 100644
index 0000000..fe31bcc
--- /dev/null
+++ b/test/js-framework/case/basic/static4.source.js
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+function otherNameFunction () {
+  var module = {}
+  var exports = {}
+  module.exports = exports
+
+  ;module.exports.style = {}
+
+  ;module.exports.template = {
+    "type": "div",
+	  "children": [
+	    {
+	      "type": "div",
+	      "repeat": {
+	        "expression": function () {return this.outer},
+	        "value": "x"
+	      },
+	      "children": [
+	        {
+	          "type": "text",
+	          "attr": {
+	            "value": function () {return this.x}
+	          }
+	        }
+	      ]
+	    }
+	  ]
+  }
+
+  ;module.exports.data = function() {return {
+    outer: null
+  }}
+
+  return module.exports
+}
+
+// module
+
+define('@weex-component/static4', function (require, exports, module) {
+
+;
+  module.exports = {
+    "components": {
+      innertpl: otherNameFunction()
+    },
+    ready: function() {
+      this.arr.$set(2, 3)
+    },
+    data: function () {return {
+      arr: [1, 2]
+    }}
+  }
+
+
+;module.exports.style = {}
+
+;module.exports.template = {
+  "type": "div",
+  "attr": {
+    "static": ""
+  },
+  "children": [
+    {
+      "type": "innertpl",
+      "attr": {
+        "outer": function () {return this.arr}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/static4')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/subvm.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/subvm.output.js b/test/js-framework/case/basic/subvm.output.js
new file mode 100644
index 0000000..b7acaa4
--- /dev/null
+++ b/test/js-framework/case/basic/subvm.output.js
@@ -0,0 +1,20 @@
+{
+    "type": "container",
+    "children": [{
+        "type": "container",
+        "style": {
+            "marginTop": 10
+        },
+        "children": [{
+            "type": "text",
+            "attr": {
+                "value": "aa"
+            }
+        },{
+            "type": "text",
+            "attr": {
+                "value": "bb"
+            }
+        }]
+    }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/subvm.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/subvm.source.js b/test/js-framework/case/basic/subvm.source.js
new file mode 100644
index 0000000..1b054a5
--- /dev/null
+++ b/test/js-framework/case/basic/subvm.source.js
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+define('@weex-component/innertpl', function (require, exports, module) {
+
+;
+
+;module.exports.style = {}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "value": function () {return this.outerData.a}
+      }
+    },
+    {
+      "type": "text",
+      "attr": {
+        "value": function () {return this.outerData.b}
+      }
+    }
+  ]
+}
+
+;module.exports.created = function() {
+  this.outerData.a = 'aa'
+}
+
+;module.exports.methods = {
+  "ready": function() {
+    this.outerData.b = 'bb'
+  }
+}
+
+;})
+
+// module
+
+define('@weex-component/subvm', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      item: {a: 'a', b: 'b'},
+      className: 'fromOuter',
+      marginTop: 10
+    }}
+  }
+
+
+;module.exports.style = {}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "innertpl",
+      "classList": function() {
+        return this.className
+      },
+      "style": {
+        marginTop: function() {
+          return this.marginTop
+        }
+      },
+      "events": {
+          "click": "clicked"
+      },
+      "attr": {
+        "outerData": function () {return this.item}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/subvm')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/transformer1.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/transformer1.output.js b/test/js-framework/case/basic/transformer1.output.js
new file mode 100644
index 0000000..1372f91
--- /dev/null
+++ b/test/js-framework/case/basic/transformer1.output.js
@@ -0,0 +1,3 @@
+{
+    type: 'container'
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/basic/transformer1.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/basic/transformer1.source.js b/test/js-framework/case/basic/transformer1.source.js
new file mode 100644
index 0000000..7985e28
--- /dev/null
+++ b/test/js-framework/case/basic/transformer1.source.js
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+define('@weex-component/foo', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {
+      return {
+        x: 1
+      }
+    }
+  }
+
+
+;module.exports.style = {
+  "a": {
+    "fontSize": 12
+  }
+}
+
+;module.exports.template = {
+  "type": "container"
+}
+
+;})
+
+
+bootstrap('@weex-component/foo', {
+  'transformerVersion': '0.3.0'
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/append-root-event.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/append-root-event.output.js b/test/js-framework/case/complex/append-root-event.output.js
new file mode 100644
index 0000000..2a1b77c
--- /dev/null
+++ b/test/js-framework/case/complex/append-root-event.output.js
@@ -0,0 +1,13 @@
+{
+  type: 'div',
+  attr: {
+    append: "tree"
+  },
+  children: [{
+    type: 'text',
+    event: ['click'],
+    attr: {
+      value: 1
+    }
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/append-root-event.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/append-root-event.source.js b/test/js-framework/case/complex/append-root-event.source.js
new file mode 100644
index 0000000..49455e5
--- /dev/null
+++ b/test/js-framework/case/complex/append-root-event.source.js
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+define('@weex-component/append-root-event', function(require, exports, module) {
+
+    ;
+    module.exports = {
+        data: function() {
+            return {
+                x: 1
+            }
+        },
+        methods: {
+            update: function() {
+                this.x = 2
+            }
+        }
+    }
+
+
+    ;
+    module.exports.style = {}
+
+    ;
+    module.exports.template = {
+        "type": "div",
+        "append": "tree",
+        "children": [{
+            "type": "text",
+            "events": {
+                "click": "update"
+            },
+            "attr": {
+                "value": function() {
+                    return this.x
+                }
+            }
+        }]
+    }
+
+    ;
+})
+
+// require module
+
+bootstrap('@weex-component/append-root-event', {
+    "transformerVersion": "0.3.1"
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/click.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/click.output.js b/test/js-framework/case/complex/click.output.js
new file mode 100644
index 0000000..d19c92f
--- /dev/null
+++ b/test/js-framework/case/complex/click.output.js
@@ -0,0 +1,26 @@
+{
+  type: 'container',
+  event: ['click'],
+  children: [{
+    type: 'image',
+    attr: {
+      src: 'https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg'
+    },
+    style: {
+      width: 200,
+      height: 200
+    }
+  }, {
+    type: 'text',
+    attr: {
+      value: '一个超赞的宝贝标题'
+    },
+    style: {
+      flex: 1,
+      color: '#ff0000',
+      fontSize: 48,
+      fontWeight: 'bold',
+      backgroundColor: '#eeeeee'
+    }
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/click.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/click.source.js b/test/js-framework/case/complex/click.source.js
new file mode 100644
index 0000000..039c98b
--- /dev/null
+++ b/test/js-framework/case/complex/click.source.js
@@ -0,0 +1,76 @@
+/*
+ * 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.
+ */
+define('@weex-component/click', function (require, exports, module) {
+
+;
+  module.exports = {
+    methods: {
+      gotoDetail: function () {
+        this.$openURL('https://item.taobao.com/item.htm?id=520421163634')
+      }
+    }
+  }
+
+
+;module.exports.style = {
+  "thumb": {
+    "width": 200,
+    "height": 200
+  },
+  "title": {
+    "flex": 1,
+    "color": "#ff0000",
+    "fontSize": 48,
+    "fontWeight": "bold",
+    "backgroundColor": "#eeeeee"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "events": {
+    "click": "gotoDetail"
+  },
+  "children": [
+    {
+      "type": "image",
+      "classList": [
+        "thumb"
+      ],
+      "attr": {
+        "src": "https://gd2.alicdn.com/bao/uploaded/i2/T14H1LFwBcXXXXXXXX_!!0-item_pic.jpg"
+      }
+    },
+    {
+      "type": "text",
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": "一个超赞的宝贝标题"
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/click')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/component-append-tree.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/component-append-tree.output.js b/test/js-framework/case/complex/component-append-tree.output.js
new file mode 100644
index 0000000..6c036ec
--- /dev/null
+++ b/test/js-framework/case/complex/component-append-tree.output.js
@@ -0,0 +1,22 @@
+{
+  type: 'scroller',
+  children: [{
+    type: 'div',
+    attr: { "append": "node" },
+    children: [{
+      type: 'text',
+      attr: {
+        value: 'ready'
+      }
+    }]
+  }, {
+    type: 'div',
+    attr: { "append": "tree" },
+    children: [{
+      type: 'text',
+      attr: {
+        value: 'ready'
+      }
+    }]
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/component-append-tree.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/component-append-tree.source.js b/test/js-framework/case/complex/component-append-tree.source.js
new file mode 100644
index 0000000..7088eea
--- /dev/null
+++ b/test/js-framework/case/complex/component-append-tree.source.js
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+define('@weex-component/sub', function(require, exports, module) {
+  
+      ;
+      module.exports = {
+          data: function() {
+              return {
+                  str: '-----'
+              }
+          },
+          ready: function() {
+              this.str = 'ready'
+              setTimeout(() => {
+                  this.str = 'timeout'
+              }, 100)
+          }
+      }
+  
+  
+      ;
+      module.exports.style = {}
+  
+      ;
+      module.exports.template = {
+          "type": "text",
+          "attr": {
+              "value": function() {
+                  return this.str
+              }
+          }
+      }
+  
+      ;
+  })
+  
+  // module
+  
+  define('@weex-component/ad164d40f42747f598b56eccfd0eae8e', function(require, exports, module) {
+  
+      ;
+  
+      ;
+      module.exports.style = {}
+  
+      ;
+      module.exports.template = {
+          "type": "scroller",
+          "children": [{
+              "type": "div",
+              "append": "node",
+              "children": [{
+                  "type": "sub"
+              }]
+          }, {
+              "type": "div",
+              "append": "tree",
+              "children": [{
+                  "type": "sub"
+              }]
+          }]
+      }
+  
+      ;
+  })
+  
+  // require module
+  bootstrap('@weex-component/ad164d40f42747f598b56eccfd0eae8e', {
+      "transformerVersion": "0.4.3"
+  })
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/computed.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/computed.output.js b/test/js-framework/case/complex/computed.output.js
new file mode 100644
index 0000000..8c5534d
--- /dev/null
+++ b/test/js-framework/case/complex/computed.output.js
@@ -0,0 +1,15 @@
+{
+  type: 'container',
+  event: ['click'],
+  children: [{
+    type: 'text',
+    attr: {
+      value: 3
+    },
+  }, {
+    type: 'text',
+    attr: {
+      value: 3
+    }
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/computed.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/computed.source.js b/test/js-framework/case/complex/computed.source.js
new file mode 100644
index 0000000..8d59ca3
--- /dev/null
+++ b/test/js-framework/case/complex/computed.source.js
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+define('@weex-component/computed', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {
+      return {x: 1, y: 2}
+    },
+    computed: {
+      n: function () {
+        return this.x + this.y
+      },
+      m: {
+        get: function () {
+          return this.x + this.y
+        },
+        set: function (v) {
+          this.y = v
+        }
+      }
+    },
+    methods: {
+      foo: function () {
+        this.m = 10
+      }
+    }
+  }
+
+
+;module.exports.template = {
+  "type": "container",
+  "events": {
+    "click": "foo"
+  },
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "value": function () {return this.n}
+      }
+    },
+    {
+      "type": "text",
+      "attr": {
+        "value": function () {return this.m}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/computed')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/if-refresh.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/if-refresh.output.js b/test/js-framework/case/complex/if-refresh.output.js
new file mode 100644
index 0000000..1372f91
--- /dev/null
+++ b/test/js-framework/case/complex/if-refresh.output.js
@@ -0,0 +1,3 @@
+{
+    type: 'container'
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/if-refresh.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/if-refresh.source.js b/test/js-framework/case/complex/if-refresh.source.js
new file mode 100644
index 0000000..3ebb80c
--- /dev/null
+++ b/test/js-framework/case/complex/if-refresh.source.js
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+define('@weex-component/if', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      showTitle: true,
+      title: 'Hello World'
+    }}
+  }
+
+
+;module.exports.style = {
+  "title": {
+    "fontSize": 26,
+    "color": "#FF0000"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "shown": function () {return this.showTitle},
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": function () {return this.title}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/if')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/if-repeat-refresh.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/if-repeat-refresh.output.js b/test/js-framework/case/complex/if-repeat-refresh.output.js
new file mode 100644
index 0000000..d4831cc
--- /dev/null
+++ b/test/js-framework/case/complex/if-repeat-refresh.output.js
@@ -0,0 +1,22 @@
+{
+  type: 'container',
+  children: [{
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: 'Hello World2'
+    }
+  }, {
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      value: 'Hello World3'
+    }
+  }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/if-repeat-refresh.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/if-repeat-refresh.source.js b/test/js-framework/case/complex/if-repeat-refresh.source.js
new file mode 100644
index 0000000..aa62931
--- /dev/null
+++ b/test/js-framework/case/complex/if-repeat-refresh.source.js
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+define('@weex-component/if-repeat', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      titlelist: [
+        {showTitle: true, title: 'Hello World1'},
+        {showTitle: false, title: 'Hello World2'}
+      ]
+    }}
+  }
+
+
+;module.exports.style = {
+  "title": {
+    "fontSize": 26,
+    "color": "#FF0000"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "repeat": function () {return this.titlelist},
+      "shown": function () {return this.showTitle},
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "value": function () {return this.title}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/if-repeat')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/inline-click.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/inline-click.output.js b/test/js-framework/case/complex/inline-click.output.js
new file mode 100644
index 0000000..bf3208e
--- /dev/null
+++ b/test/js-framework/case/complex/inline-click.output.js
@@ -0,0 +1,14 @@
+{
+ "type": "div",
+ "children": [
+  {
+   "type": "text",
+   "attr": {
+    "value": "Hello World1"
+   },
+   "event": [
+    "click"
+   ]
+  }
+ ]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/inline-click.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/inline-click.source.js b/test/js-framework/case/complex/inline-click.source.js
new file mode 100644
index 0000000..e447c9e
--- /dev/null
+++ b/test/js-framework/case/complex/inline-click.source.js
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+define('@weex-component/test', function (require, exports, module) {
+
+;
+module.exports = {
+    data: function () {return {
+        text: 'Hello World1'
+    }},
+    methods: {
+        clicked: function(text, e) {
+            this.text = text
+        }
+    }
+}
+
+
+;module.exports.style = {}
+
+;module.exports.template = {
+  "type": "div",
+  "children": [
+    {
+      "type": "text",
+      "events": {
+        "click": function ($event) {this.clicked('Hello World2', $event)}
+      },
+      "attr": {
+        "value": function () {return this.text}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+bootstrap('@weex-component/test', {"transformerVersion":"0.3.0"})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/input-binding.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/input-binding.output.js b/test/js-framework/case/complex/input-binding.output.js
new file mode 100644
index 0000000..30b9957
--- /dev/null
+++ b/test/js-framework/case/complex/input-binding.output.js
@@ -0,0 +1,12 @@
+{
+  "children": [
+    {
+      "attr": {
+        "value": "abcde"
+      },
+      "event": ["change"],
+      "type": "input"
+    }
+  ],
+  "type": "div"
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/input-binding.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/input-binding.source.js b/test/js-framework/case/complex/input-binding.source.js
new file mode 100644
index 0000000..ae96760
--- /dev/null
+++ b/test/js-framework/case/complex/input-binding.source.js
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+define('@weex-component/d6b20355da35c77d07f0a11ec5256f42', function(require, exports, module) {
+
+    ;
+    module.exports = {
+        data: function() {
+            return {
+                x: 'abcde',
+                changes: []
+            }
+        },
+        methods: {
+            change: function(e) {
+                var value = e.target.attr.value
+                this.x = value
+                this.changes.push(value)
+            }
+        }
+    }
+
+
+    ;
+    module.exports.style = {}
+
+    ;
+    module.exports.template = {
+        "type": "div",
+        "children": [{
+            "type": "input",
+            "attr": {
+                "value": function() {
+                    return this.x
+                }
+            },
+            "events": {
+                "change": "change"
+            }
+        }, {
+            "type": "text",
+            "repeat": {
+                "expression": function() {
+                    return this.changes
+                },
+                "key": "k",
+                "value": "v"
+            },
+            "attr": {
+                "value": function() {
+                    return (this.k + 1) + ' - ' + (this.v)
+                }
+            }
+        }]
+    }
+
+    ;
+})
+
+// require module
+bootstrap('@weex-component/d6b20355da35c77d07f0a11ec5256f42', {
+    "transformerVersion": "0.3.1"
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/refresh2.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/refresh2.output.js b/test/js-framework/case/complex/refresh2.output.js
new file mode 100644
index 0000000..823fda9
--- /dev/null
+++ b/test/js-framework/case/complex/refresh2.output.js
@@ -0,0 +1,20 @@
+{
+    "type": "container",
+    "children": [{
+        "type": "container",
+        "children": [{
+            "type": "text",
+            "attr": {
+                "value": "bar1"
+            }
+        }, {
+            "type": "container",
+            "children": [{
+                "type": "text",
+                "attr": {
+                    "value": "bar2-refresh"
+                }
+            }]
+        }]
+    }]
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/refresh2.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/refresh2.source.js b/test/js-framework/case/complex/refresh2.source.js
new file mode 100644
index 0000000..6fa770c
--- /dev/null
+++ b/test/js-framework/case/complex/refresh2.source.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.
+ */
+
+define('@weex-component/bar1', function(require, exports, module) {;;
+  module.exports.template = {
+    type: 'container',
+    children: [{
+      type: 'text',
+      attr: {
+        value: 'bar1'
+      }
+    }, {
+      type: 'bar2',
+      id: 'bar2',
+      component: true
+    }]
+  }
+
+  ;
+  module.exports.methods = {
+    ready: function(isRefresh) {
+      if (isRefresh) {
+        this.$vm('bar2').ready(true)
+      }
+    }
+  }
+
+})
+
+define('@weex-component/bar2', function(require, exports, module) {;;
+  module.exports.template = {
+    type: 'container',
+    children: [{
+      type: 'text',
+      attr: {
+        value: function() {
+          return this.text
+        }
+      }
+    }]
+  }
+
+  ;
+  module.exports.data = function() {
+    return {
+      text: 'bar2'
+    }
+  }
+
+  ;
+  module.exports.methods = {
+    ready: function(isRefresh) {
+      if (isRefresh) {
+        this.text = 'bar2-refresh'
+      }
+    }
+  }
+
+
+})
+
+define('@weex-component/foo', function(require, exports, module) {
+
+  ;
+  module.exports.style = {}
+
+  ;
+  module.exports.template = {
+    type: 'container',
+    children: [{
+      type: 'bar1',
+      shown: function() {
+        return this.showbar1
+      },
+      id: 'bar1',
+      component: true
+    }]
+  }
+
+  ;
+  module.exports.data = function() {
+    return {
+      showbar1: false,
+      ext: {
+        showbar1: false
+      }
+    }
+  }
+
+  ;module.exports.methods = {
+    refreshData: function(data) {
+      for (var key in data) {
+        this[key] = data[key]
+      }
+      this.ready(true)
+    },
+    ready: function(isRefresh) {
+      var self = this
+      this.showbar1 = this.ext.showbar1
+
+      if (isRefresh) {
+        if (this.showbar1) {
+          this.$renderThen(() => {
+            this.$vm('bar1').$on('hook:ready', function() {
+              self.$vm('bar1').ready(true)
+            })
+          })
+        }
+      }
+    }
+  }
+
+})
+
+// require module
+
+bootstrap('@weex-component/foo')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/repeat-track-by.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/repeat-track-by.output.js b/test/js-framework/case/complex/repeat-track-by.output.js
new file mode 100644
index 0000000..7e64c10
--- /dev/null
+++ b/test/js-framework/case/complex/repeat-track-by.output.js
@@ -0,0 +1,24 @@
+{
+  type: 'container',
+  children: [{
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      trackBy: 'text',
+      value: '0: Hello World2'
+    }
+  }, {
+    type: 'text',
+    style: {
+      color: '#FF0000',
+      fontSize: 26
+    },
+    attr: {
+      trackBy: 'text',
+      value: '1: Hello World1'
+    }
+  }]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/repeat-track-by.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/repeat-track-by.source.js b/test/js-framework/case/complex/repeat-track-by.source.js
new file mode 100644
index 0000000..127acb0
--- /dev/null
+++ b/test/js-framework/case/complex/repeat-track-by.source.js
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+define('@weex-component/repeat-track-by', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {return {
+      titlelist: [
+        {text: 'Hello World1'},
+        {text: 'Hello World2'}
+      ]
+    }}
+  }
+
+
+;module.exports.style = {
+  "title": {
+    "fontSize": 26,
+    "color": "#FF0000"
+  }
+}
+
+;module.exports.template = {
+  "type": "container",
+  "children": [
+    {
+      "type": "text",
+      "repeat": {
+        expression: function() { return this.titlelist },
+        key: 'index',
+        value: 'title'
+      },
+      "classList": [
+        "title"
+      ],
+      "attr": {
+        "trackBy": "text",
+        "value": function () {return this.index + ': ' + this.title.text}
+      }
+    }
+  ]
+}
+
+;})
+
+// require module
+
+bootstrap('@weex-component/repeat-track-by')

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/transformer2.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/transformer2.output.js b/test/js-framework/case/complex/transformer2.output.js
new file mode 100644
index 0000000..1372f91
--- /dev/null
+++ b/test/js-framework/case/complex/transformer2.output.js
@@ -0,0 +1,3 @@
+{
+    type: 'container'
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/transformer2.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/transformer2.source.js b/test/js-framework/case/complex/transformer2.source.js
new file mode 100644
index 0000000..af31348
--- /dev/null
+++ b/test/js-framework/case/complex/transformer2.source.js
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+define('@weex-component/foo', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {
+      return {
+        x: 1
+      }
+    }
+  }
+
+
+;module.exports.style = {
+  "a": {
+    "fontSize": 12
+  }
+}
+
+;module.exports.template = {
+  "type": "container"
+}
+
+;})
+
+
+bootstrap('@weex-component/foo', {
+  'transformerVersion': '0.0.1'
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/transformer3.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/transformer3.output.js b/test/js-framework/case/complex/transformer3.output.js
new file mode 100644
index 0000000..1372f91
--- /dev/null
+++ b/test/js-framework/case/complex/transformer3.output.js
@@ -0,0 +1,3 @@
+{
+    type: 'container'
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/complex/transformer3.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/complex/transformer3.source.js b/test/js-framework/case/complex/transformer3.source.js
new file mode 100644
index 0000000..2ca15a3
--- /dev/null
+++ b/test/js-framework/case/complex/transformer3.source.js
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ */
+define('@weex-component/foo', function (require, exports, module) {
+
+;
+  module.exports = {
+    data: function () {
+      return {
+        x: 1
+      }
+    }
+  }
+
+
+;module.exports.style = {
+  "a": {
+    "fontSize": 12
+  }
+}
+
+;module.exports.template = {
+  "type": "container"
+}
+
+;})
+
+
+bootstrap('@weex-component/foo', {
+  'transformerVersion': '9.9.9'
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/multi/clear-dep-target-error.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/multi/clear-dep-target-error.source.js b/test/js-framework/case/multi/clear-dep-target-error.source.js
new file mode 100644
index 0000000..0001d1c
--- /dev/null
+++ b/test/js-framework/case/multi/clear-dep-target-error.source.js
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+define('@weex-component/DepTargetError', function(require, exports, module) {
+
+  ;module.exports = {
+    data: function () {return {
+      a: {}
+    }}
+  }
+
+
+  ;module.exports.style = {}
+
+  ;module.exports.template = {
+	  "type": "div",
+	  "children": [
+	    {
+	      "type": "text",
+	      "attr": {
+	        "value": function () { return this.a.x.y }
+	      }
+	    }
+	  ]
+  }
+
+})
+
+bootstrap('@weex-component/DepTargetError', { "transformerVersion": "0.3.1" })

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/multi/clear-dep-target-fine.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/multi/clear-dep-target-fine.output.js b/test/js-framework/case/multi/clear-dep-target-fine.output.js
new file mode 100644
index 0000000..e129423
--- /dev/null
+++ b/test/js-framework/case/multi/clear-dep-target-fine.output.js
@@ -0,0 +1,11 @@
+{
+  "type": "div",
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "value": "correct"
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/multi/clear-dep-target-fine.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/multi/clear-dep-target-fine.source.js b/test/js-framework/case/multi/clear-dep-target-fine.source.js
new file mode 100644
index 0000000..fd7c7a2
--- /dev/null
+++ b/test/js-framework/case/multi/clear-dep-target-fine.source.js
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+define('@weex-component/DepTargetFine', function(require, exports, module) {
+
+  ;module.exports = {
+    data: function () {return {
+      lists: []
+    }},
+    created: function() {
+      this.lists.$set(0, 'correct')
+    }
+  }
+
+
+  ;module.exports.style = {}
+
+  ;module.exports.template = {
+	  "type": "div",
+	  "children": [
+	    {
+	      "type": "text",
+	      "attr": {
+	        "value": function () { return this.lists[0] }
+	      }
+	    }
+	  ]
+  }
+
+})
+
+bootstrap('@weex-component/DepTargetFine', { "transformerVersion": "0.3.1" })

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/multi/clear-moduleA.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/multi/clear-moduleA.output.js b/test/js-framework/case/multi/clear-moduleA.output.js
new file mode 100644
index 0000000..5e7eac3
--- /dev/null
+++ b/test/js-framework/case/multi/clear-moduleA.output.js
@@ -0,0 +1,12 @@
+{
+  "type": "div",
+  "children": [
+    {
+      "type": "text",
+      "event": ["click"],
+      "attr": {
+        "value": "sample"
+      }
+    }
+  ]
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/multi/clear-moduleA.source.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/multi/clear-moduleA.source.js b/test/js-framework/case/multi/clear-moduleA.source.js
new file mode 100644
index 0000000..190b36f
--- /dev/null
+++ b/test/js-framework/case/multi/clear-moduleA.source.js
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+define('@weex-component/moduleA', function(require, exports, module) {
+
+  define('sample', function(require, exports, module) {
+    module.exports = { name: 'sample' }
+  })
+
+  ;module.exports = {
+    data: {
+      text: ''
+    },
+    methods: {
+      update: function() {
+        var outer = require('sample')
+        this.text = outer ? outer.name : 'nothing'
+      }
+    }
+  }
+
+
+  ;module.exports.style = {}
+
+  ;module.exports.template = {
+	  "type": "div",
+	  "children": [
+	    {
+	      "type": "text",
+    	  "events": {
+    	    "click": "update"
+    	  },
+	      "attr": {
+	        "value": function() { return this.text }
+	      }
+	    }
+	  ]
+  }
+
+})
+
+// require module
+bootstrap('@weex-component/moduleA', {
+    "transformerVersion": "0.3.1"
+})

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/51dd277e/test/js-framework/case/multi/clear-moduleB.output.js
----------------------------------------------------------------------
diff --git a/test/js-framework/case/multi/clear-moduleB.output.js b/test/js-framework/case/multi/clear-moduleB.output.js
new file mode 100644
index 0000000..c31de94
--- /dev/null
+++ b/test/js-framework/case/multi/clear-moduleB.output.js
@@ -0,0 +1,11 @@
+{
+  "type": "div",
+  "children": [
+    {
+      "type": "text",
+      "attr": {
+        "value": "nothing"
+      }
+    }
+  ]
+}