(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define(factory) :
  (global.Pell = factory());
}(this, (function () { 'use strict';

  const defaultParagraphSeparatorString = 'defaultParagraphSeparator'
  const formatBlock = 'formatBlock'
  const addEventListener = (parent, type, listener) => parent.addEventListener(type, listener)
  const appendChild = (parent, child) => parent.appendChild(child)
  const createElement = tag => document.createElement(tag)
  const queryCommandState = command => document.queryCommandState(command)
  const queryCommandValue = command => document.queryCommandValue(command)
  const exec = (command, value = null) => document.execCommand(command, false, value)

  const defaultActions = {
    bold: {
      icon: '<b>B</b>',
      title: 'Bold',
      state: () => queryCommandState('bold'),
      result: () => exec('bold')
    },
    italic: {
      icon: '<i>I</i>',
      title: 'Italic',
      state: () => queryCommandState('italic'),
      result: () => exec('italic')
    },
    underline: {
      icon: '<u>U</u>',
      title: 'Underline',
      state: () => queryCommandState('underline'),
      result: () => exec('underline')
    },
    strikethrough: {
      icon: '<strike>S</strike>',
      title: 'Strike-through',
      state: () => queryCommandState('strikeThrough'),
      result: () => exec('strikeThrough')
    },
    heading1: {
      icon: '<b>H<sub>1</sub></b>',
      title: 'Heading 1',
      result: () => exec(formatBlock, '<h1>')
    },
    heading2: {
      icon: '<b>H<sub>2</sub></b>',
      title: 'Heading 2',
      result: () => exec(formatBlock, '<h2>')
    },
    paragraph: {
      icon: '&#182;',
      title: 'Paragraph',
      result: () => exec(formatBlock, '<p>')
    },
    quote: {
      icon: '&#8220; &#8221;',
      title: 'Quote',
      result: () => exec(formatBlock, '<blockquote>')
    },
    olist: {
      icon: '&#35;',
      title: 'Ordered List',
      result: () => exec('insertOrderedList')
    },
    ulist: {
      icon: '&#8226;',
      title: 'Unordered List',
      result: () => exec('insertUnorderedList')
    },
    code: {
      icon: '&lt;/&gt;',
      title: 'Code',
      result: () => exec(formatBlock, '<pre>')
    },
    line: {
      icon: '&#8213;',
      title: 'Horizontal Line',
      result: () => exec('insertHorizontalRule')
    },
    link: {
      icon: '&#128279;',
      title: 'Link',
      result: () => navigator.clipboard.readText().then(url => exec('createLink', url))
    },
    image: {
      icon: '&#128247;',
      title: 'Image',
      result: () => {
        navigator.clipboard.readText().then(url => exec('insertImage', url))
        exec('enableObjectResizing')
      }
    }
  }
  
  const defaultClasses = {
    actionbar: 'pell-actionbar',
    button: 'pell-button',
    content: 'pell-content',
    selected: 'pell-button-selected'
  }
  
  const init = settings => {
    const actions = settings.actions
      ? (
        settings.actions.map(action => {
          if (typeof action === 'string') return defaultActions[action]
          else if (defaultActions[action.name]) return { ...defaultActions[action.name], ...action }
          return action
        })
      )
      : Object.keys(defaultActions).map(action => defaultActions[action])
  
    const classes = { ...defaultClasses, ...settings.classes }
  
    const defaultParagraphSeparator = settings[defaultParagraphSeparatorString] || 'div'
  
    const actionbar = createElement('div')
    actionbar.className = classes.actionbar
    appendChild(settings.element, actionbar)
  
    const content = settings.element.content = createElement('div')
    content.contentEditable = true
    content.className = classes.content
    content.oninput = ({ target: { firstChild } }) => {
      if (firstChild && firstChild.nodeType === 3) exec(formatBlock, `<${defaultParagraphSeparator}>`)
      else if (content.innerHTML === '<br>') content.innerHTML = ''
      settings.onChange(content.innerHTML)
    }
    content.onkeydown = event => {
      if (event.key === 'Enter' && queryCommandValue(formatBlock) === 'blockquote') {
        setTimeout(() => exec(formatBlock, `<${defaultParagraphSeparator}>`), 0)
      }
    }
    appendChild(settings.element, content)
  
    actions.forEach(action => {
      const button = createElement('button')
      button.className = classes.button
      button.innerHTML = action.icon
      button.title = action.title
      button.setAttribute('type', 'button')
      button.onclick = () => action.result() && content.focus()
  
      if (action.state) {
        const handler = () => button.classList[action.state() ? 'add' : 'remove'](classes.selected)
        addEventListener(content, 'keyup', handler)
        addEventListener(content, 'mouseup', handler)
        addEventListener(button, 'click', handler)
      }
  
      appendChild(actionbar, button)
    })
  
    if (settings.styleWithCSS) exec('styleWithCSS')
    exec(defaultParagraphSeparatorString, defaultParagraphSeparator)
  
    return settings.element
  }
  
  return {exec, init}

})));