/*!
 * mvvm.js v1.4.2 (c) 2017 TANG
 * Released under the MIT license
 * Tue Nov 07 2017 20:13:59 GMT+0800 (CST)
 */
(function (global, factory) {
  typeof exports === "object" && typeof module !== "undefined"
    ? (module.exports = factory())
    : typeof define === "function" && define.amd
    ? define(factory)
    : (global.binder = factory());

  if (typeof exports === "object" && typeof module !== "undefined") {
    module.exports = factory();
  } else if (typeof define === "function" && define.amd) {
    define(factory);
  } else if (window.layui && layui.define) {
    // layui加载
    layui.define(function (exports) {
      var MOD_NAME = "binder";
      exports(MOD_NAME, factory());
    });
  } else {
    global.binder = factory();
  }
})(this, function () {
  "use strict";

  var OP = Object.prototype;
  var has = OP.hasOwnProperty;

  /**
   * typeof 类型检测
   * @param   {Mix}      test
   * @param   {String}   type
   * @return  {Boolean}
   */
  function typeOf(test, type) {
    return typeof test === type;
  }

  /**
   * 是否是对象
   */
  function isObject(object) {
    return OP.toString.call(object) === "[object Object]";
  }

  /**
   * 是否是数组
   */
  function isArray(array) {
    return Array.isArray(array);
  }

  /**
   * 是否是函数
   */
  function isFunc(func) {
    return typeOf(func, "function");
  }

  /**
   * 是否是字符串
   */
  function isString(str) {
    return typeOf(str, "string");
  }

  /**
   * 是否是布尔值
   */
  function isBool(bool) {
    return typeOf(bool, "boolean");
  }

  /**
   * 是否是数字
   */
  function isNumber(num) {
    return typeOf(num, "number") && !isNaN(num);
  }

  /**
   * 是否是纯粹对象
   */
  function isPlainObject(object) {
    if (
      !object ||
      !isObject(object) ||
      object.nodeType ||
      object === object.window
    ) {
      return false;
    }

    if (
      object.constructor &&
      !has.call(object.constructor.prototype, "isPrototypeOf")
    ) {
      return false;
    }

    return true;
  }

  /**
   * 是否是空对象
   * @param   {Object}   object
   * @return  {Boolean}
   */
  function isEmptyObject(object) {
    return Object.keys(object).length === 0;
  }

  /**
   * 将 value 转化为字符串
   * undefined 和 null 都转成空字符串
   * @param   {Mix}     value
   * @return  {String}
   */
  function _toString(value) {
    return value == null ? "" : value.toString();
  }

  /**
   * value 转成 Number 类型
   * 如转换失败原样返回
   * @param   {String|Mix}  value
   * @return  {Number|Mix}
   */
  function toNumber(value) {
    if (isString(value)) {
      var val = Number(value);
      return isNumber(val) ? val : value;
    } else {
      return value;
    }
  }

  /**
   * 空操作函数
   */
  function noop() {}

  var cons = window.console;

  /**
   * 打印警告信息
   */
  /* istanbul ignore next */
  function warn() {
    if (cons) {
      cons.warn.apply(cons, arguments);
    }
  }

  /**
   * 打印错误信息
   */
  /* istanbul ignore next */
  function error() {
    if (cons) {
      cons.error.apply(cons, arguments);
    }
  }

  /*
   * 对象自有属性检测
   */
  function hasOwn(obj, key) {
    return obj && has.call(obj, key);
  }

  /**
   * object 定义或修改 property 属性
   * @param  {Object}   object      [对象]
   * @param  {String}   property    [属性字段]
   * @param  {Mix}      value       [属性的修改值/新值]
   * @param  {Boolean}  enumerable  [属性是否出现在枚举中]
   */
  function def(object, property, value, enumerable) {
    return Object.defineProperty(object, property, {
      value: value,
      writable: true,
      enumerable: !!enumerable,
      configurable: true,
    });
  }

  /**
   * 遍历数组或对象，提供删除选项和退出遍历的功能
   * @param  {Array|Object}  iterator  [数组或对象]
   * @param  {Fuction}       callback  [回调函数]
   * @param  {Object}        context   [作用域]
   */
  function each(iterator, callback, context) {
    var i, ret;

    if (!context) {
      context = this;
    }

    // 数组
    if (isArray(iterator)) {
      for (i = 0; i < iterator.length; i++) {
        ret = callback.call(context, iterator[i], i, iterator);

        // 回调返回 false 退出循环
        if (ret === false) {
          break;
        }

        // 回调返回 null 从原数组删除当前选项
        if (ret === null) {
          iterator.splice(i, 1);
          i--;
        }
      }
    } else if (isObject(iterator)) {
      var keys = Object.keys(iterator);

      for (i = 0; i < keys.length; i++) {
        var key = keys[i];

        ret = callback.call(context, iterator[key], key, iterator);

        // 回调返回 false 退出循环
        if (ret === false) {
          break;
        }

        // 回调返回 null 从原对象删除当前选项
        if (ret === null) {
          delete iterator[key];
        }
      }
    }
  }

  /**
   * 扩展合并对象
   */
  /* istanbul ignore next */
  function extend() {
    var arguments$1 = arguments;

    var options, name, src, copy, copyIsArray, clone;
    var target = arguments[0] || {},
      i = 1,
      length = arguments.length,
      deep = false;

    // Handle a deep copy situation
    if (isBool(target)) {
      deep = target;
      target = arguments[i] || {};
      i++;
    }

    // Handle case when target is a string or something (possible in deep copy)
    if (typeof target !== "object" && !isFunc(target)) {
      target = {};
    }

    // Extend Util itself if only one argument is passed
    if (i === length) {
      target = this;
      i--;
    }

    for (; i < length; i++) {
      // Only deal with non-null/undefined values
      if ((options = arguments$1[i]) != null) {
        // Extend the base object
        for (name in options) {
          src = target[name];
          copy = options[name];

          // Prevent never-ending loop
          if (target === copy) {
            continue;
          }

          // Recurse if we're merging plain objects or arrays
          if (
            deep &&
            copy &&
            (isPlainObject(copy) || (copyIsArray = isArray(copy)))
          ) {
            if (copyIsArray) {
              copyIsArray = false;
              clone = src && isArray(src) ? src : [];
            } else {
              clone = src && isPlainObject(src) ? src : {};
            }

            // Never move original objects, clone them
            target[name] = extend(deep, clone, copy);
          }
          // Don't bring in undefined values
          else if (copy !== undefined) {
            target[name] = copy;
          }
        }
      }
    }

    // Return the modified object
    return target;
  }

  /**
   * 复制对象或数组，其他类型原样返回
   * @param   {Object|Array}  target
   * @return  {Mix}
   */
  function copy(target) {
    var ret;

    if (isArray(target)) {
      ret = extend(true, [], target);
    } else if (isObject(target)) {
      ret = extend(true, {}, target);
    }

    return ret || target;
  }

  /**
   * 返回一个空文档碎片
   * @return  {Fragment}
   */
  function createFragment() {
    return document.createDocumentFragment();
  }

  /**
   * element 的子节点转换成文档片段（element 将会被清空）
   * @param  {Element}  element
   */
  function nodeToFragment(element) {
    var child;
    var fragment = createFragment();

    while ((child = element.firstChild)) {
      fragment.appendChild(child);
    }

    return fragment;
  }

  /**
   * 去掉字符串中所有空格
   * @param   {String}  string
   * @return  {String}
   */
  var regSpaceAll = /\s/g;
  function removeSpace(string) {
    return string.replace(regSpaceAll, "");
  }

  /**
   * 设置/读取数据配置对象
   * @param   {Object}   data   [配置对象]
   * @param   {String}   name   [配置名称, 支持/分隔层次]
   * @param   {Mix}      value  [不传为读取配置信息]
   * @return  {Mix}             [返回读取的配置值]
   */
  function config(data, name, value) {
    if (name) {
      var ns = name.split(".");
      while (ns.length > 1 && hasOwn(data, ns[0])) {
        data = data[ns.shift()];
      }
      name = ns[0];
    } else {
      return data;
    }

    if (typeof value !== "undefined") {
      data[name] = value;
      return;
    } else {
      return data[name];
    }
  }

  var guid = 0;

  /**
   * 依赖收集模块
   * @param  {String}  path  [数据路径]
   */
  function Depend(path) {
    this.path = path;
    this.watchers = [];
    this.guid = guid++;
  }

  /**
   * 当前收集依赖的订阅模块 watcher
   * @type  {Object}
   */
  Depend.watcher = null;

  var dp = Depend.prototype;

  /**
   * 添加依赖订阅
   * @param  {Object}  watcher
   */
  dp.addWatcher = function (watcher) {
    this.watchers.push(watcher);
  };

  /**
   * 移除依赖订阅
   * @param  {Object}  watcher
   */
  dp.removeWatcher = function (watcher) {
    this.watchers.$remove(watcher);
  };

  /**
   * 为 watcher 收集当前的依赖
   */
  dp.depend = function () {
    if (Depend.watcher) {
      Depend.watcher.addDepend(this);
    }
  };

  /**
   * 依赖变更前调用方法，用于旧数据的缓存处理
   */
  dp.beforeNotify = function () {
    each(this.watchers, function (watcher) {
      watcher.beforeUpdate();
    });
  };

  /**
   * 依赖变更，通知每一个订阅了该依赖的 watcher
   * @param  {Object}  args  [数组操作参数信息]
   */
  dp.notify = function (args) {
    each(
      this.watchers,
      function (watcher) {
        watcher.update(args, this);
      },
      this
    );
  };

  var INIT = 0;
  var IDENT = 1;
  var QUOTE = 2;
  var OTHER = 1;

  /**
   * 保留取词
   * @param  {String}  value
   */
  function ident(value) {
    return value;
  }

  /**
   * 舍弃取词
   * @param  {String}  value
   */
  function quote(value) {
    return "";
  }

  var convert = [0, ident, quote, ident];

  /**
   * 获取字符类型
   * 这里只定义了普通取词的分割法
   * @param   {String}  cha
   * @return  {Number}
   */
  function getState(cha) {
    var code = cha.charCodeAt(0);

    // a-z A-Z 0-9
    if (
      (code >= 65 && code <= 90) ||
      (code >= 97 && code <= 122) ||
      (code >= 48 && code <= 57)
    ) {
      return IDENT;
    }

    switch (code) {
      case 91: // [
      case 93: // ]
      case 46: // .
      case 34: // "
      case 39: // '
        return QUOTE;
      default:
        return OTHER;
    }
  }

  /**
   * 取词状态机
   * @type  {Object}
   */
  var StateMachine = {
    /**
     * 初始状态设定
     */
    init: function (state) {
      this.saves = "";
      this.state = state;
    },

    /**
     * 设置状态并返回当前取词
     * @param  {Number}  state
     * @param  {String}  value
     */
    set: function (state, value) {
      var ref = this.get(state);
      var keepIdent = ref.keepIdent;
      var tobeQuote = ref.tobeQuote;
      var tobeIdent = ref.tobeIdent;

      if (keepIdent) {
        this.save(state, value);
      } else if (tobeQuote) {
        var saves = this.saves;
        this.saves = "";
        this.change(state);
        return saves;
      } else if (tobeIdent) {
        this.save(state, value);
        this.change(state);
      }
    },

    /**
     * 获取状态变更类型
     * @param   {Number}  toBecome  [将要转换的状态]
     * @return  {Object}            [状态类型对象]
     */
    get: function (toBecome) {
      var current = this.state;
      var keepIdent = current === IDENT && toBecome === IDENT;
      var tobeQuote =
        (current === IDENT || current === INIT) && toBecome === QUOTE;
      var keepQuote = current === QUOTE && toBecome === QUOTE;
      var tobeIdent =
        (current === QUOTE || current === INIT) && toBecome === IDENT;
      return {
        keepIdent: keepIdent,
        tobeQuote: tobeQuote,
        keepQuote: keepQuote,
        tobeIdent: tobeIdent,
      };
    },

    /**
     * 更改状态
     * @param  {Number}  state
     */
    change: function (state) {
      this.state = state;
    },

    /**
     * 缓存当前字符
     */
    save: function (state, value) {
      this.saves += convert[state](value);
    },

    /**
     * 重置状态
     */
    reset: function () {
      this.saves = "";
      this.state = INIT;
    },
  };

  /**
   * 将字符表达式解析成访问路径
   * @param   {String}  expression
   * @return  {Array}
   */
  function parseToPath(expression) {
    var paths = [];
    var letters = expression.split("");
    var lastIndex = letters.length - 1;
    var firstState = getState(letters[0]);

    StateMachine.init(firstState);
    each(letters, function (letter, index) {
      var state = getState(letter);
      var word = StateMachine.set(state, letter);
      if (word) {
        paths.push(word);
      }

      // 解析结束
      if (index === lastIndex && StateMachine.saves) {
        paths.push(StateMachine.saves);
        StateMachine.reset();
      }
    });

    return paths;
  }

  /**
   * 通过访问层级取值
   * @param   {Object}  target
   * @param   {Array}   paths
   * @return  {Object}
   */
  function getDeepValue(target, paths) {
    while (paths.length) {
      target = target[paths.shift()];
    }
    return target;
  }

  /**
   * 生成访问路径数组
   * @param   {String}  expression
   * @return  {Array}
   */
  function createPath(expression) {
    return parseToPath(removeSpace(expression));
  }

  /**
   * 根据访问路径设置对象指定字段值
   * @param  {Object}  scope
   * @param  {Mix}     value
   * @param  {Array}   paths
   */
  function setValueByPath(scope, value, paths) {
    var copyPaths = copy(paths);
    var set = copyPaths.pop();
    var data = getDeepValue(scope, copyPaths);
    if (isObject(data)) {
      data[set] = value;
    }
  }

  // 匹配常量缓存序号 "1"
  var regSaveConst = /"(\d+)"/g;
  // 只含有 true 或 false
  var regBool = /^(true|false)$/;
  // 匹配表达式中的常量
  var regReplaceConst = /[\{,]\s*[\w\$_]+\s*:|('[^']*'|"[^"]*")|typeof /g;
  // 匹配表达式中的取值域
  var regReplaceScope =
    /[^\w$\.]([A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\])*)/g;
  // 匹配常规取值: item or item['x'] or item["y"] or item[0]
  var regNormal =
    /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\]|\[[A-Za-z_$][\w$]*\])*$/;

  // 表达式中允许的关键字
  var allowKeywords =
    "JSON.Math.parseInt.parseFloat.Date.this.true.false.null.undefined.Infinity.NaN." +
    "isNaN.isFinite.decodeURI.decodeURIComponent.encodeURI.encodeURIComponent";
  var regAllowKeyword = new RegExp(
    "^(" + allowKeywords.replace(/\./g, "\\b|") + "\\b)"
  );

  // 表达式中禁止的关键字
  var avoidKeywords =
    "var.const.let.if.else.for.in.continue.switch.case.break.default.function.return." +
    "do.while.delete.try.catch.throw.finally.with.import.export.instanceof.yield.await";
  var regAviodKeyword = new RegExp(
    "^(" + avoidKeywords.replace(/\./g, "\\b|") + "\\b)"
  );

  // 保存常量，返回序号 "i"
  var consts = [];
  function saveConst(string) {
    var i = consts.length;
    consts[i] = string;
    return '"' + i + '"';
  }

  /**
   * 返回替换之前的常量
   * @param   {Strinf}  string
   * @param   {Number}  i
   * @return  {String}
   */
  function returnConst(string, i) {
    return consts[i];
  }

  /**
   * 返回变量/单词的 scope 替换
   * @param   {String}  string
   * @return  {String}
   */
  function replaceScope(string) {
    var pad = string.charAt(0);
    var path = string.slice(1);

    if (regAllowKeyword.test(path) || regAviodKeyword.test(path)) {
      return string;
    } else {
      path =
        path.indexOf('"') > -1 ? path.replace(regSaveConst, returnConst) : path;
      return pad + "scope." + path;
    }
  }

  /**
   * 是否是常规指令表达式
   * @param   {String}   expression
   * @return  {Boolean}
   */
  function isNormal(expression) {
    return (
      regNormal.test(expression) &&
      !regBool.test(expression) &&
      expression.indexOf("Math.") !== 0
    );
  }

  /**
   * 表达式变量添加 scope
   * @return  {String}
   */
  function addScope(expression) {
    if (isNormal(expression)) {
      return "scope." + expression;
    }

    expression = (" " + expression).replace(regReplaceConst, saveConst);
    expression = expression.replace(regReplaceScope, replaceScope);
    expression = expression.replace(regSaveConst, returnConst);

    return expression;
  }

  /**
   * 生成表达式取值函数
   * @param   {String}    expression
   * @return  {Function}
   */
  function createGetter(expression) {
    if (regAviodKeyword.test(expression)) {
      warn("Avoid using unallow keyword in expression [" + expression + "]");
      return noop;
    }

    try {
      return new Function("scope", "return " + addScope(expression) + ";");
    } catch (e) {
      error("Invalid generated expression: [" + expression + "]");
      return noop;
    }
  }

  /**
   * 生成表达式设值函数
   * @param  {String}  expression
   */
  function createSetter(expression) {
    var paths = createPath(expression);
    if (paths.length) {
      return function setter(scope, value) {
        setValueByPath(scope, value, paths);
      };
    } else {
      error("Invalid setter expression [" + expression + "]");
      return noop;
    }
  }

  /**
   * 遍历对象/数组每一个可枚举属性
   * @param  {Object|Array}  target  [遍历值/对象或数组]
   * @param  {Boolean}       root    [是否是根对象/数组]
   */
  var walkedObs = [];
  function walkThrough(target, root) {
    var ob = target && target.__ob__;
    var guid = ob && ob.dep.guid;

    if (guid) {
      if (walkedObs.indexOf(guid) > -1) {
        return;
      } else {
        walkedObs.push(guid);
      }
    }

    each(target, function (value) {
      walkThrough(value, false);
    });

    if (root) {
      walkedObs.length = 0;
    }
  }

  /**
   * 数据订阅模块
   * @param  {Object}    vm
   * @param  {Object}    desc
   * @param  {Function}  callback
   * @param  {Object}    context
   */
  function Watcher(vm, desc, callback, context) {
    this.vm = vm;
    extend(this, desc);
    this.callback = callback;
    this.context = context || this;

    // 依赖 id 缓存
    this.depIds = [];
    this.newDepIds = [];
    this.shallowIds = [];
    // 依赖实例缓存
    this.depends = [];
    this.newDepends = [];

    var once = desc.once;
    var expression = desc.expression;
    var preSetFunc = isFunc(expression);

    // 缓存取值函数
    this.getter = preSetFunc ? expression : createGetter(expression);
    // 缓存设值函数（双向数据绑定）
    this.setter = !once && desc.duplex ? createSetter(expression) : null;

    // 缓存表达式旧值
    this.oldVal = null;
    // 表达式初始值 & 提取依赖
    this.value = once ? this.getValue() : this.get();
  }

  var wp = Watcher.prototype;

  /**
   * 获取取值域
   * @return  {Object}
   */
  wp.getScope = function () {
    return this.context.scope || this.vm.$data;
  };

  /**
   * 获取表达式的取值
   */
  wp.getValue = function () {
    var scope = this.getScope();
    return this.getter.call(scope, scope);
  };

  /**
   * 设置订阅数据的值
   * @param  {Mix}  value
   */
  wp.setValue = function (value) {
    var scope = this.getScope();
    if (this.setter) {
      this.setter.call(scope, scope, value);
    }
  };

  /**
   * 获取表达式的取值 & 提取依赖
   */
  wp.get = function () {
    this.beforeGet();

    var value = this.getValue();

    // 深层依赖获取
    if (this.deep) {
      // 先缓存浅依赖的 ids
      this.shallowIds = copy(this.newDepIds);
      walkThrough(value, true);
    }

    this.afterGet();

    return value;
  };

  /**
   * 设置当前依赖对象
   */
  wp.beforeGet = function () {
    Depend.watcher = this;
  };

  /**
   * 将依赖订阅到该 watcher
   */
  wp.addDepend = function (depend) {
    var guid = depend.guid;
    var newIds = this.newDepIds;

    if (newIds.indexOf(guid) < 0) {
      newIds.push(guid);
      this.newDepends.push(depend);
      if (this.depIds.indexOf(guid) < 0) {
        depend.addWatcher(this);
      }
    }
  };

  /**
   * 移除订阅的依赖监测
   * @param  {Function}  filter
   */
  wp.removeDepends = function (filter) {
    each(
      this.depends,
      function (depend) {
        if (filter) {
          if (filter.call(this, depend)) {
            depend.removeWatcher(this);
          }
        } else {
          depend.removeWatcher(this);
        }
      },
      this
    );
  };

  /**
   * 更新/解除依赖挂载
   */
  wp.afterGet = function () {
    Depend.watcher = null;

    // 清除无用的依赖
    this.removeDepends(function (depend) {
      return this.newDepIds.indexOf(depend.guid) < 0;
    });

    // 重设依赖缓存
    this.depIds = copy(this.newDepIds);
    this.newDepIds.length = 0;

    this.depends = copy(this.newDepends);
    this.newDepends.length = 0;
  };

  /**
   * 更新前调用方法
   * 用于旧值的缓存处理，对象或数组只存副本
   */
  wp.beforeUpdate = function () {
    this.oldVal = copy(this.value);
  };

  /**
   * 依赖变化，更新取值
   * @param  {Object}  args    [数组操作参数信息]
   * @param  {Object}  depend  [变更依赖对象 id]
   */
  wp.update = function (args, depend) {
    var oldVal = this.oldVal;
    var unifyCb = this.vm.$unifyCb;
    var newVal = (this.value = this.get());

    // 多维数组的情况下判断数组操作是否为源数组所发出的
    var source = args && args.source;
    if (source && source !== newVal && this.directive === "v-for") {
      return;
    }

    var callback = this.callback;
    if (callback && oldVal !== newVal) {
      callback.call(
        this.context,
        newVal,
        oldVal,
        this.deep && this.shallowIds.indexOf(depend.guid) < 0,
        args
      );
      if (source) {
        args.source = null;
      }
    }

    // 通知统一监听回调
    if (unifyCb) {
      unifyCb({ action: args, path: depend.path }, newVal, oldVal);
    }
  };

  /**
   * 销毁函数
   */
  wp.destroy = function () {
    this.value = null;
    this.removeDepends();
    this.getter = this.setter = null;
    this.vm = this.callback = this.context = null;
  };

  /**
   * 指令通用模块
   * 提供生成数据订阅和变化更新功能
   * @param  {Object}   parser  [指令解析模块实例]
   */
  function Directive(parser) {
    this.parser = parser;
    this.scope = parser.scope;
  }

  var dp$1 = Directive.prototype;

  /**
   * 安装/解析指令，订阅数据、更新视图
   */
  dp$1.mount = function () {
    var parser = this.parser;
    // 生成数据订阅实例
    var watcher = (this.watcher = new Watcher(
      parser.vm,
      parser.desc,
      this.update,
      this
    ));
    // 更新初始视图
    this.update(watcher.value);
  };

  /**
   * 销毁/卸载指令
   */
  dp$1.destroy = function () {
    this.watcher.destroy();
    this.parser = this.scope = null;
  };

  /**
   * 更新指令视图
   * @param   {Mix}      newVal     [依赖数据新值]
   * @param   {Mix}      oldVal     [依赖数据旧值]
   * @param   {Boolean}  fromDeep   [数组内部更新]
   * @param   {Object}   methodArg  [数组操作参数信息]
   */
  dp$1.update = function () {
    var parser = this.parser;
    parser.update.apply(parser, arguments);
  };

  /**
   * 获取依赖数据当前值
   * @return  {Mix}
   */
  dp$1.get = function () {
    return this.watcher.value;
  };

  /**
   * 设置依赖数据的值（用于双向数据绑定）
   * @param  {Mix}  value
   */
  dp$1.set = function (value) {
    this.watcher.setValue(value);
  };

  /**
   * Parser 基础指令解析器模块
   * @param  {Object}   vm
   * @param  {Element}  node
   * @param  {Object}   desc
   * @param  {Object}   scope
   */
  function Parser(vm, node, desc, scope) {
    // 数据缓存
    this.vm = vm;
    this.el = node;
    this.desc = desc;
    this.scope = scope;

    // 解析指令
    this.parse();
  }

  var pp = Parser.prototype;

  /**
   * 安装指令实例
   */
  pp.bind = function () {
    this.directive = new Directive(this);
    this.directive.mount();
  };

  /**
   * 指令销毁函数
   */
  pp.destroy = function () {
    var directive = this.directive;

    // 有些指令没有实例化 Directive
    // 所以需要调用额外定义的销毁函数
    if (directive) {
      directive.destroy();
    } else if (isFunc(this._destroy)) {
      this._destroy();
    }

    this.vm = this.el = this.desc = this.scope = null;
  };

  /**
   * 解析器模块的类式继承
   * @param   {Function}   PostParser
   * @return  {Prototype}
   */
  function linkParser(PostParser) {
    return (PostParser.prototype = Object.create(Parser.prototype));
  }

  /**
   * 是否是元素节点
   * @param   {Element}  element
   * @return  {Boolean}
   */
  function isElement(element) {
    return element.nodeType === 1;
  }

  /**
   * 是否是文本节点
   * @param   {Element}  element
   * @return  {Boolean}
   */
  function isTextNode(element) {
    return element.nodeType === 3;
  }

  /**
   * 清空 element 的所有子节点
   * @param  {Element}  element
   */
  function empty(element) {
    while (element.firstChild) {
      element.removeChild(element.firstChild);
    }
    return element;
  }

  /**
   * 获取节点属性值
   * @param   {Element}  node
   * @param   {String}   name
   * @return  {String}
   */
  function getAttr(node, name) {
    return node.getAttribute(name) || "";
  }

  /**
   * 移除节点属性
   * @param  {Element}  node
   * @param  {String}   name
   */
  function removeAttr(node, name) {
    node.removeAttribute(name);
  }

  /**
   * 设置节点属性
   * @param  {Element}  node
   * @param  {String}   name
   * @param  {String}   value
   */
  function setAttr(node, name, value) {
    // 设为 null/undefined 和 false 移除该属性
    if (value == null || value === false) {
      return removeAttr(node, name);
    }

    if (value === true) {
      node[name] = value;

      // 有些浏览器/情况下用 node[name] = true
      // 是无法添加自定义属性的，此时设置一个空字符串
      if (!hasAttr(node, name)) {
        node.setAttribute(name, "");
      }
    } else if (value !== getAttr(node, name)) {
      node.setAttribute(name, value);
    }
  }

  /**
   * 判断节点是否存在属性
   * @param   {Element}  node
   * @param   {String}   name
   * @return  {Boolean}
   */
  function hasAttr(node, name) {
    return node.hasAttribute(name);
  }

  /**
   * 节点是否存在 classname
   * @param   {Element}  node
   * @param   {String}   classname
   * @return  {Boolean}
   */
  function hasClass(node, classname) {
    var current,
      list = node.classList;

    /* istanbul ignore else */
    if (list) {
      return list.contains(classname);
    } else {
      current = " " + getAttr(node, "class") + " ";
      return current.indexOf(" " + classname + " ") > -1;
    }
  }

  /**
   * 节点添加 classname
   * @param  {Element}  node
   * @param  {String}   classname
   */
  function addClass(node, classname) {
    var current,
      list = node.classList;

    if (!classname || hasClass(node, classname)) {
      return;
    }

    /* istanbul ignore else */
    if (list) {
      list.add(classname);
    } else {
      current = " " + getAttr(node, "class") + " ";

      if (current.indexOf(" " + classname + " ") === -1) {
        setAttr(node, "class", (current + classname).trim());
      }
    }
  }

  /**
   * 节点删除 classname
   * @param  {Element}  node
   * @param  {String}   classname
   */
  function removeClass(node, classname) {
    var current,
      target,
      list = node.classList;

    if (!classname || !hasClass(node, classname)) {
      return;
    }

    /* istanbul ignore else */
    if (list) {
      list.remove(classname);
    } else {
      target = " " + classname + " ";
      current = " " + getAttr(node, "class") + " ";

      while (current.indexOf(target) > -1) {
        current = current.replace(target, " ");
      }

      setAttr(node, "class", current.trim());
    }

    if (!node.className) {
      removeAttr(node, "class");
    }
  }

  /**
   * 节点事件绑定
   * @param  {Element}   node
   * @param  {String}    evt
   * @param  {Function}  callback
   * @param  {Boolean}   capture
   */
  function addEvent(node, evt, callback, capture) {
    node.addEventListener(evt, callback, capture);
  }

  /**
   * 解除节点事件绑定
   * @param  {Element}   node
   * @param  {String}    evt
   * @param  {Function}  callback
   * @param  {Boolean}   capture
   */
  function removeEvent(node, evt, callback, capture) {
    node.removeEventListener(evt, callback, capture);
  }

  /**
   * 获取事件修饰符对象
   * 支持 6 种事件修饰符
   * .self .stop .prevent .capture .keyCode .one
   * @param  {String}  type
   * @param  {String}  modifier
   */
  function getModifiers(type, modifier) {
    var modifiers = modifier.split(".");

    var self = modifiers.indexOf("self") > -1;
    var stop = modifiers.indexOf("stop") > -1;
    var one = modifiers.indexOf("one") > -1;
    var prevent = modifiers.indexOf("prevent") > -1;
    var capture = modifiers.indexOf("capture") > -1;

    var keyCode = null;
    if (type.indexOf("key") === 0) {
      each(modifiers, function (value) {
        if (/^(\d)*$/.test(value)) {
          keyCode = +value;
          return false;
        }
      });
    }

    return {
      self: self,
      stop: stop,
      prevent: prevent,
      capture: capture,
      keyCode: keyCode,
      one: one,
    };
  }

  /**
   * 收集绑定的事件
   * @param   {Object}  desc
   * @return  {Array}
   */
  function collectEvents(desc) {
    var binds = [];
    var args = desc.args;
    var expression = desc.expression.trim();

    if (args) {
      var pos = args.indexOf(".");
      var type = pos === -1 ? args : args.substr(0, pos);
      var modifier = pos === -1 ? "" : args.substr(pos + 1, args.length);
      binds = [{ type: type, handler: expression, modifier: modifier }];
    } else if (/^{.*}$/.test(expression)) {
      expression = expression.substr(1, expression.length - 2);
      each(removeSpace(expression).split(","), function (event) {
        var info = event.split(":");
        binds.push({ type: info[0], handler: info[1], modifier: "" });
      });
    }

    return binds;
  }

  /**
   * 生成匿名事件函数
   * @param   {String}  expression
   * @return  {Function}
   */
  function createAnonymous(expression) {
    try {
      return new Function("scope", addScope(expression));
    } catch (e) {
      error("Invalid generated expression: [" + expression + "]");
      return noop;
    }
  }

  /**
   * v-on 指令解析模块
   * 不需要实例化 Directive
   */
  function VOn() {
    Parser.apply(this, arguments);
  }

  var von = linkParser(VOn);

  /**
   * 解析 v-on 指令
   */
  von.parse = function () {
    each(
      collectEvents(this.desc),
      function (bind) {
        this.parseEvent(bind);
      },
      this
    );
  };

  /**
   * 解析事件绑定函数
   * @param  {Object}  event
   */
  von.parseEvent = function (event) {
    var type = event.type;
    var handler = event.handler;
    var modifier = event.modifier;
    var modifiers = getModifiers(type, modifier);

    if (handler === "$remove") {
      return this.bindRemoveEvent(type, modifiers);
    }

    var inlineStatement = isNormal(handler) ? handler + "($event)" : handler;
    this.bindEvent(type, createAnonymous(inlineStatement), modifiers);
  };

  /**
   * 绑定删除($remove) vfor 选项事件
   * @param  {String}  type   [事件类型]
   * @param  {String}  dress  [事件修饰符]
   */
  von.bindRemoveEvent = function (type, modifiers) {
    var scope = this.scope;

    if (!scope) {
      return warn("The specify event $remove must be used in v-for scope");
    }

    var alias = scope.__alias__;
    this.bindEvent(
      type,
      function $remove() {
        scope.__viterator__.$remove(scope[alias]);
      },
      modifiers
    );
  };

  /**
   * 添加一个事件绑定，同时处理参数的变更
   * @param  {String}    type       [事件类型]
   * @param  {Function}  handler    [事件函数]
   * @param  {Object}    modifiers  [事件修饰符]
   */
  von.bindEvent = function (type, handler, modifiers) {
    var el = this.el;
    var scope = this.scope || this.vm.$data;
    var self = modifiers.self;
    var stop = modifiers.stop;
    var prevent = modifiers.prevent;
    var capture = modifiers.capture;
    var keyCode = modifiers.keyCode;
    var one = modifiers.one;

    var listenerAgent = function _listenerAgent(e) {
      if (
        (self && e.target !== el) || // 是否限定只能在当前节点触发事件
        (keyCode && keyCode !== e.keyCode) // 键盘事件时是否指定键码触发
      ) {
        return;
      }

      // 是否阻止默认事件
      if (prevent) {
        e.preventDefault();
      }

      // 是否阻止冒泡
      if (stop) {
        e.stopPropagation();
      }

      // 挂载 $event
      def(scope, "$event", e);

      handler.call(scope, scope);
    };

    var listener = one
      ? function _oneListener(e) {
          listenerAgent(e);
          removeEvent(el, type, listener, capture);
        }
      : listenerAgent;

    addEvent(el, type, listener, capture);
  };

  /**
   * v-el 指令解析模块
   * 不需要实例化 Directive
   */
  function VEl() {
    Parser.apply(this, arguments);
  }

  var vel = linkParser(VEl);

  /**
   * 解析 v-el 指令
   * 不需要在 model 中声明
   */
  vel.parse = function () {
    // 不能在 vfor 中使用
    if (!this.scope) {
      this.vm.$regEles[this.desc.expression] = this.el;
    } else {
      warn(
        "v-el can not be used inside v-for! Consider use v-custom to handle v-for element."
      );
    }
  };

  var hasProto = "__proto__" in {};
  var arrayProto = Array.prototype;
  var mutatedProto = Object.create(arrayProto);

  /**
   * 重写/定义数组变异方法
   */
  each(
    ["pop", "push", "sort", "shift", "splice", "unshift", "reverse"],
    function (method) {
      var original = arrayProto[method];

      def(mutatedProto, method, function () {
        var arguments$1 = arguments;

        var args = [];
        var ob = this.__ob__;

        for (var i = 0; i < arguments.length; i++) {
          args.push(arguments$1[i]);
        }

        ob.dep.beforeNotify();

        var result = original.apply(this, args);

        var inserts;
        switch (method) {
          case "push":
          case "unshift":
            inserts = args;
            break;
          case "splice":
            inserts = args.slice(2);
            break;
        }

        if (inserts && inserts.length) {
          observeArray(inserts);
        }

        ob.dep.notify({ method: method, args: args, source: this });

        return result;
      });
    }
  );

  /**
   * 添加数组选项设置/替换方法（全局修改）
   * 提供需要修改的数组项下标 index 和新值 value
   */
  def(arrayProto, "$set", function (index, value) {
    // 超出数组长度默认追加到最后
    if (index >= this.length) {
      index = this.length;
    }
    return this.splice(index, 1, value)[0];
  });

  /**
   * 添加数组选项删除方法（全局修改）
   */
  def(arrayProto, "$remove", function (item) {
    var index = this.indexOf(item);
    if (index > -1) {
      return this.splice(index, 1);
    }
  });

  /**
   * 逐个修改数组变异方法
   * IE9, IE10 不支持修改 __proto__
   * @param   {Array}  array
   */
  var mutatedKeys = Object.getOwnPropertyNames(mutatedProto);
  /* istanbul ignore next */
  function defMutationProto(array) {
    for (var i = 0; i < mutatedKeys.length; i++) {
      var key = mutatedKeys[i];
      def(array, key, mutatedProto[key]);
    }
  }

  /**
   * 修改数组原型，将变异方法挂载到数组上
   * @param  {Array}  array
   */
  function setMutationProto(array) {
    /* istanbul ignore else */
    if (hasProto) {
      array.__proto__ = mutatedProto;
    } else {
      defMutationProto(array);
    }
  }

  /**
   * 生成取值路径
   * @param   {String}  prefix
   * @param   {String}  suffix
   * @return  {String}
   */
  function createPath$1(prefix, suffix) {
    return prefix ? prefix + "*" + suffix : suffix;
  }

  /**
   * 监测对象
   * @param  {Object}  object
   * @param  {String}  path
   */
  function observeObject(object, path) {
    each(object, function (value, key) {
      observe(object, key, value, createPath$1(path, key));
    });
  }

  /**
   * 监测数组
   * @param  {Array}   array
   * @param  {String}  path
   */
  function observeArray(array, path) {
    setMutationProto(array);
    each(array, function (item, index) {
      createObserver(item, createPath$1(path, index));
    });
  }

  /**
   * 数据监测模块
   * @param  {Object}  data  [监测对象/数组]
   * @param  {String}  path  [监测字段名称]
   */
  function Observer(data, path) {
    this.dep = new Depend(path);

    if (isArray(data)) {
      observeArray(data, path);
    } else {
      observeObject(data, path);
    }

    def(data, "__ob__", this);
  }

  /**
   * 创建一个对象监测
   * @param   {Object|Array}  target
   * @param   {String}        path
   * @return  {Object}
   */
  function createObserver(target, path) {
    if (isObject(target) || isArray(target)) {
      return hasOwn(target, "__ob__")
        ? target.__ob__
        : new Observer(target, path || "");
    }
  }

  /**
   * 监测 object[key] 的变化 & 依赖收集
   * @param  {Object}   object
   * @param  {String}   key
   * @param  {Mix}      value
   * @param  {String}   path
   */
  function observe(object, key, value, path) {
    var dep = new Depend(path);
    var descriptor = Object.getOwnPropertyDescriptor(object, key);
    var getter = descriptor && descriptor.get;
    var setter = descriptor && descriptor.set;

    var childOb = createObserver(value, path);

    Object.defineProperty(object, key, {
      get: function Getter() {
        var val = getter ? getter.call(object) : value;

        if (Depend.watcher) {
          dep.depend();
          if (childOb) {
            childOb.dep.depend();
          }
        }

        if (isArray(val)) {
          each(val, function (item) {
            var ob = item && item.__ob__;
            if (ob) {
              ob.dep.depend();
            }
          });
        }

        return val;
      },
      set: function Setter(newValue) {
        var oldValue = getter ? getter.call(object) : value;

        if (newValue === oldValue) {
          return;
        }

        dep.beforeNotify();

        if (setter) {
          setter.call(object, newValue);
        } else {
          value = newValue;
        }

        childOb = createObserver(newValue, key);
        dep.notify();
      },
    });
  }

  /**
   * 生成计算属性取值函数
   * @param   {Object}    vm
   * @param   {Function}  getter
   * @return  {Function}
   */
  function createComputedGetter(vm, getter) {
    var watcher = new Watcher(vm, {
      expression: getter.bind(vm),
    });

    return function computedGetter() {
      if (Depend.watcher) {
        each(watcher.depends, function (dep) {
          dep.depend();
        });
      }
      return watcher.value;
    };
  }

  /**
   * 设置 vm 的计算属性
   * @param  {Object}  vm
   * @param  {Object}  computed
   */
  function setComputedProperty(vm, computed) {
    each(computed, function (getter, property) {
      if (!isFunc(getter)) {
        return warn(
          "computed property [" + property + "] must be a getter function!"
        );
      }

      Object.defineProperty(vm, property, {
        set: noop,
        get: createComputedGetter(vm, getter),
      });
    });
  }

  var vforAlias = "__vfor__";
  var vforGuid = "__vforid__";
  var regForExp = /(.*) (?:in|of) (.*)/;
  var partlyMethods = "push|pop|shift|unshift|splice".split("|");

  /**
   * 获取添加前和删除后的钩子函数
   * @param   {Object}   vm
   * @param   {Element}  node
   * @return  {Object}
   */
  function getHooks(vm, node) {
    var after, before;
    var hooks = vm.$hooks;
    var afterHook = node.__afterHook__;
    var beforeHook = node.__beforeHook__;

    if (afterHook) {
      after = hooks[afterHook];
    }

    if (beforeHook) {
      before = hooks[beforeHook];
    }

    return { after: after, before: before };
  }

  /**
   * 生成一段不重复的 id
   * @return  {String}
   */
  function makeVforGuid() {
    return Math.random().toString(36).substr(2);
  }

  /**
   * v-for 指令解析模块
   */
  function VFor() {
    Parser.apply(this, arguments);
  }

  var vfor = linkParser(VFor);

  /**
   * 解析 v-for 指令
   */
  vfor.parse = function () {
    var el = this.el;
    var desc = this.desc;
    var parent = el.parentNode;
    var expression = desc.expression;
    var match = expression.match(regForExp);

    if (!match) {
      return warn('The format of v-for must be like "item in/of items"!');
    }

    if (parent.nodeType !== 1) {
      return warn("v-for cannot use in the root element!");
    }

    if (hasAttr(el, "v-if")) {
      return warn(
        "Do not use v-if and v-for on the same element! Consider filtering the source Array instead."
      );
    }

    var alias = match[1];
    var iterator = match[2];

    this.length = 0;
    this.scopes = [];
    this.init = true;
    this.alias = alias;
    this.partly = false;
    this.partlyArgs = [];

    this.$parent = parent;
    this.end = el.nextSibling;
    this.start = el.previousSibling;

    this.bodyDirs = el.__dirs__;
    this.tpl = el.cloneNode(true);
    this.hooks = getHooks(this.vm, el);
    this.isOption = el.tagName === "OPTION" && parent.tagName === "SELECT";

    desc.expression = iterator;
    this.bind();
    this.updateModel();
  };

  /**
   * 更新 select 绑定
   */
  vfor.updateModel = function () {
    if (this.isOption) {
      var selectModel = this.$parent.__vmodel__;
      if (selectModel) {
        selectModel.forceUpdate();
      }
    }
  };

  /**
   * 调用状态钩子函数
   * @param  {String}   type   [钩子类型]
   * @param  {Element}  frag   [元素板块]
   * @param  {Number}   index  [下标]
   */
  vfor.hook = function (type, frag, index) {
    var hook = this.hooks[type];
    if (isFunc(hook)) {
      hook.call(this.vm.$context, frag, index, frag[vforGuid]);
      frag = null;
    }
  };

  /**
   * 更新视图
   * @param  {Array}    newArray   [新数组]
   * @param  {Array}    oldArray   [旧数组]
   * @param  {Boolean}  fromDeep   [数组内部更新]
   * @param  {Object}   methodArg  [数组操作参数信息]
   */
  vfor.update = function (newArray, oldArray, fromDeep, methodArg) {
    this.length = newArray && newArray.length;

    // 初始化列表
    if (this.init) {
      this.$parent.replaceChild(this.buildList(newArray), this.el);
      this.el = null;
      this.init = false;
    } else {
      // 数组操作部分更新
      if (methodArg && partlyMethods.indexOf(methodArg.method) > -1) {
        this.partly = true;
        this.updatePartly(newArray, methodArg);
        this.partly = false;
      } else {
        this.recompileList(newArray);
        this.updateModel();
      }
    }
  };

  /**
   * 数组操作部分更新列表
   * @param  {Array}   list
   * @param  {Object}  arg
   */
  vfor.updatePartly = function (list, arg) {
    var partlyArgs = [];
    var args = arg.args;
    var method = arg.method;
    var scopes = this.scopes;

    // 更新处理 DOM 片段
    this[method].call(this, list, args);

    // 更新 scopes
    switch (method) {
      case "pop":
      case "shift":
        break;
      case "push":
      case "unshift":
        partlyArgs = this.partlyArgs;
        break;
      case "splice":
        partlyArgs = args.slice(0, 2);
        Array.prototype.push.apply(partlyArgs, this.partlyArgs);
        break;
    }

    scopes[method].apply(scopes, partlyArgs);
    this.partlyArgs.length = 0;

    // 同步更新下标序号
    each(scopes, function (scope, index) {
      scope.$index = index;
    });
  };

  /**
   * 重新构建列表
   * @param  {Array}  list
   */
  vfor.recompileList = function (list) {
    var this$1 = this;

    var count = 0;
    var parent = this.$parent;
    var childs = parent.childNodes;

    // 清空循环列表
    for (var i = 0; i < childs.length; i++) {
      var child = childs[i];
      if (child[vforAlias] === this$1.alias) {
        this$1.hook("before", child, count++);
        parent.removeChild(child);
        i--;
      }
    }

    // 移除所有取值域缓存
    this.scopes.length = 0;

    var listFragment = this.buildList(list);
    parent.insertBefore(listFragment, this.end);
  };

  /**
   * 构建循环板块
   * @param   {Array}     list        [列表数组]
   * @param   {Number}    startIndex  [下标起点]
   * @return  {Fragment}
   */
  vfor.buildList = function (list, startIndex) {
    var vm = this.vm;
    var tpl = this.tpl;
    var start = startIndex || 0;
    var listFragment = createFragment();
    var iterator = this.directive.watcher.value;

    each(
      list,
      function (item, i) {
        var index = start + i;
        var alias = this.alias;
        var frag = tpl.cloneNode(true);
        var parentScope = this.scope || vm.$data;
        var scope = Object.create(parentScope);

        scope.$parent = parentScope;

        // 绑定别名
        observe(scope, alias, item);
        // 绑定下标
        observe(scope, "$index", index);

        // 挂载别名
        def(scope, "__alias__", alias);
        // 挂载迭代器
        def(scope, "__viterator__", iterator);

        if (this.partly) {
          this.partlyArgs.push(scope);
        } else {
          this.scopes.push(scope);
        }

        // 阻止重复编译除 vfor 以外的指令
        if (this.init && this.bodyDirs > 1) {
          vm.block(this.el);
        }

        // 片段挂载别名
        def(frag, vforAlias, alias);
        // 挂载唯一 id
        def(frag, vforGuid, makeVforGuid());

        // 编译片段
        vm.compile(frag, true, scope, this.desc.once);

        listFragment.appendChild(frag);

        this.hook("after", frag, index);
      },
      this
    );

    return listFragment;
  };

  /**
   * 获取完整的列表数据
   * @return  {Array}
   */
  vfor.getChilds = function () {
    var this$1 = this;

    var list = [];
    var childs = this.$parent.childNodes;

    for (var i = 0; i < childs.length; i++) {
      var child = childs[i];

      if (child[vforAlias] === this$1.alias) {
        list.push(child);
      }
    }

    return list;
  };

  /**
   * 获取循环列表第一项
   * @return  {Element}
   */
  vfor.getFirst = function () {
    var start = this.start;
    return (start && start.nextSibling) || this.$parent.firstChild;
  };

  /**
   * 获取循环列表最后一项
   * @return  {Element}
   */
  vfor.getLast = function () {
    var end = this.end;
    return (end && end.previousSibling) || this.$parent.lastChild;
  };

  /**
   * 获取循环列表指定下标项
   * @param   {Number}    index
   * @return  {Element}
   */
  vfor.getChild = function (index) {
    return this.getChilds()[index] || null;
  };

  /**
   * 删除一条选项
   * @param  {Element}  child
   */
  vfor.removeChild = function (child) {
    if (child) {
      this.$parent.removeChild(child);
    }
  };

  /**
   * 删除循环列表的第一个元素 array.shift()
   */
  vfor.shift = function () {
    var first = this.getFirst();
    if (first) {
      this.hook("before", first, 0);
      this.removeChild(first);
    }
  };

  /**
   * 删除循环列表的最后一个元素 array.pop()
   */
  vfor.pop = function () {
    var last = this.getLast();
    if (last) {
      this.hook("before", last, this.length);
      this.removeChild(last);
    }
  };

  /**
   * 在循环列表结尾追加元素 array.push(item)
   * @param  {Array}  list
   * @param  {Array}  args
   */
  vfor.push = function (list, args) {
    var item = this.buildList(args, list.length - 1);
    this.$parent.insertBefore(item, this.end);
  };

  /**
   * 在循环列表开头追加元素 array.unshift(item)
   * @param  {Array}  list
   * @param  {Array}  args
   */
  vfor.unshift = function (list, args) {
    var first = this.getFirst();
    var item = this.buildList(args, 0);
    this.$parent.insertBefore(item, first);
  };

  /**
   * 循环列表的增删改 splice(start, deleteCount, inserts)
   * @param  {Array}  list
   * @param  {Array}  args
   */
  vfor.splice = function (list, args) {
    // 从数组的哪一位开始修改内容。如果超出了数组的长度，则从数组末尾开始添加内容。
    var start = args[0];
    // 整数，表示要移除的数组元素的个数。
    // 如果 deleteCount 是 0，则不移除元素。这种情况下，至少应添加一个新元素。
    // 如果 deleteCount 大于 start 之后的元素的总数，则从 start 后面的元素都将被删除（含第 start 位）。
    var deleteCont = args[1];
    // 要添加进数组的元素。如果不指定，则 splice() 只删除数组元素。
    var insertItems = args.slice(2);
    var insertLength = insertItems.length;

    // 不删除也不添加
    if (deleteCont === 0 && !insertLength) {
      return;
    }

    // 只删除 splice(2, 1);
    var deleteOnly = deleteCont && !insertLength;
    // 只插入 splice(2, 0, 'xxx')
    var insertOnly = !deleteCont && insertLength;
    // 删除并插入 splice(2, 1, 'xxx')
    var deleAndInsert = deleteCont && insertLength;

    // 删除指定选项
    if (deleteOnly || deleAndInsert) {
      var oldList = this.getChilds();
      each(
        oldList,
        function (child, index) {
          // 删除范围内
          if (index >= start && index < start + deleteCont) {
            this.hook("before", child, index);
            this.removeChild(child);
          }
        },
        this
      );
      oldList = null;
    }

    // 只插入 或 删除并插入
    if (insertOnly || deleAndInsert) {
      // 开始的元素
      var startItem = this.getChild(start) || this.end;
      // 新增列表
      var listFrag = this.buildList(insertItems, start);
      // 更新新增部分
      this.$parent.insertBefore(listFrag, startItem);
    }
  };

  /**
   * 移除 DOM 注册的引用
   * @param  {Object}      vm
   * @param  {DOMElement}  element
   */
  function removeDOMRegister(vm, element) {
    var registers = vm.$regEles;
    var childNodes = element.childNodes;

    for (var i = 0; i < childNodes.length; i++) {
      var node = childNodes[i];

      if (!isElement(node)) {
        continue;
      }

      var nodeAttrs = node.attributes;

      for (var ii = 0; ii < nodeAttrs.length; ii++) {
        var attr = nodeAttrs[ii];

        if (attr.name === "v-el" && hasOwn(registers, attr.value)) {
          registers[attr.value] = null;
        }
      }

      if (node.hasChildNodes()) {
        removeDOMRegister(vm, node);
      }
    }
  }

  /**
   * 生成一个锚点标记
   * @return  {TextNode}
   */
  function createAnchor() {
    return document.createTextNode("");
  }

  /**
   * 元素节点替换
   * @param  {Element}  oldChild
   * @param  {Element}  newChild
   */
  function replaceNode(oldChild, newChild) {
    var parent = oldChild.parentNode;
    if (parent) {
      parent.replaceChild(newChild, oldChild);
    }
  }

  /**
   * v-if 指令解析模块
   */
  function VIf() {
    Parser.apply(this, arguments);
  }

  var vif = linkParser(VIf);

  /**
   * 解析 v-if 指令
   */
  vif.parse = function () {
    var el = this.el;
    var elseEl = el.nextElementSibling;

    var parent = el.parentNode;

    if (parent.nodeType !== 1) {
      return warn("v-if cannot use in the root element!");
    }

    this.$parent = parent;

    // 状态钩子
    this.hooks = getHooks(this.vm, el);

    // 缓存渲染模板
    this.elTpl = el.cloneNode(true);
    this.elAnchor = createAnchor();
    replaceNode(el, this.elAnchor);
    this.el = null;

    // else 节点
    if (elseEl && hasAttr(elseEl, "v-else")) {
      this.elseTpl = elseEl.cloneNode(true);
      this.elseAnchor = createAnchor();
      replaceNode(elseEl, this.elseAnchor);
      elseEl = null;
    }

    this.bind();
  };

  /**
   * 调用状态钩子函数
   * @param  {String}   type      [钩子类型]
   * @param  {Element}  renderEl  [渲染元素]
   * @param  {Boolean}  isElse    [是否是 else 板块]
   */
  vif.hook = function (type, renderEl, isElse) {
    var hook = this.hooks[type];
    if (isFunc(hook)) {
      hook.call(this.vm.$context, renderEl, isElse);
    }
  };

  /**
   * 更新视图
   * @param  {Boolean}  isRender
   */
  vif.update = function (isRender) {
    var elseAnchor = this.elseAnchor;

    this.toggle(this.elAnchor, this.elTpl, isRender, false);

    if (elseAnchor) {
      this.toggle(elseAnchor, this.elseTpl, !isRender, true);
    }
  };

  /**
   * 切换节点内容渲染
   * @param  {Element}   anchor
   * @param  {Fragment}  template
   * @param  {Boolean}   isRender
   * @param  {Boolean}   isElse
   */
  vif.toggle = function (anchor, template, isRender, isElse) {
    var vm = this.vm;
    var parent = this.$parent;
    var tpl = template.cloneNode(true);

    // 渲染 & 更新视图
    if (isRender) {
      vm.compile(tpl, true, this.scope, this.desc.once);
      var insert = parent.insertBefore(tpl, anchor);
      this.hook("after", insert, isElse);
      def(insert, "__vif__", true);
    }
    // 不渲染的情况需要移除 DOM 索引的引用
    else {
      var el = anchor.previousSibling;
      if (el && el.__vif__) {
        this.hook("before", el, isElse);
        removeDOMRegister(vm, template);
        parent.removeChild(el);
      }
    }
  };

  /**
   * v-text 指令解析模块
   */
  function VText() {
    Parser.apply(this, arguments);
  }

  var vtext = linkParser(VText);

  /**
   * 解析 v-text, {{ text }} 指令
   */
  vtext.parse = function () {
    this.bind();
  };

  /**
   * 更新视图
   * @param  {String}  value
   */
  vtext.update = function (value) {
    this.el.textContent = _toString(value);
  };

  /**
   * v-html 指令解析模块
   */
  function VHtml() {
    Parser.apply(this, arguments);
  }

  var vhtml = linkParser(VHtml);

  /**
   * 解析 v-html 指令
   */
  vhtml.parse = function () {
    this.bind();
  };

  /**
   * 更新视图
   * @param  {String}  value
   */
  vhtml.update = function (value) {
    this.el.innerHTML = _toString(value);
  };

  var visibleDisplay = "__visible__";

  /**
   * 缓存节点行内样式显示值
   * 行内样式 display = '' 不会影响由 classname 中的定义
   * visibleDisplay 用于缓存节点行内样式的 display 显示值
   * @param  {Element}  node
   */
  function setVisibleDisplay(node) {
    var display = node.style.display;
    def(node, visibleDisplay, display === "none" ? "" : display);
  }

  /**
   * 设置节点 style.display 值
   * @param  {Element}  node
   * @param  {String}   display
   */
  function setStyleDisplay(node, display) {
    node.style.display = display;
  }

  /**
   * v-show 指令解析模块
   */
  function VShow() {
    Parser.apply(this, arguments);
  }

  var vshow = linkParser(VShow);

  /**
   * 解析 v-show 指令
   */
  vshow.parse = function () {
    var el = this.el;

    setVisibleDisplay(el);

    // else 片段
    var elseEl = el.nextElementSibling;
    if (elseEl && hasAttr(elseEl, "v-else")) {
      this.elseEl = elseEl;
      setVisibleDisplay(elseEl);
    }

    this.bind();
  };

  /**
   * 更新视图
   * @param  {Boolean}  isShow
   */
  vshow.update = function (isShow) {
    var el = this.el;
    var elseEl = this.elseEl;

    setStyleDisplay(el, isShow ? el[visibleDisplay] : "none");

    if (elseEl) {
      setStyleDisplay(elseEl, !isShow ? elseEl[visibleDisplay] : "none");
    }
  };

  /**
   * 返回 contrast 相对于 refer 的差异对象
   * @param   {Object}  contrast  [对比对象]
   * @param   {Object}  refer     [参照对象]
   * @return  {Object}
   */
  function getDiffObject(contrast, refer) {
    var unique = {};

    each(contrast, function (value, key) {
      var _diff,
        oldItem = refer[key];

      if (isObject(value)) {
        _diff = getDiffObject(value, oldItem);
        if (!isEmptyObject(_diff)) {
          unique[key] = _diff;
        }
      } else if (isArray(value)) {
        var newArray = [];

        each(value, function (nItem, index) {
          var _diff;

          if (isObject(nItem)) {
            _diff = getDiffObject(nItem, oldItem[index]);
            newArray.push(_diff);
          } else {
            // 新数组元素
            if (oldItem.indexOf(nItem) < 0) {
              newArray.push(nItem);
            }
          }
        });

        unique[key] = newArray;
      } else {
        if (value !== oldItem) {
          unique[key] = value;
        }
      }
    });

    return unique;
  }

  /**
   * 返回 contrast 相对于 refer 的差异数组
   * @param   {Array}  contrast  [对比数组]
   * @param   {Array}  refer     [参照数组]
   * @return  {Array}
   */
  function getDiffArray(contrast, refer) {
    var uniques = [];

    if (!isArray(contrast) || !isArray(refer)) {
      return contrast;
    }

    each(contrast, function (item) {
      if (refer.indexOf(item) < 0) {
        uniques.push(item);
      }
    });

    return uniques;
  }

  /**
   * 返回两个比较值的差异
   * 获取绑定 object 和 array 的更新差异
   * @param   {Object|Array}  newVal
   * @param   {Object|Array}  oldVal
   * @return  {Object}
   */
  function diff(newVal, oldVal) {
    var isA = isArray(newVal) && isArray(oldVal);
    var isO = isObject(newVal) && isObject(oldVal);
    var handler = isO ? getDiffObject : isA ? getDiffArray : null;

    var after = (handler && handler(newVal, oldVal)) || newVal;
    var before = (handler && handler(oldVal, newVal)) || oldVal;

    return { after: after, before: before };
  }

  /**
   * 处理 styleObject, 批量更新元素 style
   * @param  {Element}  element
   * @param  {String}   styleObject
   */
  function updateStyle(element, styleObject) {
    var style = element.style;

    if (!isObject(styleObject)) {
      return warn("v-bind for style must be a type of Object", styleObject);
    }

    each(styleObject, function (value, property) {
      if (style[property] !== value) {
        style[property] = value;
      }
    });
  }

  /**
   * 支持空格分割的 add/remove class
   * @param  {Element}  element
   * @param  {String}   className
   * @param  {Boolean}  remove
   */
  function handleClass(element, className, remove) {
    each(className.split(" "), function (cls) {
      if (remove) {
        removeClass(element, cls);
      } else {
        addClass(element, cls);
      }
    });
  }

  /**
   * 根据绑定值更新元素的 className
   * @param  {Element}  element
   * @param  {Mix}      classValue
   * @param  {Boolean}  remove
   */
  function updateClass(element, classValue, remove) {
    if (isString(classValue)) {
      handleClass(element, classValue, remove);
    } else if (isArray(classValue)) {
      each(classValue, function (cls) {
        handleClass(element, cls, remove);
      });
    } else if (isObject(classValue)) {
      each(classValue, function (add, cls) {
        handleClass(element, cls, remove || !add);
      });
    }
  }

  /**
   * v-bind 指令解析模块
   */
  function VBind() {
    Parser.apply(this, arguments);
  }

  var vbind = linkParser(VBind);

  /**
   * 解析 v-bind 指令
   */
  vbind.parse = function () {
    this.desc.deep = true;
    this.bind();
  };

  /**
   * 视图更新
   * @param  {Mix}  newValue
   * @param  {Mix}  oldValue
   */
  vbind.update = function (newValue, oldValue) {
    var type = this.desc.args;
    if (type) {
      this.single(type, newValue, oldValue);
    } else {
      this.multi(newValue, oldValue);
    }
  };

  /**
   * 解析单个 attribute
   * @param  {String}  type
   * @param  {Mix}     newValue
   * @param  {Mix}     oldValue
   */
  vbind.single = function (type, newValue, oldValue) {
    switch (type) {
      case "class":
        this.processClass(newValue, oldValue);
        break;
      case "style":
        this.processStyle(newValue, oldValue);
        break;
      default:
        this.processAttr(type, newValue);
    }
  };

  /**
   * 解析 attribute, class, style 组合
   * @param  {Object}  newObj
   * @param  {Object}  oldObj
   */
  vbind.multi = function (newObj, oldObj) {
    if (oldObj) {
      var ref = diff(newObj, oldObj);
      var after = ref.after;
      var before = ref.before;
      this.batch(after, before);
    }

    this.batch(newObj);
  };

  /**
   * 绑定属性批处理
   * @param  {Object}  newObj
   * @param  {Object}  oldObj
   */
  vbind.batch = function (newObj, oldObj) {
    each(
      newObj,
      function (value, key) {
        this.single(key, value, oldObj && oldObj[key]);
      },
      this
    );
  };

  /**
   * 更新处理 className
   * @param  {Mix}  newClass
   * @param  {Mix}  oldClass
   */
  vbind.processClass = function (newClass, oldClass) {
    var el = this.el;

    // 数据更新
    if (oldClass) {
      var ref = diff(newClass, oldClass);
      var after = ref.after;
      var before = ref.before;
      updateClass(el, before, true);
      updateClass(el, after);
    } else {
      updateClass(el, newClass);
    }
  };

  /**
   * 更新处理 style
   * @param  {Mix}  newStyle
   * @param  {Mix}  oldStyle
   */
  vbind.processStyle = function (newStyle, oldStyle) {
    var el = this.el;

    // 数据更新
    if (oldStyle) {
      // 移除旧样式(设为 '')
      each(oldStyle, function (v, key) {
        oldStyle[key] = "";
      });

      updateStyle(el, oldStyle);
    }

    updateStyle(el, newStyle);
  };

  /**
   * 更新处理 attribute
   * @param  {String}   attr
   * @param  {String}   value
   */
  vbind.processAttr = function (attr, value) {
    setAttr(this.el, attr, value);
  };

  /**
   * 保证 func 在上次执行后的 delay 时间内不会被触发第二次
   * @param  {Function}  func
   * @param  {Number}    delay
   */
  var beginTime = 0;
  function debounceDelay(func, delay) {
    beginTime = Date.now();
    setTimeout(function () {
      if (Date.now() - beginTime >= delay) {
        func.call(func);
      }
    }, delay);
  }

  var userAgent = window.navigator.userAgent.toLowerCase();
  var isMsie9 = userAgent && userAgent.indexOf("msie 9.0") > 0;

  var text = {
    /**
     * 绑定 text 变化事件
     */
    bind: function bind() {
      var self = this;
      var trim = this.trim;
      var lazy = this.lazy;
      var number = this.number;
      var debounce = this.debounce;
      var directive = this.directive;

      /**
       * 表单值变化设置数据值
       * @param  {String}  value  [表单值]
       */
      function setModelValue(value) {
        if (trim) {
          value = value.trim();
        }

        var val = formatValue(value, number);

        if (debounce) {
          debounceDelay(function () {
            self.onDebounce = true;
            directive.set(val);
          }, toNumber(debounce));
        } else {
          directive.set(val);
        }
      }

      // 解决输入板在未选择词组时 input 事件的触发问题
      // https://developer.mozilla.org/zh-CN/docs/Web/Events/compositionstart
      var composeLock;
      this.on("compositionstart", function () {
        composeLock = true;
      });
      this.on("compositionend", function () {
        composeLock = false;
        if (!lazy) {
          // 在某些浏览器下 compositionend 会在 input 事件之后触发
          // 所以必须在 compositionend 之后进行一次更新以确保数据的同步
          setModelValue(this.value);
        }
      });

      this.on("input", function () {
        if (!composeLock && !lazy) {
          setModelValue(this.value);
        }
      });

      this.on("blur", function () {
        setModelValue(this.value);
      });

      this.on("change", function () {
        setModelValue(this.value);
      });

      // 在 IE9 中，backspace, delete 和剪切事件不会触发 input 事件
      /* istanbul ignore next */
      if (isMsie9) {
        this.on("cut", function () {
          setModelValue(this.value);
        });

        this.on("keyup", function (e) {
          var keyCode = e.keyCode;
          if (keyCode === 8 || keyCode === 46) {
            setModelValue(this.value);
          }
        });
      }
    },

    /**
     * 根据数据更新更新 text 值
     * @param  {String}  data
     */
    update: function update(data) {
      var el = this.el;
      var val = _toString(data);
      if (el.value !== val && !this.onDebounce) {
        el.value = val;
      }
    },
  };

  var radio = {
    /**
     * 绑定 radio 变化事件
     */
    bind: function bind() {
      var number = this.number;
      var directive = this.directive;

      this.on("change", function () {
        directive.set(formatValue(this.value, number));
      });
    },

    /**
     * 根据数据更新更新 radio 值
     * @param  {String}  data
     */
    update: function update(data) {
      var el = this.el;
      el.checked = el.value === _toString(data);
    },
  };

  /**
   * 获取多选 select 的选中值
   * @param   {Select}   select
   * @param   {Boolean}  number
   * @return  {Array}
   */
  function getSelecteds(select, number) {
    var sels = [];
    var options = select.options;

    for (var i = 0; i < options.length; i++) {
      var option = options[i];
      var value = option.value;
      if (option.selected) {
        sels.push(formatValue(value, number));
      }
    }

    return sels;
  }

  var select = {
    /**
     * 绑定 select 变化事件
     */
    bind: function bind() {
      var multi = this.multi;
      var number = this.number;
      var directive = this.directive;

      this.on("change", function () {
        var setVal = multi
          ? getSelecteds(this, number)
          : formatValue(this.value, number);

        directive.set(setVal);
      });

      // 在 更新 select 时将 selectedIndex 设为 -1 的情况下
      // 假如当前 select dom 片段的父元素被移动(append)到文档或其他节点之后
      // 在部分浏览器如 IE9, PhantomJS 中，selectedIndex 将会从 -1 又变回 0
      // 这将导致 v-for option 在列表渲染完成后无法正确的设置 select 的选中值
      // 因为编译阶段都是在文档碎片上执行，所以必须在编译完成后再次强制初始选中状态
      this.vm.after(this.forceUpdate, this);
    },

    /**
     * 根据数据更新 select 选中值
     * @param  {Array|String}  data
     */
    update: function update(data) {
      var el = this.el;
      var multi = this.multi;
      var exp = this.desc.expression;

      // 初始选中项设为空（默认情况下会是第一项）
      // 在 v-model 中 select 的选中项总是以数据(data)为准
      el.selectedIndex = -1;

      if (multi && !isArray(data)) {
        return warn(
          "<select> cannot be multiple when the model set [" +
            exp +
            "] as not Array"
        );
      }

      if (!multi && isArray(data)) {
        return warn(
          "The model [" +
            exp +
            "] cannot set as Array when <select> has no multiple propperty"
        );
      }

      var options = el.options;
      for (var i = 0; i < options.length; i++) {
        var option = options[i];
        var value = option.value;
        option.selected = multi
          ? indexOf(value, data) > -1
          : value === _toString(data);
      }
    },

    /**
     * 强制更新 select 的值，用于动态的 option
     */
    forceUpdate: function forceUpdate() {
      this.update(this.directive.get());
    },
  };

  var checkbox = {
    /**
     * 绑定 checkbox 变化事件
     */
    bind: function bind() {
      var number = this.number;
      var directive = this.directive;

      this.on("change", function () {
        var value = directive.get();
        var checked = this.checked;

        if (isBool(value)) {
          directive.set(checked);
        } else if (isArray(value)) {
          var val = formatValue(this.value, number);
          var index = indexOf(val, value);

          if (checked && index === -1) {
            value.push(val);
          } else if (index > -1) {
            value.splice(index, 1);
          }
        }
      });
    },

    /**
     * 根据数据更新更新 checkbox 值
     * @param  {Boolean|Array}  data
     */
    update: function update(data) {
      var el = this.el;
      var value = formatValue(el.value, this.number);

      if (!isArray(data) && !isBool(data)) {
        return warn(
          "Checkbox v-model value must be a type of Boolean or Array"
        );
      }

      el.checked = isBool(data) ? data : indexOf(value, data) > -1;
    },
  };

  /**
   * 表单数据格式化
   * @param   {String}   value
   * @param   {Boolean}  convertToNumber
   * @return  {Number}
   */
  function formatValue(value, convertToNumber) {
    return convertToNumber ? toNumber(value) : value;
  }

  /**
   * 非全等比较的数组查找
   * @param   {Mix}     item
   * @param   {Array}   array
   * @return  {Number}
   */
  function indexOf(item, array) {
    for (var i = 0; i < array.length; i++) {
      /* jshint ignore:start */
      if (array[i] == item) {
        return i;
      }
      /* jshint ignore:end */
    }

    return -1;
  }

  // 双向数据绑定限制的表单元素
  var validForms = ["input", "select", "textarea"];

  /**
   * v-model 指令解析模块
   */
  function VModel() {
    Parser.apply(this, arguments);
  }

  var vmodel = linkParser(VModel);

  /**
   * 解析 v-model 指令
   */
  vmodel.parse = function () {
    var el = this.el;
    var desc = this.desc;
    var tagName = el.tagName.toLowerCase();
    var type = tagName === "input" ? getAttr(el, "type") : tagName;

    if (validForms.indexOf(tagName) < 0) {
      return warn("v-model only for using in " + validForms.join(", "));
    }

    // v-model 仅支持静态指令表达式
    if (!isNormal(desc.expression)) {
      return warn("v-model directive value can be use by static expression");
    }

    // 双向数据绑定
    desc.duplex = true;
    this.bindDuplex(type);
  };

  /**
   * 双向数据绑定
   * @param  {String}  type
   */
  vmodel.bindDuplex = function (type) {
    var form;
    var el = this.el;

    switch (type) {
      case "text":
      case "password":
      case "textarea":
        form = text;
        // 可以使用 trim 属性来清除首尾空格
        this.trim = hasAttr(el, "trim");
        // 可以使用 lazy 属性来控制 input 事件是否同步数据
        this.lazy = hasAttr(el, "lazy");
        // 可以使用 debounce 来设置更新数据的延迟时间
        this.debounce = getAttr(el, "debounce");
        break;
      case "radio":
        form = radio;
        break;
      case "checkbox":
        form = checkbox;
        break;
      case "select":
        form = select;
        // select 需要将指令实例挂载到元素上
        def(el, "__vmodel__", this);
        // 是否多选
        this.multi = hasAttr(el, "multiple");
        // 动态 option 强制刷新取值方法
        this.forceUpdate = select.forceUpdate.bind(this);
        break;
    }

    // 提示未指定类型的表单元素
    if (!form) {
      return warn("Do not use incorrect form-type with v-model: ", el);
    }

    // 是否将绑定值转化成数字
    this.number = hasAttr(el, "number");

    // 表单刷新函数
    this.update = form.update.bind(this);

    // 订阅数据 & 更新初始值
    this.bind();

    // 绑定表单变化事件
    if (!this.desc.once) {
      form.bind.call(this);
    }
  };

  /**
   * 表单元素事件绑定
   * @param  {String}    type
   * @param  {Function}  callback
   */
  vmodel.on = function (type, callback) {
    addEvent(this.el, type, callback, false);
  };

  /**
   * v-custom 指令解析模块
   */
  function VCustom() {
    Parser.apply(this, arguments);
  }

  var vcustom = linkParser(VCustom);

  /**
   * 解析 v-custom 指令
   */
  vcustom.parse = function () {
    var desc = this.desc;
    var update = this.vm.$customs[desc.args];

    if (!isFunc(update)) {
      return warn(
        "Custom directive [" +
          desc.attr +
          "] must define with a refresh function!"
      );
    }

    this.update = update;
    this.bind();
  };

  /**
   * 导出指令解析模块
   * @type {Object}
   */
  var DirectiveParsers = {
    von: VOn,
    vel: VEl,
    vif: VIf,
    vfor: VFor,
    vtext: VText,
    vhtml: VHtml,
    vshow: VShow,
    vbind: VBind,
    vmodel: VModel,
    vcustom: VCustom,
  };

  var regNewline = /\n/g;
  var regText = /\{\{(.+?)\}\}/g;
  var regMustache = /(\{\{.*\}\})/;
  var noNeedParsers = ["velse", "vpre", "vcloak", "vonce", "vhook"];

  /**
   * 是否是合法指令
   * @param   {String}   directive
   * @return  {Boolean}
   */
  function isDirective(directive) {
    return directive.indexOf("v-") === 0;
  }

  /**
   * 是否是 v-once 指令
   * 节点以及所有子节点的指令只渲染，无数据绑定
   * @param   {Element}  node
   * @return  {Boolean}
   */
  function isOnceNode(node) {
    return isElement(node) && hasAttr(node, "v-once");
  }

  /**
   * 节点的子节点是否延迟编译
   * 单独处理 vif, vfor 和 vpre 子节点的编译
   * @param   {Element}  node
   * @return  {Boolean}
   */
  function hasLateCompileChilds(node) {
    return (
      hasAttr(node, "v-if") || hasAttr(node, "v-for") || hasAttr(node, "v-pre")
    );
  }

  /**
   * 节点是否含有合法指令
   * @param   {Element}  node
   * @return  {Number}
   */
  function hasDirective(node) {
    if (isElement(node) && node.hasAttributes()) {
      var nodeAttrs = node.attributes;

      for (var i = 0; i < nodeAttrs.length; i++) {
        if (isDirective(nodeAttrs[i].name)) {
          return true;
        }
      }
    } else if (isTextNode(node) && regMustache.test(node.textContent)) {
      return true;
    }
  }

  /**
   * 获取指令信息
   * @param   {Attr}    attribute
   * @return  {Object}
   */
  function getDirectiveDesc(attribute) {
    var attr = attribute.name;
    var expression = attribute.value;
    var directive,
      args,
      pos = attr.indexOf(":");

    if (pos > -1) {
      args = attr.substr(pos + 1);
      directive = attr.substr(0, pos);
    } else {
      directive = attr;
    }

    return {
      args: args,
      attr: attr,
      directive: directive,
      expression: expression,
    };
  }

  /**
   * 缓存指令钩子函数名称
   * @param  {Elemnt}  node
   */
  function saveDirectiveHooks(node) {
    if (!node.__afterHook__) {
      def(node, "__afterHook__", getAttr(node, "v-hook:after"));
    }

    if (!node.__beforeHook__) {
      def(node, "__beforeHook__", getAttr(node, "v-hook:before"));
    }
  }

  /**
   * 统一变更回调函数
   * 保证多个相同依赖的变更只触发一次
   * @param   {Function}  func
   * @param   {Object}    context
   * @return  {Function}
   */
  function makeUnifyCallback(func, context) {
    var _path, _newVal, _oldVal;
    return function (param, newVal, oldVal) {
      var path = param.path;
      if (path !== _path || newVal !== _newVal || oldVal !== _oldVal) {
        func.apply(context, arguments);
        _path = path;
        _newVal = newVal;
        _oldVal = oldVal;
      }
    };
  }

  /**
   * ViewModel 编译模块
   * @param  {Object}  option  [参数对象]
   */
  function Compiler(option) {
    var data = option.data;
    var elem = option.el;
    var computed = option.computed;
    var watchAll = option.watchAll;
    var element = {};
    // if (!isElement(element)) {
    //   return warn("elem must be a type of DOMElement: ", element);
    // }
    if (elem.charAt(0)==".") {
      let str = elem.substr(1);
      element = document.getElementsByClassName(str);
    } else if (elem.charAt(0)=="#") {
      let str = elem.substr(1);
      element = document.getElementById(str);
    } else if (!isElement(element)) {
      return warn("el must be a type of DOMElement: ", element);
    }
    if (!isObject(data)) {
      return warn("data must be a type of Object: ", data);
    }

    // 编译节点缓存队列
    this.$queue = [];
    // 数据模型对象
    this.$data = data;
    // 缓存根节点
    this.$element = element;
    // DOM 注册索引
    this.$regEles = {};
    // 指令实例缓存
    this.$directives = [];
    // 钩子和统一回调作用域
    this.$context = option.context || this;

    // 监测数据模型
    this.$ob = createObserver(this.$data);
    // 设置计算属性
    if (computed) {
      setComputedProperty(this.$data, computed);
    }

    // 编译完成后的回调集合
    this.$afters = [];
    // v-if, v-for DOM 插删钩子函数
    this.$hooks = option.hooks || {};
    // 自定义指令刷新函数
    this.$customs = option.customs || {};
    // 监听变更统一回调
    this.$unifyCb = isFunc(watchAll)
      ? makeUnifyCallback(watchAll, this.$context)
      : null;

    // 是否立刻编译根元素
    if (!option.lazy) {
      this.mount();
    }
  }

  var cp = Compiler.prototype;

  /**
   * 挂载/编译根元素
   */
  cp.mount = function () {
    this.$done = false;
    this.$fragment = nodeToFragment(this.$element);
    this.compile(this.$fragment, true);
  };

  /**
   * 收集节点所有需要编译的指令
   * 并在收集完成后编译指令队列
   * @param  {Element}  element  [编译节点]
   * @param  {Boolean}  root     [是否是根节点]
   * @param  {Object}   scope    [vfor 取值域]
   * @param  {Boolean}  once     [是否只渲染首次]
   */
  cp.compile = function (element, root, scope, once) {
    var this$1 = this;

    var children = element.childNodes;
    var parentOnce = !!once || isOnceNode(element);

    if (root && hasDirective(element)) {
      this.$queue.push([element, scope]);
    }

    def(element, "__vonce__", parentOnce);

    for (var i = 0; i < children.length; i++) {
      var node = children[i];
      var nodeType = node.nodeType;

      // 指令只能应用在文本或元素节点
      if (nodeType !== 1 && nodeType !== 3) {
        continue;
      }

      var selfOnce = parentOnce || isOnceNode(node);

      if (hasDirective(node)) {
        this$1.$queue.push([node, scope]);
        def(node, "__vonce__", selfOnce);
      }

      if (node.hasChildNodes() && !hasLateCompileChilds(node)) {
        this$1.compile(node, false, scope, selfOnce);
      }
    }

    if (root) {
      this.compileAll();
    }
  };

  /**
   * 编译节点缓存队列
   */
  cp.compileAll = function () {
    each(
      this.$queue,
      function (tuple) {
        this.compileNode(tuple);
        return null;
      },
      this
    );

    this.completed();
  };

  /**
   * 收集并编译节点指令
   * @param  {Array}  tuple  [node, scope]
   */
  cp.compileNode = function (tuple) {
    var node = tuple[0];
    var scope = tuple[1];

    if (isElement(node)) {
      var vfor,
        attrs = [];
      var hasModel, hasBind, index;
      var nodeAttrs = node.attributes;

      for (var i = 0; i < nodeAttrs.length; i++) {
        var attr = nodeAttrs[i];
        var name = attr.name;

        // 收集合法指令
        if (isDirective(name)) {
          if (name === "v-for") {
            vfor = attr;
          } else if (name === "v-model") {
            hasModel = true;
            index = attrs.length;
          } else if (name.indexOf("v-bind") === 0) {
            hasBind = true;
          } else if (name.indexOf("v-hook") === 0) {
            saveDirectiveHooks(node);
          }

          attrs.push(attr);
        }
      }

      // 当 v-bind 和 v-model 共存时，即使 v-model 写在 v-bind 的后面
      // 在 IE9+ 和 Edge 中遍历 attributes 时 v-model 仍然会先于 v-bind
      // 所以当二者共存时，v-model 需要放到最后编译以保证表单 value 的正常获取
      /* istanbul ignore next */
      if (
        !vfor &&
        hasBind &&
        hasModel &&
        attrs.length > 1 &&
        index !== attrs.length - 1
      ) {
        var vmodel = attrs.splice(index, 1)[0];
        attrs.push(vmodel);
        vmodel = null;
      }

      // vfor 指令与其他指令共存时只需编译 vfor
      if (vfor) {
        def(node, "__dirs__", attrs.length);
        attrs = [vfor];
        vfor = null;
      }

      // 解析节点指令
      each(
        attrs,
        function (attribute) {
          this.parse(node, attribute, scope);
        },
        this
      );
    } else if (isTextNode(node)) {
      this.parseText(node, scope);
    }
  };

  /**
   * 解析元素节点指令
   * @param  {Element}  node
   * @param  {Object}   attr
   * @param  {Object}   scope
   */
  cp.parse = function (node, attr, scope) {
    var once = node.__vonce__;
    var desc = getDirectiveDesc(attr);
    var directive = desc.directive;

    var dir = "v" + directive.substr(2);
    var Parser = DirectiveParsers[dir];

    // 移除指令标记
    removeAttr(node, desc.attr);

    // 不需要实例化解析的指令
    if (noNeedParsers.indexOf(dir) > -1) {
      return;
    }

    if (Parser) {
      desc.once = once;
      var dirParser = new Parser(this, node, desc, scope);

      if (once) {
        dirParser.destroy();
      } else if (!scope) {
        this.$directives.push(dirParser);
      }
    } else {
      warn("[" + directive + "] is an unknown directive!");
    }
  };

  /**
   * 解析文本指令 {{ text }}
   * @param  {Element}  node
   * @param  {Object}   scope
   */
  cp.parseText = function (node, scope) {
    var tokens = [],
      desc = {};
    var once = node.parentNode && node.parentNode.__vonce__;
    var text = node.textContent.trim().replace(regNewline, "");

    var pieces = text.split(regText);
    var matches = text.match(regText);

    // 文本节点转化为常量和变量的组合表达式
    // 'a {{b}} c' => '"a " + b + " c"'
    each(pieces, function (piece) {
      if (matches.indexOf("{{" + piece + "}}") > -1) {
        tokens.push("(" + piece + ")");
      } else if (piece) {
        tokens.push('"' + piece + '"');
      }
    });

    desc.once = once;
    desc.expression = tokens.join("+");

    var directive = new DirectiveParsers.vtext(this, node, desc, scope);

    if (once) {
      directive.destroy();
    } else if (!scope) {
      this.$directives.push(directive);
    }
  };

  /**
   * 停止编译节点的剩余指令
   * 如含有其他指令的 vfor 节点
   * 应保留指令信息并放到循环列表中编译
   * @param  {Element}  node
   */
  cp.block = function (node) {
    each(this.$queue, function (tuple) {
      if (node === tuple[0]) {
        return null;
      }
    });
  };

  /**
   * 添加编译完成后的回调函数
   * @param  {Function}  callback
   * @param  {Object}    context
   */
  cp.after = function (callback, context) {
    this.$afters.push([callback, context]);
  };

  /**
   * 检查根节点是否编译完成
   */
  cp.completed = function () {
    if (this.$queue.length === 0 && !this.$done) {
      this.$done = true;
      this.$element.appendChild(this.$fragment);

      // 触发编译完成后的回调函数
      each(this.$afters, function (after) {
        after[0].call(after[1]);
        return null;
      });
    }
  };

  /**
   * 销毁函数，销毁指令，清空根节点
   */
  cp.destroy = function () {
    this.$data = null;
    empty(this.$element);
    each(this.$directives, function (directive) {
      directive.destroy();
      return null;
    });
  };

  /**
   * binder 构造函数入口
   * @param  {Object}  option  [数据参数对象]
   * @param  {Element}   - view      [视图对象]
   * @param  {Object}    - model     [数据对象]
   * @param  {Object}    - computed  [<可选>计算属性对象]
   * @param  {Object}    - methods   [<可选>事件声明函数对象]
   * @param  {Object}    - watches   [<可选>批量 watch 数据对象]
   * @param  {Object}    - customs   [<可选>自定义指令刷新函数对象]
   * @param  {Object}    - context   [<可选>methods, watches 回调上下文]
   * @param  {Object}    - hooks     [<可选>v-if/v-for DOM 增删钩子函数定义]
   * @param  {Function}  - watchAll  [<可选>model 变更统一回调函数]
   * @param  {Boolean}   - lazy      [<可选>是否手动编译根元素]
   */
  function binder(option) {
    var context = option.context || option.data;

    // 将事件函数 this 指向调用者
    each(option.data, function (value, key) {
      if (isFunc(value)) {
        option.data[key] = value.bind(context);
      }
    });

    // 整合事件函数声明对象
    each(option.methods, function (callback, func) {
      option.data[func] = callback.bind(context);
    });

    // 事件或 watch 函数作用域
    this.__ct__ = context;
    // 初始数据备份，用于 reset
    this.__bk__ = copy(option.data);
    // 内部 ViewModel 实例
    this.__vm__ = new Compiler(option);

    // 数据模型对象
    this.$data = this.__vm__.$data;
    // DOM 注册索引
    this.$els = this.__vm__.$regEles;

    // 批量 watch
    each(
      option.watches,
      function (callback, expression) {
        if (isFunc(callback)) {
          this.watch(expression, callback, false);
        } else if (isObject(callback)) {
          this.watch(expression, callback.handler, callback.deep);
        }
      },
      this
    );
  }

  var mvp = binder.prototype;

  /**
   * 手动挂载/编译根元素
   */
  mvp.mount = function () {
    this.__vm__.mount();
  };

  /**
   * 获取指定数据模型值
   * 如果获取的模型为对象或数组
   * 返回数据与原数据保持引用关系
   * @param   {String}  key  [<可选>数据模型字段]
   * @return  {Mix}
   */
  mvp.get = function (key) {
    var data = this.$data;
    return isString(key) ? config(data, key) : data;
  };

  /**
   * 获取指定数据模型值
   * 如果获取的模型为对象或数组
   * 返回数据与原数据将不会保持引用关系
   * @param   {String}  key  [<可选>数据模型字段]
   * @return  {Mix}
   */
  mvp.getCopy = function (key) {
    return copy(this.get(key));
  };

  /**
   * 设置数据模型的值，key 为 json 时则批量设置
   * @param  {String}  key    [数据模型字段]
   * @param  {Mix}     value  [值]
   */
  mvp.set = function (key, value) {
    var data = this.$data;

    // 设置单个
    if (isString(key)) {
      config(data, key, value);
    }
    // 批量设置
    else if (isObject(key)) {
      each(key, function (v, k) {
        config(data, k, v);
      });
    }
  };

  /**
   * 重置数据和视图为初始状态
   * @param  {Array|String}  key  [<可选>数据模型字段，或字段数组，空则重置所有]
   */
  mvp.reset = function (key) {
    var data = this.$data;
    var backup = copy(this.__bk__);

    // 重置单个
    if (isString(key)) {
      data[key] = backup[key];
    }
    // 重置多个
    else if (isArray(key)) {
      each(key, function (v) {
        data[v] = backup[v];
      });
    }
    // 重置所有
    else {
      each(data, function (v, k) {
        data[k] = backup[k];
      });
    }
  };

  /**
   * 监测表达式值的变化
   * @param  {String}    expression  [监测的表达式]
   * @param  {Function}  callback    [监测变化回调]
   * @param  {Boolean}   deep        [<可选>深层依赖监测]
   */
  mvp.watch = function (expression, callback, deep) {
    return new Watcher(
      this,
      {
        deep: deep,
        expression: expression,
      },
      callback.bind(this.__ct__)
    );
  };

  /**
   * 销毁函数
   */
  mvp.destroy = function () {
    this.__vm__.destroy();
    this.__vm__ = this.__ct__ = this.__bk__ = this.$data = this.$els = null;
  };

  return binder;
});
