text: // classList polyfill for old browsers
  require('classlist-polyfill')
  // Object.assign polyfill
  require('es6-object-assign').polyfill()
  
  // String to DOM function
  var domify = require('domify')
  
  // Use the DOM's HTML parsing to escape any dangerous strings
  var escapeHtml = function escapeHtml (str) {
    if (typeof str !== 'undefined') {
      var div = document.createElement('div')
      div.appendChild(document.createTextNode(str))
      return div.innerHTML
    } else {
      return ''
    }
  }
  
  // Utility function to add space-delimited class strings to a DOM element's classList
  var addClasses = function addClasses (el, classStr) {
    if (typeof classStr !== 'string' || classStr.length === 0) {
      return
    }
    var classes = classStr.split(' ')
    for (var i = 0; i < classes.length; i++) {
      var className = classes[i]
      if (className.length) {
        el.classList.add(className)
      }
    }
  }
  
  // Detect CSS Animation End Support
  // https://github.com/limonte/sweetalert2/blob/99bd539f85e15ac170f69d35001d12e092ef0054/src/utils/dom.js#L194
  var animationEndEvent = (function detectAnimationEndEvent () {
    var el = document.createElement('div')
    var eventNames = {
      'animation': 'animationend',
      'WebkitAnimation': 'webkitAnimationEnd',
      'MozAnimation': 'animationend',
      'OAnimation': 'oanimationend',
      'msAnimation': 'MSAnimationEnd'
    }
    for (var i in eventNames) {
      if (el.style[i] !== undefined) {
        return eventNames[i]
      }
    }
    return false
  })()
  
  // vex base CSS classes
  var baseClassNames = {
    vex: 'vex',
    content: 'vex-content',
    overlay: 'vex-overlay',
    close: 'vex-close',
    closing: 'vex-closing',
    open: 'vex-open'
  }
  
  // Private lookup table of all open vex objects, keyed by id
  var vexes = {}
  var globalId = 1
  
  // Private boolean to assist the escapeButtonCloses option
  var isEscapeActive = false
  
  // vex itself is an object that exposes a simple API to open and close vex objects in various ways
  var vex = {
    open: function open (opts) {
      // Check for usage of deprecated options, and log a warning
      var warnDeprecated = function warnDeprecated (prop) {
        console.warn('The "' + prop + '" property is deprecated in vex 3. Use CSS classes and the appropriate "ClassName" options, instead.')
        console.warn('See http://github.hubspot.com/vex/api/advanced/#options')
      }
      if (opts.css) {
        warnDeprecated('css')
      }
      if (opts.overlayCSS) {
        warnDeprecated('overlayCSS')
      }
      if (opts.contentCSS) {
        warnDeprecated('contentCSS')
      }
      if (opts.closeCSS) {
        warnDeprecated('closeCSS')
      }
  
      // The dialog instance
      var vexInstance = {}
  
      // Set id
      vexInstance.id = globalId++
  
      // Store internally
      vexes[vexInstance.id] = vexInstance
  
      // Set state
      vexInstance.isOpen = true
  
      // Close function on the vex instance
      // This is how all API functions should close individual vexes
      vexInstance.close = function instanceClose () {
        // Check state
        if (!this.isOpen) {
          return true
        }
  
        var options = this.options
  
        // escapeButtonCloses is checked first
        if (isEscapeActive && !options.escapeButtonCloses) {
          return false
        }
  
        // Allow the user to validate any info or abort the close with the beforeClose callback
        var shouldClose = (function shouldClose () {
          // Call before close callback
          if (options.beforeClose) {
            return options.beforeClose.call(this)
          }
          // Otherwise indicate that it's ok to continue with close
          return true
        }.bind(this)())
  
        // If beforeClose() fails, abort the close
        if (shouldClose === false) {
          return false
        }
  
        // Update state
        this.isOpen = false
  
        // Detect if the content el has any CSS animations defined
        var style = window.getComputedStyle(this.contentEl)
        function hasAnimationPre (prefix) {
          return style.getPropertyValue(prefix + 'animation-name') !== 'none' && style.getPropertyValue(prefix + 'animation-duration') !== '0s'
        }
        var hasAnimation = hasAnimationPre('') || hasAnimationPre('-webkit-') || hasAnimationPre('-moz-') || hasAnimationPre('-o-')
  
        // Define the function that will actually close the instance
        var close = function close () {
          if (!this.rootEl.parentNode) {
            return
          }
          // Run once
          this.rootEl.removeEventListener(animationEndEvent, close)
          this.overlayEl.removeEventListener(animationEndEvent, close)
          // Remove from lookup table (prevent memory leaks)
          delete vexes[this.id]
          // Remove the dialog from the DOM
          this.rootEl.parentNode.removeChild(this.rootEl)
          // Remove the overlay from the DOM
          this.bodyEl.removeChild(this.overlayEl)
          // Call after close callback
          if (options.afterClose) {
            options.afterClose.call(this)
          }
          // Remove styling from the body, if no more vexes are open
          if (Object.keys(vexes).length === 0) {
            document.body.classList.remove(baseClassNames.open)
          }
        }.bind(this)
  
        // Close the vex
        if (animationEndEvent && hasAnimation) {
          // Setup the end event listener, to remove the el from the DOM
          this.rootEl.addEventListener(animationEndEvent, close)
          this.overlayEl.addEventListener(animationEndEvent, close)
          // Add the closing class to the dialog, showing the close animation
          this.rootEl.classList.add(baseClassNames.closing)
          this.overlayEl.classList.add(baseClassNames.closing)
        } else {
          close()
        }
  
        return true
      }
  
      // Allow strings as content
      if (typeof opts === 'string') {
        opts = {
          content: opts
        }
      }
  
      // `content` is unsafe internally, so translate
      // safe default: HTML-escape the content before passing it through
      if (opts.unsafeContent && !opts.content) {
        opts.content = opts.unsafeContent
      } else if (opts.content) {
        opts.content = escapeHtml(opts.content)
      }
  
      // Store options on instance for future reference
      var options = vexInstance.options = Object.assign({}, vex.defaultOptions, opts)
  
      // Get Body Element
      var bodyEl = vexInstance.bodyEl = document.getElementsByTagName('body')[0]
  
      // vex root
      var rootEl = vexInstance.rootEl = document.createElement('div')
      rootEl.classList.add(baseClassNames.vex)
      addClasses(rootEl, options.className)
  
      // Overlay
      var overlayEl = vexInstance.overlayEl = document.createElement('div')
      overlayEl.classList.add(baseClassNames.overlay)
      addClasses(overlayEl, options.overlayClassName)
      if (options.overlayClosesOnClick) {
        rootEl.addEventListener('click', function overlayClickListener (e) {
          if (e.target === rootEl) {
            vexInstance.close()
          }
        })
      }
      bodyEl.appendChild(overlayEl)
  
      // Content
      var contentEl = vexInstance.contentEl = document.createElement('div')
      contentEl.classList.add(baseClassNames.content)
      addClasses(contentEl, options.contentClassName)
      contentEl.appendChild(options.content instanceof window.Node ? options.content : domify(options.content))
      rootEl.appendChild(contentEl)
  
      // Close button
      if (options.showCloseButton) {
        var closeEl = vexInstance.closeEl = document.createElement('div')
        closeEl.classList.add(baseClassNames.close)
        addClasses(closeEl, options.closeClassName)
        closeEl.addEventListener('click', vexInstance.close.bind(vexInstance))
        contentEl.appendChild(closeEl)
      }
  
      // Add to DOM
      document.querySelector(options.appendLocation).appendChild(rootEl)
  
      // Call after open callback
      if (options.afterOpen) {
        options.afterOpen.call(vexInstance)
      }
  
      // Apply styling to the body
      document.body.classList.add(baseClassNames.open)
  
      // Return the created vex instance
      return vexInstance
    },
  
    // A top-level vex.close function to close dialogs by reference or id
    close: function close (vexOrId) {
      var id
      if (vexOrId.id) {
        id = vexOrId.id
      } else if (typeof vexOrId === 'string') {
        id = vexOrId
      } else {
        throw new TypeError('close requires a vex object or id string')
      }
      if (!vexes[id]) {
        return false
      }
      return vexes[id].close()
    },
  
    // Close the most recently created/opened vex
    closeTop: function closeTop () {
      var ids = Object.keys(vexes)
      if (!ids.length) {
        return false
      }
      return vexes[ids[ids.length - 1]].close()
    },
  
    // Close every vex!
    closeAll: function closeAll () {
      for (var id in vexes) {
        this.close(id)
      }
      return true
    },
  
    // A getter for the internal lookup table
    getAll: function getAll () {
      return vexes
    },
  
    // A getter for the internal lookup table
    getById: function getById (id) {
      return vexes[id]
    }
  }
  
  // Close top vex on escape
  window.addEventListener('keyup', function vexKeyupListener (e) {
    if (e.keyCode === 27) {
      isEscapeActive = true
      vex.closeTop()
      isEscapeActive = false
    }
  })
  
  // Close all vexes on history pop state (useful in single page apps)
  window.addEventListener('popstate', function () {
    if (vex.defaultOptions.closeAllOnPopState) {
      vex.closeAll()
    }
  })
  
  vex.defaultOptions = {
    content: '',
    showCloseButton: true,
    escapeButtonCloses: true,
    overlayClosesOnClick: true,
    appendLocation: 'body',
    className: '',
    overlayClassName: '',
    contentClassName: '',
    closeClassName: '',
    closeAllOnPopState: true
  }
  
  // TODO Loading symbols?
  
  // Include escapeHtml function on the library object
  Object.defineProperty(vex, '_escapeHtml', {
    configurable: false,
    enumerable: false,
    writable: false,
    value: escapeHtml
  })
  
  // Plugin system!
  vex.registerPlugin = function registerPlugin (pluginFn, name) {
    var plugin = pluginFn(vex)
    var pluginName = name || plugin.name
    if (vex[pluginName]) {
      throw new Error('Plugin ' + name + ' is already registered.')
    }
    vex[pluginName] = plugin
  }
  
  module.exports = vex
  