[
  {
    "path": ".babelrc",
    "content": "{\n  \"presets\": [\n    [\"es2015\", {\"modules\": false}],\n    \"stage-0\"\n  ]\n}\n"
  },
  {
    "path": ".eslintignore",
    "content": "dist\n"
  },
  {
    "path": ".eslintrc",
    "content": "{\n  \"extends\": \"jared\"\n}\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\nnode_modules\n"
  },
  {
    "path": "LICENSE.md",
    "content": "The MIT License (MIT)\n\nCopyright (c) Jared Reich\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "#### [v2 working branch](https://github.com/jaredreich/pell/tree/v2) and [v2 project board](https://github.com/jaredreich/pell/projects/1)\n\n---\n\n<img src=\"./images/logo.png\" width=\"128\" alt=\"Logo\">\n\n[![npm](https://img.shields.io/npm/v/pell.svg)](https://www.npmjs.com/package/pell)\n[![cdnjs](https://img.shields.io/cdnjs/v/pell.svg)](https://cdnjs.com/libraries/pell)\n\n> pell is the simplest and smallest WYSIWYG text editor for web, with no dependencies\n\n![Demo](/demo.gif?raw=true \"Demo\")\n\n## Comparisons\n\n| library       | size (min+gzip) | size (min) | jquery | bootstrap | react | link |\n|---------------|-----------------|------------|--------|-----------|-------|------|\n| pell          | 1.38kB          | 3.54kB     |        |           |       | https://github.com/jaredreich/pell |\n| squire        | 16kB            | 49kB       |        |           |       | https://github.com/neilj/Squire |\n| medium-editor | 27kB            | 105kB      |        |           |       | https://github.com/yabwe/medium-editor |\n| quill         | 43kB            | 205kB      |        |           |       | https://github.com/quilljs/quill |\n| trix          | 47kB            | 204kB      |        |           |       | https://github.com/basecamp/trix |\n| ckeditor      | 163kB           | 551kB      |        |           |       | https://ckeditor.com |\n| trumbowyg     | 8kB             | 23kB       | x      |           |       | https://github.com/Alex-D/Trumbowyg |\n| summernote    | 26kB            | 93kB       | x      | x         |       | https://github.com/summernote/summernote |\n| draft         | 46kB            | 147kB      |        |           | x     | https://github.com/facebook/draft-js |\n| froala        | 52kB            | 186kB      | x      |           |       | https://github.com/froala/wysiwyg-editor |\n| tinymce       | 157kB           | 491kB      | x      |           |       | https://github.com/tinymce/tinymce |\n\n## Features\n\n* Pure JavaScript, no dependencies, written in ES6\n* Easily customizable with the sass file (pell.scss) or overwrite the CSS\n\nIncluded actions:\n- Bold\n- Italic\n- Underline\n- Strike-through\n- Heading 1\n- Heading 2\n- Paragraph\n- Quote\n- Ordered List\n- Unordered List\n- Code\n- Horizontal Rule\n- Link\n- Image\n\nOther available actions (listed at https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand):\n- Justify Center\n- Justify Full\n- Justify Left\n- Justify Right\n- Subscript\n- Superscript\n- Font Name\n- Font Size\n- Indent\n- Outdent\n- Clear Formatting\n- Undo\n- Redo\n\nOr create any custom action!\n\n## Browser Support\n\n* IE 9+ (theoretically, but good luck)\n* Chrome 5+\n* Firefox 4+\n* Safari 5+\n* Opera 11.6+\n\n## Installation\n\n#### npm:\n\n```bash\nnpm install --save pell\n```\n\n#### HTML:\n\n```html\n<head>\n  ...\n  <link rel=\"stylesheet\" type=\"text/css\" href=\"https://unpkg.com/pell/dist/pell.min.css\">\n  <style>\n    /* override styles here */\n    .pell-content {\n      background-color: pink;\n    }\n  </style>\n</head>\n<body>\n  ...\n  <!-- Bottom of body -->\n  <script src=\"https://unpkg.com/pell\"></script>\n</body>\n```\n\n## Usage\n\n#### API\n\n```js\n// ES6\nimport pell from 'pell'\n// or\nimport { exec, init } from 'pell'\n```\n\n```js\n// Browser\npell\n// or\nwindow.pell\n```\n\n```js\n// Initialize pell on an HTMLElement\npell.init({\n  // <HTMLElement>, required\n  element: document.getElementById('some-id'),\n\n  // <Function>, required\n  // Use the output html, triggered by element's `oninput` event\n  onChange: html => console.log(html),\n\n  // <string>, optional, default = 'div'\n  // Instructs the editor which element to inject via the return key\n  defaultParagraphSeparator: 'div',\n\n  // <boolean>, optional, default = false\n  // Outputs <span style=\"font-weight: bold;\"></span> instead of <b></b>\n  styleWithCSS: false,\n\n  // <Array[string | Object]>, string if overwriting, object if customizing/creating\n  // action.name<string> (only required if overwriting)\n  // action.icon<string> (optional if overwriting, required if custom action)\n  // action.title<string> (optional)\n  // action.result<Function> (required)\n  // Specify the actions you specifically want (in order)\n  actions: [\n    'bold',\n    {\n      name: 'custom',\n      icon: 'C',\n      title: 'Custom Action',\n      result: () => console.log('Do something!')\n    },\n    'underline'\n  ],\n\n  // classes<Array[string]> (optional)\n  // Choose your custom class names\n  classes: {\n    actionbar: 'pell-actionbar',\n    button: 'pell-button',\n    content: 'pell-content',\n    selected: 'pell-button-selected'\n  }\n})\n\n// Execute a document command, see reference:\n// https://developer.mozilla.org/en/docs/Web/API/Document/execCommand\n// this is just `document.execCommand(command, false, value)`\npell.exec(command<string>, value<string>)\n```\n\n#### List of overwriteable action names\n- bold\n- italic\n- underline\n- strikethrough\n- heading1\n- heading2\n- paragraph\n- quote\n- olist\n- ulist\n- code\n- line\n- link\n- image\n\n## Examples\n\n#### General\n\n```html\n<div id=\"editor\" class=\"pell\"></div>\n<div>\n  HTML output:\n  <div id=\"html-output\" style=\"white-space:pre-wrap;\"></div>\n</div>\n```\n\n```js\nimport { exec, init } from 'pell'\n\nconst editor = init({\n  element: document.getElementById('editor'),\n  onChange: html => {\n    document.getElementById('html-output').textContent = html\n  },\n  defaultParagraphSeparator: 'p',\n  styleWithCSS: true,\n  actions: [\n    'bold',\n    'underline',\n    {\n      name: 'italic',\n      result: () => exec('italic')\n    },\n    {\n      name: 'backColor',\n      icon: '<div style=\"background-color:pink;\">A</div>',\n      title: 'Highlight Color',\n      result: () => exec('backColor', 'pink')\n    },\n    {\n      name: 'image',\n      result: () => {\n        const url = window.prompt('Enter the image URL')\n        if (url) exec('insertImage', url)\n      }\n    },\n    {\n      name: 'link',\n      result: () => {\n        const url = window.prompt('Enter the link URL')\n        if (url) exec('createLink', url)\n      }\n    }\n  ],\n  classes: {\n    actionbar: 'pell-actionbar-custom-name',\n    button: 'pell-button-custom-name',\n    content: 'pell-content-custom-name',\n    selected: 'pell-button-selected-custom-name'\n  }\n})\n\n// editor.content<HTMLElement>\n// To change the editor's content:\neditor.content.innerHTML = '<b><u><i>Initial content!</i></u></b>'\n```\n\n#### Example (Markdown)\n\n```html\n<div id=\"editor\" class=\"pell\"></div>\n<div>\n  Markdown output:\n  <div id=\"markdown-output\" style=\"white-space:pre-wrap;\"></div>\n</div>\n```\n\n```js\nimport { init } from 'pell'\nimport Turndown from 'turndown'\n\nconst { turndown } = new Turndown({ headingStyle: 'atx' })\n\ninit({\n  element: document.getElementById('editor'),\n  actions: ['bold', 'italic', 'heading1', 'heading2', 'olist', 'ulist'],\n  onChange: html => {\n    document.getElementById('markdown-output').innerHTML = turndown(html)\n  }\n})\n```\n\n#### Frameworks\n\n- [React](/examples/react.md)\n- [Vue](/examples/vue.md)\n\n## Custom Styles\n\n#### SCSS\n\n```scss\n$pell-content-height: 400px;\n// See all overwriteable variables in src/pell.scss\n\n// Then import pell.scss into styles:\n@import '../../node_modules/pell/src/pell';\n```\n\n#### CSS\n\n```css\n/* After pell styles are applied to DOM: */\n.pell-content {\n  height: 400px;\n}\n```\n\n## License\n\nMIT\n\n## Credits\n\nBrowserStack for cross browser testing:\n\n<a href=\"https://www.browserstack.com\" target=\"_blank\" rel=\"noopener noreferrer\"><img width=\"128\" src=\"./images/browserstack.png\" alt=\"BrowserStack logo\"></a>\n"
  },
  {
    "path": "demo.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n\n    <meta name=\"viewport\" content=\"user-scalable=1.0,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0\">\n\n    <title>pell</title>\n\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"dist/pell.css\">\n\n    <style>\n      body {\n        margin: 0;\n        padding: 0;\n      }\n\n      .content {\n        box-sizing: border-box;\n        margin: 0 auto;\n        max-width: 600px;\n        padding: 20px;\n      }\n\n      #html-output {\n        white-space: pre-wrap;\n      }\n    </style>\n\n  </head>\n  <body>\n\n    <div class=\"content\">\n      <h1>pell</h1>\n      <div id=\"editor\" class=\"pell\"></div>\n      <div style=\"margin-top:20px;\">\n        <h3>Text output:</h3>\n        <div id=\"text-output\"></div>\n      </div>\n      <div style=\"margin-top:20px;\">\n        <h3>HTML output:</h3>\n        <pre id=\"html-output\"></pre>\n      </div>\n    </div>\n\n    <script src=\"dist/pell.js\"></script>\n    <script>\n      var editor = window.pell.init({\n        element: document.getElementById('editor'),\n        defaultParagraphSeparator: 'p',\n        onChange: function (html) {\n          document.getElementById('text-output').innerHTML = html\n          document.getElementById('html-output').textContent = html\n        }\n      })\n    </script>\n\n  </body>\n</html>\n"
  },
  {
    "path": "dist/pell.css",
    "content": ".pell {\n  border: 1px solid rgba(10, 10, 10, 0.1);\n  box-sizing: border-box; }\n\n.pell-content {\n  box-sizing: border-box;\n  height: 300px;\n  outline: 0;\n  overflow-y: auto;\n  padding: 10px; }\n\n.pell-actionbar {\n  background-color: #FFF;\n  border-bottom: 1px solid rgba(10, 10, 10, 0.1); }\n\n.pell-button {\n  background-color: transparent;\n  border: none;\n  cursor: pointer;\n  height: 30px;\n  outline: 0;\n  width: 30px;\n  vertical-align: bottom; }\n\n.pell-button-selected {\n  background-color: #F0F0F0; }\n"
  },
  {
    "path": "dist/pell.js",
    "content": "(function (global, factory) {\n\ttypeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :\n\ttypeof define === 'function' && define.amd ? define(['exports'], factory) :\n\t(factory((global.pell = {})));\n}(this, (function (exports) { 'use strict';\n\nvar _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };\n\nvar defaultParagraphSeparatorString = 'defaultParagraphSeparator';\nvar formatBlock = 'formatBlock';\nvar addEventListener = function addEventListener(parent, type, listener) {\n  return parent.addEventListener(type, listener);\n};\nvar appendChild = function appendChild(parent, child) {\n  return parent.appendChild(child);\n};\nvar createElement = function createElement(tag) {\n  return document.createElement(tag);\n};\nvar queryCommandState = function queryCommandState(command) {\n  return document.queryCommandState(command);\n};\nvar queryCommandValue = function queryCommandValue(command) {\n  return document.queryCommandValue(command);\n};\n\nvar exec = function exec(command) {\n  var value = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;\n  return document.execCommand(command, false, value);\n};\n\nvar defaultActions = {\n  bold: {\n    icon: '<b>B</b>',\n    title: 'Bold',\n    state: function state() {\n      return queryCommandState('bold');\n    },\n    result: function result() {\n      return exec('bold');\n    }\n  },\n  italic: {\n    icon: '<i>I</i>',\n    title: 'Italic',\n    state: function state() {\n      return queryCommandState('italic');\n    },\n    result: function result() {\n      return exec('italic');\n    }\n  },\n  underline: {\n    icon: '<u>U</u>',\n    title: 'Underline',\n    state: function state() {\n      return queryCommandState('underline');\n    },\n    result: function result() {\n      return exec('underline');\n    }\n  },\n  strikethrough: {\n    icon: '<strike>S</strike>',\n    title: 'Strike-through',\n    state: function state() {\n      return queryCommandState('strikeThrough');\n    },\n    result: function result() {\n      return exec('strikeThrough');\n    }\n  },\n  heading1: {\n    icon: '<b>H<sub>1</sub></b>',\n    title: 'Heading 1',\n    result: function result() {\n      return exec(formatBlock, '<h1>');\n    }\n  },\n  heading2: {\n    icon: '<b>H<sub>2</sub></b>',\n    title: 'Heading 2',\n    result: function result() {\n      return exec(formatBlock, '<h2>');\n    }\n  },\n  paragraph: {\n    icon: '&#182;',\n    title: 'Paragraph',\n    result: function result() {\n      return exec(formatBlock, '<p>');\n    }\n  },\n  quote: {\n    icon: '&#8220; &#8221;',\n    title: 'Quote',\n    result: function result() {\n      return exec(formatBlock, '<blockquote>');\n    }\n  },\n  olist: {\n    icon: '&#35;',\n    title: 'Ordered List',\n    result: function result() {\n      return exec('insertOrderedList');\n    }\n  },\n  ulist: {\n    icon: '&#8226;',\n    title: 'Unordered List',\n    result: function result() {\n      return exec('insertUnorderedList');\n    }\n  },\n  code: {\n    icon: '&lt;/&gt;',\n    title: 'Code',\n    result: function result() {\n      return exec(formatBlock, '<pre>');\n    }\n  },\n  line: {\n    icon: '&#8213;',\n    title: 'Horizontal Line',\n    result: function result() {\n      return exec('insertHorizontalRule');\n    }\n  },\n  link: {\n    icon: '&#128279;',\n    title: 'Link',\n    result: function result() {\n      var url = window.prompt('Enter the link URL');\n      if (url) exec('createLink', url);\n    }\n  },\n  image: {\n    icon: '&#128247;',\n    title: 'Image',\n    result: function result() {\n      var url = window.prompt('Enter the image URL');\n      if (url) exec('insertImage', url);\n    }\n  }\n};\n\nvar defaultClasses = {\n  actionbar: 'pell-actionbar',\n  button: 'pell-button',\n  content: 'pell-content',\n  selected: 'pell-button-selected'\n};\n\nvar init = function init(settings) {\n  var actions = settings.actions ? settings.actions.map(function (action) {\n    if (typeof action === 'string') return defaultActions[action];else if (defaultActions[action.name]) return _extends({}, defaultActions[action.name], action);\n    return action;\n  }) : Object.keys(defaultActions).map(function (action) {\n    return defaultActions[action];\n  });\n\n  var classes = _extends({}, defaultClasses, settings.classes);\n\n  var defaultParagraphSeparator = settings[defaultParagraphSeparatorString] || 'div';\n\n  var actionbar = createElement('div');\n  actionbar.className = classes.actionbar;\n  appendChild(settings.element, actionbar);\n\n  var content = settings.element.content = createElement('div');\n  content.contentEditable = true;\n  content.className = classes.content;\n  content.oninput = function (_ref) {\n    var firstChild = _ref.target.firstChild;\n\n    if (firstChild && firstChild.nodeType === 3) exec(formatBlock, '<' + defaultParagraphSeparator + '>');else if (content.innerHTML === '<br>') content.innerHTML = '';\n    settings.onChange(content.innerHTML);\n  };\n  content.onkeydown = function (event) {\n    if (event.key === 'Enter' && queryCommandValue(formatBlock) === 'blockquote') {\n      setTimeout(function () {\n        return exec(formatBlock, '<' + defaultParagraphSeparator + '>');\n      }, 0);\n    }\n  };\n  appendChild(settings.element, content);\n\n  actions.forEach(function (action) {\n    var button = createElement('button');\n    button.className = classes.button;\n    button.innerHTML = action.icon;\n    button.title = action.title;\n    button.setAttribute('type', 'button');\n    button.onclick = function () {\n      return action.result() && content.focus();\n    };\n\n    if (action.state) {\n      var handler = function handler() {\n        return button.classList[action.state() ? 'add' : 'remove'](classes.selected);\n      };\n      addEventListener(content, 'keyup', handler);\n      addEventListener(content, 'mouseup', handler);\n      addEventListener(button, 'click', handler);\n    }\n\n    appendChild(actionbar, button);\n  });\n\n  if (settings.styleWithCSS) exec('styleWithCSS');\n  exec(defaultParagraphSeparatorString, defaultParagraphSeparator);\n\n  return settings.element;\n};\n\nvar pell = { exec: exec, init: init };\n\nexports.exec = exec;\nexports.init = init;\nexports['default'] = pell;\n\nObject.defineProperty(exports, '__esModule', { value: true });\n\n})));\n"
  },
  {
    "path": "examples/react.md",
    "content": "```js\n// App.js\n\nimport React, { Component } from 'react';\nimport { init } from 'pell';\n\nimport 'pell/dist/pell.css'\n\nclass App extends Component {\n  editor = null\n\n  constructor (props) {\n    super(props)\n    this.state = { html: null }\n  }\n\n  componentDidMount () {\n    this.editor = init({\n      element: document.getElementById('editor'),\n      onChange: html => this.setState({ html }),\n      actions: ['bold', 'underline', 'italic'],\n    })\n  }\n\n  render() {\n    return (\n      <div className=\"App\">\n        <h3>Editor:</h3>\n        <div id=\"editor\" className=\"pell\" />\n        <h3>HTML Output:</h3>\n        <div id=\"html-output\">{this.state.html}</div>\n      </div>\n    );\n  }\n}\n\nexport default App;\n```\n"
  },
  {
    "path": "examples/vue.md",
    "content": "```vue\n<template>\n  <div>\n    <h6>Editor:</h6>\n    <div id=\"pell\" class=\"pell\" />\n    <h6>HTML Output:</h6>\n    <pre id=\"pell-html-output\"></pre>\n  </div>\n</template>\n\n<script>\nimport pell from 'pell'\n\nexport default {\n  methods: {\n    ensureHTTP: str => /^https?:\\/\\//.test(str) && str || `http://${str}`\n  },\n  mounted () {\n    pell.init({\n      element: document.getElementById('pell'),\n      onChange: html => {\n        window.document.getElementById('pell-html-output').textContent = html\n      },\n      actions: [\n        'bold',\n        'italic',\n        'underline',\n        'strikethrough',\n        'heading1',\n        'heading2',\n        'paragraph',\n        'quote',\n        'olist',\n        'ulist',\n        'code',\n        'line',\n        {\n          name: 'image',\n          result: () => {\n            const url = window.prompt('Enter the image URL')\n            if (url) pell.exec('insertImage', this.ensureHTTP(url))\n          }\n        },\n        {\n          name: 'link',\n          result: () => {\n            const url = window.prompt('Enter the link URL')\n            if (url) pell.exec('createLink', this.ensureHTTP(url))\n          }\n        }\n      ]\n    })\n  }\n}\n</script>\n\n<style>\n.pell {\n  border: 2px solid #000;\n  border-radius: 0;\n  box-shadow: none;\n}\n\n#pell-html-output {\n  margin: 0;\n  white-space: pre-wrap;\n}\n</style>\n```\n"
  },
  {
    "path": "gulpfile.js",
    "content": "const babel = require('rollup-plugin-babel')\nconst cssnano = require('gulp-cssnano')\nconst del = require('del')\nconst gulp = require('gulp')\nconst rename = require('gulp-rename')\nconst Rollup = require('rollup')\nconst rollup = require('gulp-rollup')\nconst run = require('run-sequence')\nconst sass = require('gulp-sass')\nconst size = require('gulp-size')\nconst uglify = require('rollup-plugin-uglify')\n\ngulp.task('clean', () => del(['./dist']))\n\nconst rollupConfig = minimize => ({\n  rollup: Rollup,\n  entry: './src/pell.js',\n  moduleName: 'pell',\n  format: 'umd',\n  exports: 'named',\n  plugins: [babel({ exclude: 'node_modules/**' })].concat(\n    minimize\n      ? [\n        uglify({\n          compress: { warnings: false },\n          mangle: true,\n          sourceMap: false\n        })\n      ]\n      : []\n  )\n})\n\ngulp.task('script', () => {\n  gulp.src('./src/*.js')\n    .pipe(rollup(rollupConfig(false)))\n    .pipe(size({ showFiles: true }))\n    .pipe(gulp.dest('./dist'))\n\n  gulp.src('./src/*.js')\n    .pipe(rollup(rollupConfig(true)))\n    .pipe(rename('pell.min.js'))\n    .pipe(size({ showFiles: true }))\n    .pipe(size({ gzip: true, showFiles: true }))\n    .pipe(gulp.dest('./dist'))\n})\n\ngulp.task('style', () => {\n  gulp.src(['./src/pell.scss'])\n    .pipe(sass().on('error', sass.logError))\n    .pipe(gulp.dest('./dist'))\n    .pipe(cssnano())\n    .pipe(rename('pell.min.css'))\n    .pipe(gulp.dest('./dist'))\n})\n\ngulp.task('default', ['clean'], () => {\n  run('script', 'style')\n  gulp.watch('./src/pell.scss', ['style'])\n  gulp.watch('./src/pell.js', ['script'])\n})\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"pell\",\n  \"description\": \"pell - the simplest and smallest WYSIWYG text editor for web, with no dependencies\",\n  \"author\": \"Jared Reich\",\n  \"version\": \"1.0.6\",\n  \"main\": \"./dist/pell.min.js\",\n  \"scripts\": {\n    \"dev\": \"gulp\",\n    \"build\": \"gulp clean && gulp script && gulp style\",\n    \"lint\": \"./node_modules/.bin/eslint .js ./\"\n  },\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/jaredreich/pell.git\"\n  },\n  \"keywords\": [\n    \"text editor\",\n    \"editor\",\n    \"rich text\",\n    \"wysiwyg\",\n    \"contenteditable\"\n  ],\n  \"license\": \"MIT\",\n  \"bugs\": {\n    \"url\": \"https://github.com/jaredreich/pell/issues\"\n  },\n  \"homepage\": \"https://github.com/jaredreich/pell\",\n  \"devDependencies\": {\n    \"babel-core\": \"6.23.1\",\n    \"babel-preset-es2015\": \"6.22.0\",\n    \"babel-preset-stage-0\": \"6.22.0\",\n    \"del\": \"2.2.2\",\n    \"eslint-config-jared\": \"1.2.0\",\n    \"gulp\": \"3.9.1\",\n    \"gulp-cssnano\": \"2.1.2\",\n    \"gulp-rename\": \"1.2.2\",\n    \"gulp-rollup\": \"2.14.0\",\n    \"gulp-sass\": \"3.1.0\",\n    \"gulp-size\": \"2.1.0\",\n    \"rollup\": \"0.45.1\",\n    \"rollup-plugin-babel\": \"2.7.1\",\n    \"rollup-plugin-uglify\": \"2.0.1\",\n    \"run-sequence\": \"1.2.2\"\n  }\n}\n"
  },
  {
    "path": "src/pell.js",
    "content": "const defaultParagraphSeparatorString = 'defaultParagraphSeparator'\nconst formatBlock = 'formatBlock'\nconst addEventListener = (parent, type, listener) => parent.addEventListener(type, listener)\nconst appendChild = (parent, child) => parent.appendChild(child)\nconst createElement = tag => document.createElement(tag)\nconst queryCommandState = command => document.queryCommandState(command)\nconst queryCommandValue = command => document.queryCommandValue(command)\n\nexport const exec = (command, value = null) => document.execCommand(command, false, value)\n\nconst defaultActions = {\n  bold: {\n    icon: '<b>B</b>',\n    title: 'Bold',\n    state: () => queryCommandState('bold'),\n    result: () => exec('bold')\n  },\n  italic: {\n    icon: '<i>I</i>',\n    title: 'Italic',\n    state: () => queryCommandState('italic'),\n    result: () => exec('italic')\n  },\n  underline: {\n    icon: '<u>U</u>',\n    title: 'Underline',\n    state: () => queryCommandState('underline'),\n    result: () => exec('underline')\n  },\n  strikethrough: {\n    icon: '<strike>S</strike>',\n    title: 'Strike-through',\n    state: () => queryCommandState('strikeThrough'),\n    result: () => exec('strikeThrough')\n  },\n  heading1: {\n    icon: '<b>H<sub>1</sub></b>',\n    title: 'Heading 1',\n    result: () => exec(formatBlock, '<h1>')\n  },\n  heading2: {\n    icon: '<b>H<sub>2</sub></b>',\n    title: 'Heading 2',\n    result: () => exec(formatBlock, '<h2>')\n  },\n  paragraph: {\n    icon: '&#182;',\n    title: 'Paragraph',\n    result: () => exec(formatBlock, '<p>')\n  },\n  quote: {\n    icon: '&#8220; &#8221;',\n    title: 'Quote',\n    result: () => exec(formatBlock, '<blockquote>')\n  },\n  olist: {\n    icon: '&#35;',\n    title: 'Ordered List',\n    result: () => exec('insertOrderedList')\n  },\n  ulist: {\n    icon: '&#8226;',\n    title: 'Unordered List',\n    result: () => exec('insertUnorderedList')\n  },\n  code: {\n    icon: '&lt;/&gt;',\n    title: 'Code',\n    result: () => exec(formatBlock, '<pre>')\n  },\n  line: {\n    icon: '&#8213;',\n    title: 'Horizontal Line',\n    result: () => exec('insertHorizontalRule')\n  },\n  link: {\n    icon: '&#128279;',\n    title: 'Link',\n    result: () => {\n      const url = window.prompt('Enter the link URL')\n      if (url) exec('createLink', url)\n    }\n  },\n  image: {\n    icon: '&#128247;',\n    title: 'Image',\n    result: () => {\n      const url = window.prompt('Enter the image URL')\n      if (url) exec('insertImage', url)\n    }\n  }\n}\n\nconst defaultClasses = {\n  actionbar: 'pell-actionbar',\n  button: 'pell-button',\n  content: 'pell-content',\n  selected: 'pell-button-selected'\n}\n\nexport const init = settings => {\n  const actions = settings.actions\n    ? (\n      settings.actions.map(action => {\n        if (typeof action === 'string') return defaultActions[action]\n        else if (defaultActions[action.name]) return { ...defaultActions[action.name], ...action }\n        return action\n      })\n    )\n    : Object.keys(defaultActions).map(action => defaultActions[action])\n\n  const classes = { ...defaultClasses, ...settings.classes }\n\n  const defaultParagraphSeparator = settings[defaultParagraphSeparatorString] || 'div'\n\n  const actionbar = createElement('div')\n  actionbar.className = classes.actionbar\n  appendChild(settings.element, actionbar)\n\n  const content = settings.element.content = createElement('div')\n  content.contentEditable = true\n  content.className = classes.content\n  content.oninput = ({ target: { firstChild } }) => {\n    if (firstChild && firstChild.nodeType === 3) exec(formatBlock, `<${defaultParagraphSeparator}>`)\n    else if (content.innerHTML === '<br>') content.innerHTML = ''\n    settings.onChange(content.innerHTML)\n  }\n  content.onkeydown = event => {\n    if (event.key === 'Enter' && queryCommandValue(formatBlock) === 'blockquote') {\n      setTimeout(() => exec(formatBlock, `<${defaultParagraphSeparator}>`), 0)\n    }\n  }\n  appendChild(settings.element, content)\n\n  actions.forEach(action => {\n    const button = createElement('button')\n    button.className = classes.button\n    button.innerHTML = action.icon\n    button.title = action.title\n    button.setAttribute('type', 'button')\n    button.onclick = () => action.result() && content.focus()\n\n    if (action.state) {\n      const handler = () => button.classList[action.state() ? 'add' : 'remove'](classes.selected)\n      addEventListener(content, 'keyup', handler)\n      addEventListener(content, 'mouseup', handler)\n      addEventListener(button, 'click', handler)\n    }\n\n    appendChild(actionbar, button)\n  })\n\n  if (settings.styleWithCSS) exec('styleWithCSS')\n  exec(defaultParagraphSeparatorString, defaultParagraphSeparator)\n\n  return settings.element\n}\n\nexport default { exec, init }\n"
  },
  {
    "path": "src/pell.scss",
    "content": "$pell-actionbar-color: #FFF !default;\n$pell-border-color: rgba(10, 10, 10, 0.1) !default;\n$pell-border-style: solid !default;\n$pell-border-width: 1px !default;\n$pell-button-height: 30px !default;\n$pell-button-selected-color: #F0F0F0 !default;\n$pell-button-width: 30px !default;\n$pell-content-height: 300px !default;\n$pell-content-padding: 10px !default;\n\n.pell {\n  border: $pell-border-width $pell-border-style $pell-border-color;\n  box-sizing: border-box;\n}\n\n.pell-content {\n  box-sizing: border-box;\n  height: $pell-content-height;\n  outline: 0;\n  overflow-y: auto;\n  padding: $pell-content-padding;\n}\n\n.pell-actionbar {\n  background-color: $pell-actionbar-color;\n  border-bottom: $pell-border-width $pell-border-style $pell-border-color;\n}\n\n.pell-button {\n  background-color: transparent;\n  border: none;\n  cursor: pointer;\n  height: $pell-button-height;\n  outline: 0;\n  width: $pell-button-width;\n  vertical-align: bottom;\n}\n\n.pell-button-selected {\n  background-color: $pell-button-selected-color;\n}\n"
  }
]