text: /* @flow */
  
  import { isIE, isIE9, isEdge } from 'core/util/env'
  
  import { extend, isDef, isUndef } from 'shared/util'
  
  import {
    isXlink,
    xlinkNS,
    getXlinkProp,
    isBooleanAttr,
    isEnumeratedAttr,
    isFalsyAttrValue,
    convertEnumeratedValue,
  } from 'web/util/index'
  
  function updateAttrs(oldVnode: VNodeWithData, vnode: VNodeWithData) {
    const opts = vnode.componentOptions
    if (isDef(opts) && opts.Ctor.options.inheritAttrs === false) {
      return
    }
    if (isUndef(oldVnode.data.attrs) && isUndef(vnode.data.attrs)) {
      return
    }
    let key, cur, old
    const elm = vnode.elm
    const oldAttrs = oldVnode.data.attrs || {}
    let attrs: any = vnode.data.attrs || {}
    // clone observed objects, as the user probably wants to mutate it
    if (isDef(attrs.__ob__)) {
      attrs = vnode.data.attrs = extend({}, attrs)
    }
  
    for (key in attrs) {
      cur = attrs[key]
      old = oldAttrs[key]
      if (old !== cur) {
        setAttr(elm, key, cur)
      }
    }
    // #4391: in IE9, setting type can reset value for input[type=radio]
    // #6666: IE/Edge forces progress value down to 1 before setting a max
    /* istanbul ignore if */
    if ((isIE || isEdge) && attrs.value !== oldAttrs.value) {
      setAttr(elm, 'value', attrs.value)
    }
    for (key in oldAttrs) {
      if (isUndef(attrs[key])) {
        if (isXlink(key)) {
          elm.removeAttributeNS(xlinkNS, getXlinkProp(key))
        } else if (!isEnumeratedAttr(key)) {
          elm.removeAttribute(key)
        }
      }
    }
  }
  
  function setAttr(el: Element, key: string, value: any) {
    if (el.tagName.indexOf('-') > -1) {
      baseSetAttr(el, key, value)
    } else if (isBooleanAttr(key)) {
      // set attribute for blank value
      // e.g. <option disabled>Select one</option>
      if (isFalsyAttrValue(value)) {
        el.removeAttribute(key)
      } else {
        // technically allowfullscreen is a boolean attribute for <iframe>,
        // but Flash expects a value of "true" when used on <embed> tag
        value = key === 'allowfullscreen' && el.tagName === 'EMBED' ? 'true' : key
        el.setAttribute(key, value)
      }
    } else if (isEnumeratedAttr(key)) {
      el.setAttribute(key, convertEnumeratedValue(key, value))
    } else if (isXlink(key)) {
      if (isFalsyAttrValue(value)) {
        el.removeAttributeNS(xlinkNS, getXlinkProp(key))
      } else {
        el.setAttributeNS(xlinkNS, key, value)
      }
    } else {
      baseSetAttr(el, key, value)
    }
  }
  
  function baseSetAttr(el, key, value) {
    if (isFalsyAttrValue(value)) {
      el.removeAttribute(key)
    } else {
      // #7138: IE10 & 11 fires input event when setting placeholder on
      // <textarea>... block the first input event and remove the blocker
      // immediately.
      /* istanbul ignore if */
      if (isIE && !isIE9 && el.tagName === 'TEXTAREA' && key === 'placeholder' && value !== '' && !el.__ieph) {
        const blocker = (e) => {
          e.stopImmediatePropagation()
          el.removeEventListener('input', blocker)
        }
        el.addEventListener('input', blocker)
        // $flow-disable-line
        el.__ieph = true /* IE placeholder patched */
      }
      el.setAttribute(key, value)
    }
  }
  
  export default {
    create: updateAttrs,
    update: updateAttrs,
  }
  