http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/5aa1517a/packages/weex-legacy-framework/index.js
----------------------------------------------------------------------
diff --git a/packages/weex-legacy-framework/index.js 
b/packages/weex-legacy-framework/index.js
new file mode 100644
index 0000000..a2d9804
--- /dev/null
+++ b/packages/weex-legacy-framework/index.js
@@ -0,0 +1,5770 @@
+/* 'WEEX VANILLA FRAMEWORK 0.20.6, Build 2017-08-01 19:19. */
+
+(function (global, factory) {
+       typeof exports === 'object' && typeof module !== 'undefined' ? 
factory(exports) :
+       typeof define === 'function' && define.amd ? define(['exports'], 
factory) :
+       (factory((global.WeexVanillaFramework = global.WeexVanillaFramework || 
{})));
+}(this, (function (exports) { 'use strict';
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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}
+ */
+function $ (id) {
+  console.warn('[JS Framework] Vm#$ is deprecated, please use Vm#$vm instead');
+  var 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}
+ */
+function $el (id) {
+  var 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}
+ */
+function $vm (id) {
+  var info = this._ids[id];
+  if (info) {
+    return info.vm
+  }
+}
+
+/**
+ * Fire when differ rendering finished
+ *
+ * @param  {Function} fn
+ */
+function $renderThen (fn) {
+  var app = this._app;
+  var differ = app.differ;
+  return differ.then(function () {
+    fn();
+  })
+}
+
+/**
+ * scroll an element specified by id into view,
+ * moreover specify a number of offset optionally
+ * @param  {string} id
+ * @param  {number} offset
+ */
+function $scrollTo (id, offset) {
+  console.warn('[JS Framework] Vm#$scrollTo is deprecated, ' +
+          'please use "require(\'@weex-module/dom\')' +
+          '.scrollTo(el, options)" instead');
+  var el = this.$el(id);
+  if (el) {
+    var 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
+ */
+function $transition (id, options, callback) {
+  var this$1 = this;
+
+  var el = this.$el(id);
+  if (el && options && options.styles) {
+    var animation = this._app.requireModule('animation');
+    animation.transition(el.ref, options, function () {
+      var args = [], len = arguments.length;
+      while ( len-- ) args[ len ] = arguments[ len ];
+
+      this$1._setStyle(el, options.styles);
+      callback && callback.apply(void 0, 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
+ */
+function $getConfig (callback) {
+  var 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
+ */
+function $sendHttp (params, callback) {
+  console.warn('[JS Framework] Vm#$sendHttp is deprecated, ' +
+          'please use "require(\'@weex-module/stream\')' +
+          '.sendHttp(params, callback)" instead');
+  var stream = this._app.requireModule('stream');
+  stream.sendHttp(params, callback);
+}
+
+/**
+ * @deprecated
+ * open a url
+ * @param  {string} url
+ */
+function $openURL (url) {
+  console.warn('[JS Framework] Vm#$openURL is deprecated, ' +
+          'please use "require(\'@weex-module/event\')' +
+          '.openURL(url)" instead');
+  var event = this._app.requireModule('event');
+  event.openURL(url);
+}
+
+/**
+ * @deprecated
+ * set a title for page
+ * @param  {string} title
+ */
+function $setTitle (title) {
+  console.warn('[JS Framework] Vm#$setTitle is deprecated, ' +
+          'please use "require(\'@weex-module/pageInfo\')' +
+          '.setTitle(title)" instead');
+  var 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
+ */
+function $call (moduleName, methodName) {
+  var args = [], len = arguments.length - 2;
+  while ( len-- > 0 ) args[ len ] = arguments[ len + 2 ];
+
+  console.warn('[JS Framework] Vm#$call is deprecated, ' +
+    'please use "require(\'@weex-module/moduleName\')" instead');
+  var module = this._app.requireModule(moduleName);
+  if (module && module[methodName]) {
+    module[methodName].apply(module, args);
+  }
+}
+
+
+var methods = Object.freeze({
+       $: $,
+       $el: $el,
+       $vm: $vm,
+       $renderThen: $renderThen,
+       $scrollTo: $scrollTo,
+       $transition: $transition,
+       $getConfig: $getConfig,
+       $sendHttp: $sendHttp,
+       $openURL: $openURL,
+       $setTitle: $setTitle,
+       $call: $call
+});
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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) {
+  var src = [], len = arguments.length - 1;
+  while ( len-- > 0 ) src[ len ] = arguments[ len + 1 ];
+
+  /* istanbul ignore else */
+  if (typeof Object.assign === 'function') {
+    Object.assign.apply(Object, [ target ].concat( src ));
+  }
+  else {
+    var first = src.shift();
+    for (var key in first) {
+      target[key] = first[key];
+    }
+    if (src.length) {
+      extend.apply(void 0, [ target ].concat( 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) {
+    var 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}
+ */
+var 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) {
+    var l = arguments.length;
+    return l
+      ? l > 1
+        ? fn.apply(ctx, arguments)
+        : fn.call(ctx, a)
+      : fn.call(ctx)
+  }
+}
+
+/**
+ * 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}
+ */
+
+var toString = Object.prototype.toString;
+var OBJECT_STRING = '[object Object]';
+function isPlainObject (obj) {
+  return toString.call(obj) === OBJECT_STRING
+}
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/**
+ * Check if a string starts with $ or _
+ *
+ * @param {String} str
+ * @return {Boolean}
+ */
+
+function isReserved (str) {
+  var c = (str + '').charCodeAt(0);
+  return c === 0x24 || c === 0x5F
+}
+
+// can we use __proto__?
+var hasProto = '__proto__' in {};
+
+var _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);
+  };
+}
+
+/**
+ * Polyfill in iOS7 by native because the JavaScript polyfill has memory 
problem.
+ * @return {object}
+ */
+
+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}
+ */
+
+
+
+
+
+
+
+function typof (v) {
+  var s = Object.prototype.toString.call(v);
+  return s.substring(8, s.length - 1).toLowerCase()
+}
+
+// weex name rules
+
+var WEEX_COMPONENT_REG = /^@weex-component\//;
+var WEEX_MODULE_REG = /^@weex-module\//;
+var NORMAL_MODULE_REG = /^\.{1,2}\//;
+var JS_SURFIX_REG = /\.js$/;
+
+var isWeexComponent = function (name) { return 
!!name.match(WEEX_COMPONENT_REG); };
+var isWeexModule = function (name) { return !!name.match(WEEX_MODULE_REG); };
+var isNormalModule = function (name) { return !!name.match(NORMAL_MODULE_REG); 
};
+var isNpmModule = function (name) { return !isWeexComponent(name) && 
!isWeexModule(name) && !isNormalModule(name); };
+
+function removeWeexPrefix (str) {
+  var result = str.replace(WEEX_COMPONENT_REG, '').replace(WEEX_MODULE_REG, 
'');
+  return result
+}
+
+function removeJSSurfix (str) {
+  return str.replace(JS_SURFIX_REG, '')
+}
+
+/* eslint-disable */
+
+
+var uid$1 = 0;
+
+/**
+ * A dep is an observable that can have multiple
+ * directives subscribing to it.
+ *
+ * @constructor
+ */
+
+function Dep () {
+  this.id = uid$1++;
+  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;
+var targetStack = [];
+
+function pushTarget (_target) {
+  if (Dep.target) { targetStack.push(Dep.target); }
+  Dep.target = _target;
+}
+
+function popTarget () {
+  Dep.target = targetStack.pop();
+}
+
+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
+  var subs = this.subs.slice();
+  for (var i = 0, l = subs.length; i < l; i++) {
+    subs[i].update();
+  }
+};
+
+/* eslint-disable */
+
+
+// import { pushWatcher } from './batcher'
+var 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
+ */
+
+function Watcher (vm, expOrFn, cb, options) {
+  // mix in options
+  if (options) {
+    extend(this, options);
+  }
+  var 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);
+  var 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) {
+  var 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 () {
+  var this$1 = this;
+
+  var i = this.deps.length;
+  while (i--) {
+    var dep = this$1.deps[i];
+    if (!this$1.newDepIds.has(dep.id)) {
+      dep.removeSub(this$1);
+    }
+  }
+  var 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) {
+    var 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
+      var 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 () {
+  var this$1 = this;
+
+  var i = this.deps.length;
+  while (i--) {
+    this$1.deps[i].depend();
+  }
+};
+
+/**
+ * Remove self from all dependencies' subcriber list.
+ */
+
+Watcher.prototype.teardown = function () {
+  var this$1 = this;
+
+  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);
+    }
+    var i = this.deps.length;
+    while (i--) {
+      this$1.deps[i].removeSub(this$1);
+    }
+    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
+ */
+
+var seenObjects = createNewSet(); // new Set()
+/* istanbul ignore next */
+function traverse (val, seen) {
+  var i, keys, isA, isO;
+  if (!seen) {
+    seen = seenObjects;
+    seen.clear();
+  }
+  isA = Array.isArray(val);
+  isO = isObject(val);
+  if (isA || isO) {
+    if (val.__ob__) {
+      var 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); }
+    }
+  }
+}
+
+/* eslint-disable */
+
+
+var arrayProto = Array.prototype;
+var arrayMethods = Object.create(arrayProto);[
+  'push',
+  'pop',
+  'shift',
+  'unshift',
+  'splice',
+  'sort',
+  'reverse'
+]
+.forEach(function (method) {
+  // cache original method
+  var original = arrayProto[method];
+  def(arrayMethods, method, function mutator () {
+    var arguments$1 = arguments;
+
+    // avoid leaking arguments:
+    // http://jsperf.com/closure-with-arguments
+    var i = arguments.length;
+    var args = new Array(i);
+    while (i--) {
+      args[i] = arguments$1[i];
+    }
+    var result = original.apply(this, args);
+    var ob = this.__ob__;
+    var 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) {
+    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) {
+    /* 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);
+    }
+  }
+);
+
+/* eslint-disable */
+
+
+var 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
+ */
+
+function Observer (value) {
+  this.value = value;
+  this.dep = new Dep();
+  def(value, '__ob__', this);
+  if (Array.isArray(value)) {
+    var 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) {
+  var this$1 = this;
+
+  for (var key in obj) {
+    this$1.convert(key, obj[key]);
+  }
+};
+
+/**
+ * Observe a list of Array items.
+ *
+ * @param {Array} items
+ */
+
+Observer.prototype.observeArray = function (items) {
+  for (var 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 (var i = 0, l = keys.length; i < l; i++) {
+    var 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
+ */
+
+function observe (value, vm) {
+  if (!isObject(value)) {
+    return
+  }
+  var 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
+ */
+
+function defineReactive (obj, key, val) {
+  var dep = new Dep();
+
+  var property = Object.getOwnPropertyDescriptor(obj, key);
+  if (property && property.configurable === false) {
+    return
+  }
+
+  // cater for pre-defined getter/setters
+  var getter = property && property.get;
+  var setter = property && property.set;
+
+  var childOb = observe(val);
+  Object.defineProperty(obj, key, {
+    enumerable: true,
+    configurable: true,
+    get: function reactiveGetter () {
+      var value = getter ? getter.call(obj) : val;
+      if (Dep.target) {
+        dep.depend();
+        if (childOb) {
+          childOb.dep.depend();
+        }
+        if (Array.isArray(value)) {
+          for (var e = (void 0), i = 0, l = value.length; i < l; i++) {
+            e = value[i];
+            e && e.__ob__ && e.__ob__.dep.depend();
+          }
+        }
+      }
+      return value
+    },
+    set: function reactiveSetter (newVal) {
+      var 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 */
+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
+  }
+  var ob = obj.__ob__;
+  if (!ob) {
+    obj[key] = val;
+    return
+  }
+  ob.convert(key, val);
+  ob.dep.notify();
+  if (ob.vms) {
+    var i = ob.vms.length;
+    while (i--) {
+      var 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 */
+function del (obj, key) {
+  if (!hasOwn(obj, key)) {
+    return
+  }
+  delete obj[key];
+  var ob = obj.__ob__;
+
+  if (!ob) {
+    if (obj._isVue) {
+      delete obj._data[key];
+      // obj.$forceUpdate()
+    }
+    return
+  }
+  ob.dep.notify();
+  if (ob.vms) {
+    var i = ob.vms.length;
+    while (i--) {
+      var vm = ob.vms[i];
+      unproxy(vm, key);
+      // vm.$forceUpdate()
+    }
+  }
+}
+
+var KEY_WORDS = ['$index', '$value', '$event'];
+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 */
+function unproxy (vm, key) {
+  if (!isReserved(key)) {
+    delete vm[key];
+  }
+}
+
+/* eslint-disable */
+
+
+function initState (vm) {
+  vm._watchers = [];
+  initData(vm);
+  initComputed(vm);
+  initMethods(vm);
+}
+
+function initData (vm) {
+  var data = vm._data;
+
+  if (!isPlainObject(data)) {
+    data = {};
+  }
+  // proxy data on instance
+  var keys = Object.keys(data);
+  var i = keys.length;
+  while (i--) {
+    proxy(vm, keys[i]);
+  }
+  // observe data
+  observe(data, vm);
+}
+
+/* istanbul ignore next */
+function noop () {
+}
+
+function initComputed (vm) {
+  var computed = vm._computed;
+  if (computed) {
+    for (var key in computed) {
+      var userDef = computed[key];
+      var def$$1 = {
+        enumerable: true,
+        configurable: true
+      };
+      if (typeof userDef === 'function') {
+        def$$1.get = makeComputedGetter(userDef, vm);
+        def$$1.set = noop;
+      } else {
+        def$$1.get = userDef.get
+          ? userDef.cache !== false
+            ? makeComputedGetter(userDef.get, vm)
+            : bind(userDef.get, vm)
+          : noop;
+        def$$1.set = userDef.set
+          ? bind(userDef.set, vm)
+          : noop;
+      }
+      Object.defineProperty(vm, key, def$$1);
+    }
+  }
+}
+
+function makeComputedGetter (getter, owner) {
+  var watcher = new Watcher(owner, getter, null, {
+    lazy: true
+  });
+  return function computedGetter () {
+    if (watcher.dirty) {
+      watcher.evaluate();
+    }
+    if (Dep.target) {
+      watcher.depend();
+    }
+    return watcher.value
+  }
+}
+
+function initMethods (vm) {
+  var methods = vm._methods;
+  if (methods) {
+    for (var key in methods) {
+      vm[key] = methods[key];
+    }
+  }
+}
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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()`.
+
+var config = {
+  nativeComponentMap: {
+    text: true,
+    image: true,
+    container: true,
+    slider: {
+      type: 'slider',
+      append: 'tree'
+    },
+    cell: {
+      type: 'cell',
+      append: 'tree'
+    }
+  }
+};
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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
+ */
+
+var nativeComponentMap = config.nativeComponentMap;
+
+var SETTERS = {
+  attr: 'setAttr',
+  style: 'setStyle',
+  event: 'addEvent'
+};
+
+/**
+ * apply the native component's options(specified by template.type)
+ * to the template
+ */
+function applyNaitveComponentOptions (template) {
+  var type = template.type;
+  var options = nativeComponentMap[type];
+
+  if (typeof options === 'object') {
+    for (var key in options) {
+      if (template[key] == null) {
+        template[key] = options[key];
+      }
+      else if (typof(template[key]) === 'object' &&
+        typof(options[key]) === 'object') {
+        for (var 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
+ */
+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
+ */
+function bindSubVm (vm, subVm, template, repeatItem) {
+  subVm = subVm || {};
+  template = template || {};
+
+  var options = subVm._options || {};
+
+  // bind props
+  var props = options.props;
+
+  if (Array.isArray(props)) {
+    props = props.reduce(function (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.
+ */
+function bindSubVmAfterInitialized (vm, subVm, template, target) {
+  if ( target === void 0 ) 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
+  }
+  var loop = function ( key ) {
+    if (!props || props[key]) {
+      var value = target[key];
+      if (typeof value === 'function') {
+        var returnValue = watch(vm, value, function (v) {
+          subVm[key] = v;
+        });
+        subVm[key] = returnValue;
+      }
+      else {
+        subVm[key] = value;
+      }
+    }
+  };
+
+  for (var key in target) loop( key );
+}
+
+/**
+ * Bind style from vm to sub vm and watch their updates.
+ */
+function mergeStyle (target, vm, subVm) {
+  var loop = function ( key ) {
+    var value = target[key];
+    if (typeof value === 'function') {
+      var 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);
+      }
+    }
+  };
+
+  for (var key in target) loop( key );
+}
+
+/**
+ * Bind class & style from vm to sub vm and watch their updates.
+ */
+function mergeClassStyle (target, vm, subVm) {
+  var css = vm._options && vm._options.style || {};
+
+  /* istanbul ignore if */
+  if (!subVm._rootEl) {
+    return
+  }
+
+  var className = '@originalRootEl';
+  css[className] = subVm._rootEl.classStyle;
+
+  function addClassName (list, name) {
+    if (typof(list) === 'array') {
+      list.unshift(name);
+    }
+  }
+
+  if (typeof target === 'function') {
+    var value = watch(vm, target, function (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
+ */
+function setId (vm, el, id, target) {
+  var map = Object.create(null);
+
+  Object.defineProperties(map, {
+    vm: {
+      value: target,
+      writable: false,
+      configurable: false
+    },
+    el: {
+      get: function () { return el || target._rootEl; },
+      configurable: false
+    }
+  });
+
+  if (typeof id === 'function') {
+    var handler = id;
+    id = handler.call(vm);
+    if (id || id === 0) {
+      vm._ids[id] = map;
+    }
+    watch(vm, handler, function (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) {
+  var classStyle = {};
+  var length = classList.length;
+
+  var loop = function ( i ) {
+    var style = css[classList[i]];
+    if (style) {
+      Object.keys(style).forEach(function (key) {
+        classStyle[key] = style[key];
+      });
+    }
+  };
+
+  for (var i = 0; i < length; i++) loop( i );
+  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
+  }
+
+  var style = vm._options && vm._options.style || {};
+  if (typeof classList === 'function') {
+    var value = watch(vm, classList, function (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
+  }
+  var keys = Object.keys(events);
+  var i = keys.length;
+  while (i--) {
+    var key = keys[i];
+    var 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
+  }
+  var keys = Object.keys(data);
+  var i = keys.length;
+  while (i--) {
+    var key = keys[i];
+    var 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) {
+  var methodName = SETTERS[name];
+  // watch the calc, and returns a value by calc.call()
+  var value = watch(vm, calc, function (value) {
+    function handler () {
+      el[methodName](key, value);
+    }
+    var differ = vm && vm._app && vm._app.differ;
+    if (differ) {
+      differ.append('element', el.depth, el.ref, handler);
+    }
+    else {
+      handler();
+    }
+  });
+
+  el[methodName](key, value);
+}
+
+/**
+ * watch a calc function and callback if the calc value changes
+ */
+function watch (vm, calc, callback) {
+  if (vm._static) {
+    return calc.call(vm, vm)
+  }
+  var watcher = new Watcher(vm, calc, function (value, oldValue) {
+    /* istanbul ignore if */
+    if (typeof value !== 'object' && value === oldValue) {
+      return
+    }
+    callback(value);
+  });
+
+  return watcher.value
+}
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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
+ */
+function createBody (vm, type) {
+  var doc = vm._app.doc;
+  return doc.createBody(type)
+}
+
+/**
+ * Create an element by type
+ * Using this._app.doc
+ *
+ * @param  {string} type
+ */
+function createElement (vm, type) {
+  var 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
+ */
+function createBlock (vm, element) {
+  var start = createBlockStart(vm);
+  var end = createBlockEnd(vm);
+  var blockId = lastestBlockId++;
+  if (element.element) {
+    var 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: start, end: end, element: element, blockId: blockId }
+}
+
+var lastestBlockId = 1;
+
+/**
+ * Create and return a block starter.
+ * Using this._app.doc
+ */
+function createBlockStart (vm) {
+  var doc = vm._app.doc;
+  var anchor = doc.createComment('start');
+  return anchor
+}
+
+/**
+ * Create and return a block ender.
+ * Using this._app.doc
+ */
+function createBlockEnd (vm) {
+  var doc = vm._app.doc;
+  var 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
+ */
+function attachTarget (vm, target, dest) {
+  if (dest.element) {
+    var before = dest.end;
+    var after = dest.updateMark;
+    // push new target for watch list update later
+    if (dest.children) {
+      dest.children.push(target);
+    }
+    // for check repeat case
+    if (after) {
+      var 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
+ */
+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) {
+  var 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) {
+  var parent = after.parentNode;
+
+  if (parent) {
+    var el = fragBlock.start;
+    var signal;
+    var group = [el];
+
+    while (el && el !== fragBlock.end) {
+      el = el.nextSibling;
+      group.push(el);
+    }
+
+    var temp = after;
+    group.every(function (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
+ */
+function removeTarget (vm, target, preserveBlock) {
+  if ( preserveBlock === void 0 ) 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) {
+  var 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) {
+  if ( preserveBlock === void 0 ) preserveBlock = false;
+
+  var result = [];
+  var el = fragBlock.start.nextSibling;
+
+  while (el && el !== fragBlock.end) {
+    result.push(el);
+    el = el.nextSibling;
+  }
+
+  if (!preserveBlock) {
+    removeElement(fragBlock.start);
+  }
+  result.forEach(function (el) {
+    removeElement(el);
+  });
+  if (!preserveBlock) {
+    removeElement(fragBlock.end);
+  }
+}
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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
+ */
+
+/**
+ * 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)
+ */
+function build (vm) {
+  var opt = vm._options || {};
+  var 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) {
+  var 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
+  }
+  var typeGetter = meta.type || target.type;
+  if (targetNeedCheckType(typeGetter, meta)) {
+    compileType(vm, target, dest, typeGetter, meta);
+    return
+  }
+  var type = typeGetter;
+  var 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) {
+  var 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) {
+  var fragBlock = createBlock(vm, dest);
+  target.forEach(function (child) {
+    compile(vm, child, fragBlock, meta);
+  });
+}
+
+/**
+ * Compile a target with repeat directive.
+ *
+ * @param {object} target
+ * @param {object} dest
+ */
+function compileRepeat (vm, target, dest) {
+  var repeat = target.repeat;
+  var oldStyle = typeof repeat === 'function';
+  var getter = repeat.getter || repeat.expression || repeat;
+  if (typeof getter !== 'function') {
+    getter = function () { return [] };
+  }
+  var key = repeat.key || '$index';
+  var value = repeat.value || '$value';
+  var trackBy = repeat.trackBy || target.trackBy ||
+    (target.attr && target.attr.trackBy);
+
+  var fragBlock = createBlock(vm, dest);
+  fragBlock.children = [];
+  fragBlock.data = [];
+  fragBlock.vms = [];
+
+  bindRepeat(vm, target, fragBlock, { getter: getter, key: key, value: value, 
trackBy: trackBy, oldStyle: oldStyle });
+}
+
+/**
+ * Compile a target with if directive.
+ *
+ * @param {object} target
+ * @param {object} dest
+ * @param {object} meta
+ */
+function compileShown (vm, target, dest, meta) {
+  var newMeta = { shown: true };
+  var 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) {
+  var type = typeGetter.call(vm);
+  var newMeta = extend({ type: type }, meta);
+  var fragBlock = createBlock(vm, dest);
+
+  if (dest.element && dest.children) {
+    dest.children.push(fragBlock);
+  }
+
+  watch(vm, typeGetter, function (value) {
+    var 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) {
+  var Ctor = vm.constructor;
+  var 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);
+
+  var 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
+    var binding = vm._externalBinding || {};
+    var target = binding.template;
+    var parentVm = binding.parent;
+    if (target && target.events && parentVm && element) {
+      for (var type$1 in target.events) {
+        var handler = parentVm[target.events[type$1]];
+        if (handler) {
+          element.addEvent(type$1, 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;
+  }
+
+  var treeMode = template.append === 'tree';
+  var 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) {
+  var app = vm._app || {};
+  var children = template.children;
+  if (children && children.length) {
+    children.every(function (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) {
+  var vms = fragBlock.vms;
+  var children = fragBlock.children;
+  var getter = info.getter;
+  var trackBy = info.trackBy;
+  var oldStyle = info.oldStyle;
+  var keyName = info.key;
+  var valueName = info.value;
+
+  function compileItem (item, index, context) {
+    var mergedData;
+    if (oldStyle) {
+      mergedData = item;
+      if (isObject(item)) {
+        mergedData[keyName] = index;
+        if (!mergedData.hasOwnProperty('INDEX')) {
+          Object.defineProperty(mergedData, 'INDEX', {
+            value: function () {
+              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;
+    }
+    var newContext = mergeContext(context, mergedData);
+    vms.push(newContext);
+    compile(newContext, target, fragBlock, { repeat: item });
+  }
+
+  var list = watchBlock(vm, fragBlock, getter, 'repeat',
+    function (data) {
+      console.debug('[JS Framework] the "repeat" item has changed', data);
+      if (!fragBlock || !data) {
+        return
+      }
+
+      var oldChildren = children.slice();
+      var oldVms = vms.slice();
+      var oldData = fragBlock.data.slice();
+      // 1. collect all new refs track by
+      var trackMap = {};
+      var reusedMap = {};
+      data.forEach(function (item, index) {
+        var 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
+      var reusedList = [];
+      oldData.forEach(function (item, index) {
+        var key = trackBy ? item[trackBy] : (oldStyle ? item[keyName] : index);
+        if (trackMap.hasOwnProperty(key)) {
+          reusedMap[key] = {
+            item: item, index: index, key: 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(function (item, index) {
+        var key = trackBy ? item[trackBy] : (oldStyle ? item[keyName] : index);
+        var 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(function (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) {
+  var display = watchBlock(vm, fragBlock, target.shown, 'shown',
+    function (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) {
+  var differ = vm && vm._app && vm._app.differ;
+  var config = {};
+  var depth = (fragBlock.element.depth || 0) + 1;
+
+  return watch(vm, calc, function (value) {
+    config.latestValue = value;
+    if (differ && !config.recorded) {
+      differ.append(type, depth, fragBlock.blockId, function () {
+        var 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) {
+  var newContext = Object.create(context);
+  newContext._data = mergedData;
+  initData(newContext);
+  initComputed(newContext);
+  newContext._realParent = context;
+  if (context._static) {
+    newContext._static = context._static;
+  }
+  return newContext
+}
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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;
+
+  var 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
+ */
+function $emit (type, detail) {
+  var this$1 = this;
+
+  var events = this._vmEvents;
+  var handlerList = events[type];
+  if (handlerList) {
+    var evt = new Evt(type, detail);
+    handlerList.forEach(function (handler) {
+      handler.call(this$1, evt);
+    });
+  }
+}
+
+/**
+ * Emit an event and dispatch it up.
+ * @param  {string} type
+ * @param  {any}    detail
+ */
+function $dispatch (type, detail) {
+  var 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
+ */
+function $broadcast (type, detail) {
+  var evt = new Evt(type, detail);
+  this.$emit(type, evt);
+
+  if (!evt.hasStopped() && this._childrenVms) {
+    this._childrenVms.forEach(function (subVm) {
+      subVm.$broadcast(type, evt);
+    });
+  }
+}
+
+/**
+ * Add event listener.
+ * @param  {string}   type
+ * @param  {function} handler
+ */
+function $on (type, handler) {
+  if (!type || typeof handler !== 'function') {
+    return
+  }
+  var events = this._vmEvents;
+  var 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
+ */
+function $off (type, handler) {
+  if (!type) {
+    return
+  }
+  var events = this._vmEvents;
+  if (!handler) {
+    delete events[type];
+    return
+  }
+  var handlerList = events[type];
+  if (!handlerList) {
+    return
+  }
+  handlerList.$remove(handler);
+}
+
+var 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
+ */
+function initEvents (vm, externalEvents) {
+  var options = vm._options || {};
+  var events = options.events || {};
+  for (var type1 in events) {
+    vm.$on(type1, events[type1]);
+  }
+  for (var type2 in externalEvents) {
+    vm.$on(type2, externalEvents[type2]);
+  }
+  LIFE_CYCLE_TYPES.forEach(function (type) {
+    vm.$on(("hook:" + type), options[type]);
+  });
+}
+
+/**
+ * Bind event related methods to ViewModel instance.
+ * @param  {Vm} vm
+ */
+function mixinEvents (vm) {
+  vm.$emit = $emit;
+  vm.$dispatch = $dispatch;
+  vm.$broadcast = $broadcast;
+  vm.$on = $on;
+  vm.$off = $off;
+}
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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
+ */
+
+/**
+ * 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
+ */
+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 || {};
+
+  var 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;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT 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 nativeModules = {};
+
+// for testing
+
+/**
+ * for testing
+ */
+
+
+/**
+ * for testing
+ */
+
+
+// for framework
+
+/**
+ * init modules for an app instance
+ * the second param determines whether to replace an existed method
+ */
+function initModules (modules, ifReplace) {
+  var loop = function ( moduleName ) {
+    // init `modules[moduleName][]`
+    var 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;
+      }
+    });
+  };
+
+  for (var moduleName in modules) loop( moduleName );
+}
+
+/**
+ * init app methods
+ */
+function initMethods$1 (Vm, apis) {
+  var p = Vm.prototype;
+
+  for (var apiName in apis) {
+    if (!p.hasOwnProperty(apiName)) {
+      p[apiName] = apis[apiName];
+    }
+  }
+}
+
+/**
+ * get a module of methods for an app instance
+ */
+function requireModule (app, name) {
+  var methods = nativeModules[name];
+  var target = {};
+  var loop = function ( methodName ) {
+    Object.defineProperty(target, methodName, {
+      configurable: true,
+      enumerable: true,
+      get: function moduleGetter () {
+        return function () {
+          var args = [], len = arguments.length;
+          while ( len-- ) args[ len ] = arguments[ len ];
+
+          return 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]
+          })
+        }
+      }
+    });
+  };
+
+  for (var methodName in methods) loop( methodName );
+  return target
+}
+
+/**
+ * get a custom component options
+ */
+function requireCustomComponent (app, name) {
+  var customComponentMap = app.customComponentMap;
+  return customComponentMap[name]
+}
+
+/**
+ * register a custom component options
+ */
+function registerCustomComponent (app, name, def) {
+  var customComponentMap = app.customComponentMap;
+
+  if (customComponentMap[name]) {
+    console.error(("[JS Framework] define a component(" + name + ") that 
already exists"));
+    return
+  }
+
+  customComponentMap[name] = def;
+}
+
+function createCommonjsModule(fn, module) {
+       return module = { exports: {} }, fn(module, module.exports), 
module.exports;
+}
+
+var semver = createCommonjsModule(function (module, exports) {
+exports = module.exports = SemVer;
+
+// The debug function is excluded entirely from the minified version.
+/* nomin */ var debug;
+/* nomin */ if (typeof process === 'object' &&
+    /* nomin */ process.env &&
+    /* nomin */ false &&
+    /* nomin */ /\bsemver\b/i.test(false))
+  /* nomin */ { debug = function() {
+    /* nomin */ var args = Array.prototype.slice.call(arguments, 0);
+    /* nomin */ args.unshift('SEMVER');
+    /* nomin */ console.log.apply(console, args);
+    /* nomin */ }; }
+/* nomin */ else
+  /* nomin */ { debug = function() {}; }
+
+// Note: this is the semver.org version of the spec that it implements
+// Not necessarily the package version of this code.
+exports.SEMVER_SPEC_VERSION = '2.0.0';
+
+var MAX_LENGTH = 256;
+var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;
+
+// The actual regexps go on exports.re
+var re = exports.re = [];
+var src = exports.src = [];
+var R = 0;
+
+// The following Regular Expressions can be used for tokenizing,
+// validating, and parsing SemVer version strings.
+
+// ## Numeric Identifier
+// A single `0`, or a non-zero digit followed by zero or more digits.
+
+var NUMERICIDENTIFIER = R++;
+src[NUMERICIDENTIFIER] = '0|[1-9]\\d*';
+var NUMERICIDENTIFIERLOOSE = R++;
+src[NUMERICIDENTIFIERLOOSE] = '[0-9]+';
+
+
+// ## Non-numeric Identifier
+// Zero or more digits, followed by a letter or hyphen, and then zero or
+// more letters, digits, or hyphens.
+
+var NONNUMERICIDENTIFIER = R++;
+src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*';
+
+
+// ## Main Version
+// Three dot-separated numeric identifiers.
+
+var MAINVERSION = R++;
+src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' +
+                   '(' + src[NUMERICIDENTIFIER] + ')\\.' +
+                   '(' + src[NUMERICIDENTIFIER] + ')';
+
+var MAINVERSIONLOOSE = R++;
+src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' +
+                        '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' +
+                        '(' + src[NUMERICIDENTIFIERLOOSE] + ')';
+
+// ## Pre-release Version Identifier
+// A numeric identifier, or a non-numeric identifier.
+
+var PRERELEASEIDENTIFIER = R++;
+src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] +
+                            '|' + src[NONNUMERICIDENTIFIER] + ')';
+
+var PRERELEASEIDENTIFIERLOOSE = R++;
+src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] +
+                                 '|' + src[NONNUMERICIDENTIFIER] + ')';
+
+
+// ## Pre-release Version
+// Hyphen, followed by one or more dot-separated pre-release version
+// identifiers.
+
+var PRERELEASE = R++;
+src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] +
+                  '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))';
+
+var PRERELEASELOOSE = R++;
+src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] +
+                       '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))';
+
+// ## Build Metadata Identifier
+// Any combination of digits, letters, or hyphens.
+
+var BUILDIDENTIFIER = R++;
+src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+';
+
+// ## Build Metadata
+// Plus sign, followed by one or more period-separated build metadata
+// identifiers.
+
+var BUILD = R++;
+src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] +
+             '(?:\\.' + src[BUILDIDENTIFIER] + ')*))';
+
+
+// ## Full Version String
+// A main version, followed optionally by a pre-release version and
+// build metadata.
+
+// Note that the only major, minor, patch, and pre-release sections of
+// the version string are capturing groups.  The build metadata is not a
+// capturing group, because it should not ever be used in version
+// comparison.
+
+var FULL = R++;
+var FULLPLAIN = 'v?' + src[MAINVERSION] +
+                src[PRERELEASE] + '?' +
+                src[BUILD] + '?';
+
+src[FULL] = '^' + FULLPLAIN + '$';
+
+// like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
+// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
+// common in the npm registry.
+var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] +
+                 src[PRERELEASELOOSE] + '?' +
+                 src[BUILD] + '?';
+
+var LOOSE = R++;
+src[LOOSE] = '^' + LOOSEPLAIN + '$';
+
+var GTLT = R++;
+src[GTLT] = '((?:<|>)?=?)';
+
+// Something like "2.*" or "1.2.x".
+// Note that "x.x" is a valid xRange identifer, meaning "any version"
+// Only the first item is strictly required.
+var XRANGEIDENTIFIERLOOSE = R++;
+src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*';
+var XRANGEIDENTIFIER = R++;
+src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*';
+
+var XRANGEPLAIN = R++;
+src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' +
+                   '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' +
+                   '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' +
+                   '(?:' + src[PRERELEASE] + ')?' +
+                   src[BUILD] + '?' +
+                   ')?)?';
+
+var XRANGEPLAINLOOSE = R++;
+src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' +
+                        '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' +
+                        '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' +
+                        '(?:' + src[PRERELEASELOOSE] + ')?' +
+                        src[BUILD] + '?' +
+                        ')?)?';
+
+var XRANGE = R++;
+src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$';
+var XRANGELOOSE = R++;
+src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$';
+
+// Tilde ranges.
+// Meaning is "reasonably at or greater than"
+var LONETILDE = R++;
+src[LONETILDE] = '(?:~>?)';
+
+var TILDETRIM = R++;
+src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+';
+re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g');
+var tildeTrimReplace = '$1~';
+
+var TILDE = R++;
+src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$';
+var TILDELOOSE = R++;
+src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$';
+
+// Caret ranges.
+// Meaning is "at least and backwards compatible with"
+var LONECARET = R++;
+src[LONECARET] = '(?:\\^)';
+
+var CARETTRIM = R++;
+src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+';
+re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g');
+var caretTrimReplace = '$1^';
+
+var CARET = R++;
+src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$';
+var CARETLOOSE = R++;
+src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$';
+
+// A simple gt/lt/eq thing, or just "" to indicate "any version"
+var COMPARATORLOOSE = R++;
+src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$';
+var COMPARATOR = R++;
+src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$';
+
+
+// An expression to strip any whitespace between the gtlt and the thing
+// it modifies, so that `> 1.2.3` ==> `>1.2.3`
+var COMPARATORTRIM = R++;
+src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] +
+                      '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')';
+
+// this one has to use the /g flag
+re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g');
+var comparatorTrimReplace = '$1$2$3';
+
+
+// Something like `1.2.3 - 1.2.4`
+// Note that these all use the loose form, because they'll be
+// checked against either the strict or loose comparator form
+// later.
+var HYPHENRANGE = R++;
+src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' +
+                   '\\s+-\\s+' +
+                   '(' + src[XRANGEPLAIN] + ')' +
+                   '\\s*$';
+
+var HYPHENRANGELOOSE = R++;
+src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' +
+                        '\\s+-\\s+' +
+                        '(' + src[XRANGEPLAINLOOSE] + ')' +
+                        '\\s*$';
+
+// Star ranges basically just allow anything at all.
+var STAR = R++;
+src[STAR] = '(<|>)?=?\\s*\\*';
+
+// Compile to actual regexp objects.
+// All are flag-free, unless they were created above with a flag.
+for (var i = 0; i < R; i++) {
+  debug(i, src[i]);
+  if (!re[i])
+    { re[i] = new RegExp(src[i]); }
+}
+
+exports.parse = parse;
+function parse(version, loose) {
+  if (version instanceof SemVer)
+    { return version; }
+
+  if (typeof version !== 'string')
+    { return null; }
+
+  if (version.length > MAX_LENGTH)
+    { return null; }
+
+  var r = loose ? re[LOOSE] : re[FULL];
+  if (!r.test(version))
+    { return null; }
+
+  try {
+    return new SemVer(version, loose);
+  } catch (er) {
+    return null;
+  }
+}
+
+exports.valid = valid;
+function valid(version, loose) {
+  var v = parse(version, loose);
+  return v ? v.version : null;
+}
+
+
+exports.clean = clean;
+function clean(version, loose) {
+  var s = parse(version.trim().replace(/^[=v]+/, ''), loose);
+  return s ? s.version : null;
+}
+
+exports.SemVer = SemVer;
+
+function SemVer(version, loose) {
+  if (version instanceof SemVer) {
+    if (version.loose === loose)
+      { return version; }
+    else
+      { version = version.version; }
+  } else if (typeof version !== 'string') {
+    throw new TypeError('Invalid Version: ' + version);
+  }
+
+  if (version.length > MAX_LENGTH)
+    { throw new TypeError('version is longer than ' + MAX_LENGTH + ' 
characters') }
+
+  if (!(this instanceof SemVer))
+    { return new SemVer(version, loose); }
+
+  debug('SemVer', version, loose);
+  this.loose = loose;
+  var m = version.trim().match(loose ? re[LOOSE] : re[FULL]);
+
+  if (!m)
+    { throw new TypeError('Invalid Version: ' + version); }
+
+  this.raw = version;
+
+  // these are actually numbers
+  this.major = +m[1];
+  this.minor = +m[2];
+  this.patch = +m[3];
+
+  if (this.major > MAX_SAFE_INTEGER || this.major < 0)
+    { throw new TypeError('Invalid major version') }
+
+  if (this.minor > MAX_SAFE_INTEGER || this.minor < 0)
+    { throw new TypeError('Invalid minor version') }
+
+  if (this.patch > MAX_SAFE_INTEGER || this.patch < 0)
+    { throw new TypeError('Invalid patch version') }
+
+  // numberify any prerelease numeric ids
+  if (!m[4])
+    { this.prerelease = []; }
+  else
+    { this.prerelease = m[4].split('.').map(function(id) {
+      if (/^[0-9]+$/.test(id)) {
+        var num = +id;
+        if (num >= 0 && num < MAX_SAFE_INTEGER)
+          { return num; }
+      }
+      return id;
+    }); }
+
+  this.build = m[5] ? m[5].split('.') : [];
+  this.format();
+}
+
+SemVer.prototype.format = function() {
+  this.version = this.major + '.' + this.minor + '.' + this.patch;
+  if (this.prerelease.length)
+    { this.version += '-' + this.prerelease.join('.'); }
+  return this.version;
+};
+
+SemVer.prototype.toString = function() {
+  return this.version;
+};
+
+SemVer.prototype.compare = function(other) {
+  debug('SemVer.compare', this.version, this.loose, other);
+  if (!(other instanceof SemVer))
+    { other = new SemVer(other, this.loose); }
+
+  return this.compareMain(other) || this.comparePre(other);
+};
+
+SemVer.prototype.compareMain = function(other) {
+  if (!(other instanceof SemVer))
+    { other = new SemVer(other, this.loose); }
+
+  return compareIdentifiers(this.major, other.major) ||
+         compareIdentifiers(this.minor, other.minor) ||
+         compareIdentifiers(this.patch, other.patch);
+};
+
+SemVer.prototype.comparePre = function(other) {
+  var this$1 = this;
+
+  if (!(other instanceof SemVer))
+    { other = new SemVer(other, this.loose); }
+
+  // NOT having a prerelease is > having one
+  if (this.prerelease.length && !other.prerelease.length)
+    { return -1; }
+  else if (!this.prerelease.length && other.prerelease.length)
+    { return 1; }
+  else if (!this.prerelease.length && !other.prerelease.length)
+    { return 0; }
+
+  var i = 0;
+  do {
+    var a = this$1.prerelease[i];
+    var b = other.prerelease[i];
+    debug('prerelease compare', i, a, b);
+    if (a === undefined && b === undefined)
+      { return 0; }
+    else if (b === undefined)
+      { return 1; }
+    else if (a === undefined)
+      { return -1; }
+    else if (a === b)
+      { continue; }
+    else
+      { return compareIdentifiers(a, b); }
+  } while (++i);
+};
+
+// preminor will bump the version up to the next minor release, and immediately
+// down to pre-release. premajor and prepatch work the same way.
+SemVer.prototype.inc = function(release, identifier) {
+  var this$1 = this;
+
+  switch (release) {
+    case 'premajor':
+      this.prerelease.length = 0;
+      this.patch = 0;
+      this.minor = 0;
+      this.major++;
+      this.inc('pre', identifier);
+      break;
+    case 'preminor':
+      this.prerelease.length = 0;
+      this.patch = 0;
+      this.minor++;
+      this.inc('pre', identifier);
+      break;
+    case 'prepatch':
+      // If this is already a prerelease, it will bump to the next version
+      // drop any prereleases that might already exist, since they are not
+      // relevant at this point.
+      this.prerelease.length = 0;
+      this.inc('patch', identifier);
+      this.inc('pre', identifier);
+      

<TRUNCATED>

Reply via email to