text: /**
   * @file model.js
   * @author sfe-sy(sfe-sy@baidu.com)
   */
  
  /**
   * Not type checking this file because flow doesn't like attaching
   * properties to Elements.
   */
  
  import {isTextInputType} from 'web/util/element';
  import {looseEqual, looseIndexOf} from 'shared/util';
  import {
      warn,
      isAndroid,
      isIE9,
      isIE,
      isEdge
  } from 'core/util/index';
  
  /* istanbul ignore if */
  if (isIE9) {
      // http://www.matts411.com/post/internet-explorer-9-oninput/
      document.addEventListener('selectionchange', () => {
          const el = document.activeElement;
          if (el && el.vmodel) {
              trigger(el, 'input');
          }
  
      });
  }
  
  export default {
      inserted(el, binding, vnode) {
          if (vnode.tag === 'select') {
              setSelected(el, binding, vnode.context);
              el._vOptions = [].map.call(el.options, getValue);
          }
          else if (vnode.tag === 'textarea' || isTextInputType(el.type)) {
              el._vModifiers = binding.modifiers;
              if (!binding.modifiers.lazy) {
                  // Safari < 10.2 & UIWebView doesn't fire compositionend when
                  // switching focus before confirming composition choice
                  // this also fixes the issue where some browsers e.g. iOS Chrome
                  // fires "change" instead of "input" on autocomplete.
                  el.addEventListener('change', onCompositionEnd);
                  if (!isAndroid) {
                      el.addEventListener('compositionstart', onCompositionStart);
                      el.addEventListener('compositionend', onCompositionEnd);
                  }
  
                  /* istanbul ignore if */
                  if (isIE9) {
                      el.vmodel = true;
                  }
              }
          }
  
      },
      componentUpdated(el, binding, vnode) {
          if (vnode.tag === 'select') {
              setSelected(el, binding, vnode.context);
              // in case the options rendered by v-for have changed,
              // it's possible that the value is out-of-sync with the rendered options.
              // detect such cases and filter out values that no longer has a matching
              // option in the DOM.
              const prevOptions = el._vOptions;
              const curOptions = el._vOptions = [].map.call(el.options, getValue);
              if (curOptions.some((o, i) => !looseEqual(o, prevOptions[i]))) {
                  // trigger change event if
                  // no matching option found for at least one value
                  const needReset = el.multiple
                      ? binding.value.some(v => hasNoMatchingOption(v, curOptions))
                      : binding.value !== binding.oldValue && hasNoMatchingOption(binding.value, curOptions);
                  if (needReset) {
                      trigger(el, 'change');
                  }
              }
          }
  
      }
  };
  
  function setSelected(el, binding, vm) {
      actuallySetSelected(el, binding, vm);
  
      /* istanbul ignore if */
      if (isIE || isEdge) {
          setTimeout(() => {
              actuallySetSelected(el, binding, vm);
          }, 0);
      }
  }
  
  function actuallySetSelected(el, binding, vm) {
      const value = binding.value;
      const isMultiple = el.multiple;
      if (isMultiple && !Array.isArray(value)) {
          process.env.NODE_ENV !== 'production' && warn(
              `<select multiple v-model="${binding.expression}"> `
              + `expects an Array value for its binding, but got ${
              Object.prototype.toString.call(value).slice(8, -1)
              }`,
              vm
          );
          return;
      }
  
      let selected;
      let option;
      for (let i = 0, l = el.options.length; i < l; i++) {
          option = el.options[i];
          if (isMultiple) {
              selected = looseIndexOf(value, getValue(option)) > -1;
              if (option.selected !== selected) {
                  option.selected = selected;
              }
          }
          else {
              if (looseEqual(getValue(option), value)) {
                  if (el.selectedIndex !== i) {
                      el.selectedIndex = i;
                  }
  
                  return;
              }
          }
      }
      if (!isMultiple) {
          el.selectedIndex = -1;
      }
  }
  
  function hasNoMatchingOption(value, options) {
      return options.every(o => !looseEqual(o, value));
  }
  
  function getValue(option) {
      return '_value' in option
          ? option._value
          : option.value;
  }
  
  function onCompositionStart(e) {
      e.target.composing = true;
  }
  
  function onCompositionEnd(e) {
      // prevent triggering an input event for no reason
      if (!e.target.composing) {
          return;
      }
  
      e.target.composing = false;
      trigger(e.target, 'input');
  }
  
  function trigger(el, type) {
      const e = document.createEvent('HTMLEvents');
      e.initEvent(type, true, true);
      el.dispatchEvent(e);
  }
  