diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..0915b639 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = false + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true \ No newline at end of file diff --git a/.eslintrc b/.eslintrc index c06c48f7..3b962db9 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,7 +1,8 @@ { /** Enable ES6 features */ "parserOptions": { - "ecmaVersion": 6, + "ecmaVersion": 2017, + "sourceType": "module" }, "rules": { @@ -18,7 +19,7 @@ "no-shadow": 2, "no-undef-init": 2, "no-undef": 2, - "no-unused-vars": 1, + "no-unused-vars": 0, /** Style */ "array-bracket-spacing": [2, "never", { @@ -26,7 +27,10 @@ "objectsInArrays": true, "arraysInArrays": true }], - "quotes": [2, "single", "avoid-escape"], + "quotes": [2, "single", { + "avoidEscape": true, + "allowTemplateLiterals": true + }], "eqeqeq": 0, "brace-style": [2, "1tbs"], "comma-spacing": [2, { @@ -38,7 +42,7 @@ "no-nested-ternary": 1, "no-trailing-spaces": 2, "no-mixed-spaces-and-tabs": 2, - "padded-blocks": [2, "always"], + "padded-blocks": [2, "never"], "space-before-blocks": 1, "space-before-function-paren": [1, { "anonymous": "always", @@ -49,7 +53,7 @@ "markers": ["=", "!"] }], "semi": [2, "always"], - "indent": [2, 4, { + "indent": [2, 2, { "SwitchCase": 1 }], "camelcase": [2, { @@ -71,7 +75,14 @@ "FormData": true, "XMLHttpRequest": true, "ActiveXObject": true, - "RegExp": true - + "RegExp": true, + "Module": true, + "Node": true, + "Element": true, + "Proxy": true, + "Symbol": true, + "$": true, + "_": true, + "setTimeout": true } -} \ No newline at end of file +} diff --git a/.gitignore b/.gitignore index 93782c6f..3c8ec3b7 100644 --- a/.gitignore +++ b/.gitignore @@ -6,7 +6,5 @@ Thumbs.db /*.sublime-workspace node_modules/* -/server/ -/uploads/ -plugins/personality/ +npm-debug.log diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..9594c590 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,21 @@ +[submodule "example/tools/inline-code"] + path = example/tools/inline-code + url = https://github.com/codex-editor/inline-code +[submodule "example/tools/header"] + path = example/tools/header + url = https://github.com/codex-editor/header +[submodule "example/tools/delimiter"] + path = example/tools/delimiter + url = https://github.com/codex-editor/delimiter +[submodule "example/tools/list"] + path = example/tools/list + url = https://github.com/codex-editor/list +[submodule "example/tools/quote"] + path = example/tools/quote + url = https://github.com/codex-editor/quote +[submodule "example/tools/simple-image"] + path = example/tools/simple-image + url = https://github.com/codex-editor/simple-image +[submodule "src/components/tools/paragraph"] + path = src/components/tools/paragraph + url = https://github.com/codex-editor/paragraph diff --git a/.jshintrc b/.jshintrc index 49d68f10..6cc12082 100644 --- a/.jshintrc +++ b/.jshintrc @@ -12,11 +12,15 @@ // Define globals exposed by CodeX Team "predef": [ - "codex" + "codex", + "_", + "$", + "editorModules", + "Module" ], // Allow ES6. - "esversion": 6, + "esversion": 2017, /* * ENFORCING OPTIONS @@ -61,4 +65,4 @@ // Suppress warnings about == null comparisons. "eqnull": true -} \ No newline at end of file +} diff --git a/.postcssrc b/.postcssrc new file mode 100644 index 00000000..3780c0bc --- /dev/null +++ b/.postcssrc @@ -0,0 +1,16 @@ +plugins: + postcss-smart-import: {} + postcss-custom-properties: {} + postcss-apply: {} + postcss-custom-media: {} + postcss-media-minmax: {} + postcss-custom-selectors: {} + postcss-nested-ancestors: {} + postcss-nesting: {} + postcss-nested: {} + postcss-color-mod-function: {} + postcss-color-hex-alpha: {} + postcss-font-variant: {} + postcss-font-family-system-ui: {} + autoprefixer: + browsers: ['last 2 versions', '> 1%'] diff --git a/.stylelintrc b/.stylelintrc index 57c9fa2a..23b203aa 100644 --- a/.stylelintrc +++ b/.stylelintrc @@ -1,12 +1,17 @@ { "rules": { - "at-rule-empty-line-before": [ "always", { - except: [ - "blockless-after-same-name-blockless", - "first-nested", - ], - ignore: ["after-comment"], - } ], + "at-rule-empty-line-before": [ + "always", + { + except: [ + "blockless-after-same-name-blockless", + "first-nested", + ], + ignore: [ + "after-comment" + ], + } + ], "at-rule-name-case": "lower", "at-rule-name-space-after": "always-single-line", "at-rule-semicolon-newline-after": "always", @@ -21,27 +26,42 @@ "color-hex-case": "lower", "color-hex-length": "short", "color-no-invalid-hex": true, - "comment-empty-line-before": [ "always", { - except: ["first-nested"], - ignore: ["stylelint-commands"], - } ], + "comment-empty-line-before": [ + "always", + { + except: [ + "first-nested" + ], + ignore: [ + "stylelint-commands" + ], + } + ], "comment-no-empty": true, "comment-whitespace-inside": "always", - "custom-property-empty-line-before": [ "always", { - except: [ - "after-custom-property", - "first-nested", - ], - ignore: [ - "after-comment", - "inside-single-line-block", - ], - } ], + "custom-property-empty-line-before": [ + "always", + { + except: [ + "after-custom-property", + "first-nested", + ], + ignore: [ + "after-comment", + "inside-single-line-block", + ], + } + ], "declaration-bang-space-after": "never", "declaration-bang-space-before": "always", - "declaration-block-no-duplicate-properties": [ true, { - ignore: ["consecutive-duplicates-with-different-values"], - } ], + "declaration-block-no-duplicate-properties": [ + true, + { + ignore: [ + "consecutive-duplicates-with-different-values" + ], + } + ], "declaration-block-no-redundant-longhand-properties": true, "declaration-block-no-shorthand-property-overrides": true, "declaration-block-semicolon-newline-after": "always-multi-line", @@ -52,16 +72,19 @@ "declaration-colon-newline-after": "always-multi-line", "declaration-colon-space-after": "always-single-line", "declaration-colon-space-before": "never", - "declaration-empty-line-before": [ "always", { - except: [ - "after-declaration", - "first-nested", - ], - ignore: [ - "after-comment", - "inside-single-line-block", - ], - } ], + "declaration-empty-line-before": [ + "always", + { + except: [ + "after-declaration", + "first-nested", + ], + ignore: [ + "after-comment", + "inside-single-line-block", + ], + } + ], "font-family-no-duplicate-names": true, "function-calc-no-unspaced-operator": true, "function-comma-newline-after": "always-multi-line", @@ -96,13 +119,17 @@ "number-no-trailing-zeros": true, "property-case": "lower", "property-no-unknown": true, - "rule-nested-empty-line-before": [ "always-multi-line", { - except: ["first-nested"], - ignore: ["after-comment"], - } ], - "rule-non-nested-empty-line-before": [ "always-multi-line", { - ignore: ["after-comment"], - } ], + "rule-empty-line-before": [ + "always-multi-line", + { + except: [ + "first-nested" + ], + ignore: [ + "after-comment" + ], + } + ], "selector-attribute-brackets-space-inside": "never", "selector-attribute-operator-space-after": "never", "selector-attribute-operator-space-before": "never", @@ -129,4 +156,4 @@ "value-list-comma-space-before": "never", "value-list-max-empty-lines": 0, }, -} \ No newline at end of file +} diff --git a/README.md b/README.md index e947177c..116087db 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,222 @@ -### NOTE +

-At the moment we working hard on the CodeX Editor 2.0 with completely new core, new API and great new documentation. Stay tuned and subsribe to us on the Twitter. Be in the front. +![](https://flat.badgen.net/badge/CodeX%20Editor/v2.0.0/blue?icon=npm) -[http://twitter.com/codex_team](http://twitter.com/codex_team) +## Version 2.0-beta is here! + +We glad to introduce the next version of CodeX Editor. Totally new core, structure and plugins — that was an impressive adventure 🤓. + +Welcome to testing stage. Please, join a [public Telegram-chat](//t.me/codex_editor) where you always find a support. + +## Documentation + +While we develop the new Documentation Site with all stuff, you can check some available docs at the [docs/](docs/) dir. + +- [Installation](docs/installation.md) +- [How to use](docs/usage.md) +- [How to create a Block Tool Plugin](docs/tools.md) +- [How to create an Inline Tool Plugin](docs/tools-inline.md) +- [API for Tools](src/components/interfaces/api.ts) + +Sorry if we missed something. You can join a [Telegram-chat](//t.me/codex_editor) and ask a question. --- -# CodeX Editor -Next generation Editor for modern web applications. +# So how to use CodeX Editor -### Block-styled +## Basics -Entry composed by different and extended list of Blocks +CodeX Editor is a Block-Styled editor. Blocks is a structural units, of which the Entry is composed. +For example, `Paragraph`, `Heading`, `Image`, `Video`, `List` are Blocks. Each Block is represented by a Plugin. +We have [many](http://github.com/codex-editor) ready-to-use Plugins and the [simple API](tools.md) for creation new ones. -### JSON as output +So how to use the Editor after [Installation](installation.md). -Outputs clear Blocks data via JSON +- Create new Blocks by Enter or with the Plus Button +- Press `TAB` or click on the Plus Button to view the Toolbox +- Press `TAB` again to leaf Toolbox and select a Block you need. Then press Enter. -```json -{ - "blocks": [ - { - "type": "paragraph", - "data": { - "text": "There is a first paragraph" - } + + ![](https://github.com/codex-editor/list/raw/master/assets/example.gif) + +- Select text fragment and apply a style or insert a link from the Inline Toolbar + +![](https://capella.pics/7ccbcfcd-1c49-4674-bea7-71021468a1bd.jpg) + +- Use «three-dots» button on the right to open Block Settings. From here, you can move and delete a Block +or apply Tool's settings, if it provided. For example, set a Heading level or List style. + +![](https://capella.pics/01a55381-46cd-47c7-b92e-34765434f2ca.jpg) + +## Shortcuts + +We really appreciate shortcuts. So there are few presets. + +Action | Shortcut | Restrictions +-- | -- | -- +`TAB` | Show/leaf a Toolbox. | On empty block +`SHIFT+TAB` | Leaf back a Toolbox. | While Toolbox is opened +`ENTER` | Create a Block | While Toolbox is opened and some Tool is selected +`CMD+B` | Bold style | On selection +`CMD+I` | Italic style | On selection +`CMD+K` | Insert a link | On selection + +Also we support shortcuts on the all type of Tools. Specify a shortcut with the Tools configuration. For example: + +```js +var editor = CodexEditor({ + //... + tools: { + header: { + class: Header, + shortcut: 'CMD+SHIFT+H' }, - { - "type": "header", - "data": { - "text": "Aaand here is a Header", - "type": "H2" - } - }, - ... - ] -} + list: { + class: List, + shortcut: 'CMD+SHIFT+L' + } + } + //... + }); + ``` -### API oriented -Focused on the Plugins API that allows to create amazing Blocks +# Installation Guide + +There are few steps to run CodeX Editor on your site. + +1. [Load Editor's core](#load-editors-core) +2. [Load Tools](#load-tools) +3. [Initialize Editor's instance](#create-editor-instance) + +## Load Editor's core + +Firstly you need to get CodeX Editor itself. It is a [minified script](../build/codex-editor.js) with minimal available + +Choose the most usable method of getting Editor for you. + +- Node package +- Source from CDN +- Local file from project + +### Node.js + +Install the package via NPM or Yarn + +```shell +npm i codex.editor --save-dev +``` + +Include module at your application + +```javascript +const CodexEditor = require('codex.editor'); +``` + +### Use from CDN + +You can load specific version of package from [jsDelivr CDN](https://www.jsdelivr.com/package/npm/codex.editor). + +`https://cdn.jsdelivr.net/npm/codex.editor@2.0.0` + +Then require this script. + +```html + +``` + +### Save sources to project + +Copy [codex-editor.js](../build/codex-editor.js) file to your project and load it. + +```html + +``` + +## Load Tools + +Each Block at the CodeX Editor represented by [Tools](tools.md). There are simple external scripts with own logic. To start using the Editor, you should connect at least one Block Tool. + +For example check out our [Paragraph](https://github.com/codex-editor/paragraph) Tool that represents simple text block. + +Each Tool should have an installation guide. You can install Paragraph Tool via the same ways as an Editor (Node.js, CDN, local file). + +Check [CodeX Editor's community](https://github.com/codex-editor) to see Tools examples. + +**Example:** use Paragragh from CDN + +```html + +``` + +## Create Editor instance + +Create an instance of CodeX Editor and pass [Configuration Object](../src/components/interfaces/editor-config.ts). +Minimal params is a `holderId`, `tools` list and `initialBlock` marker. + +```html +
+``` + +You can create a simple Editor only with a default Paragraph Tool by passing a string with element's Id (wrapper for Editor) as a configuration param or use default `codex-editor`. + +```javascript +var editor = new CodexEditor(); + +// equals + +var editor = new CodexEditor('codex-editor'); +```` + +Or pass a whole settings object. + +```javascript +var editor = new CodexEditor({ + /** + * Create a holder for the Editor and pass its ID + */ + holderId : 'codex-editor', + + /** + * Available Tools list. + * Pass Tool's class or Settings object for each Tool you want to use + */ + tools: { + paragraph: { + class: Paragraph, + inlineToolbar : true + }, + // ... + }, + + /** + * What Block will be inserted by default + */ + initialBlock : 'paragraph', + + /** + * Previously saved data that should be rendered + */ + data: {} +}); +``` + +## Saving Data + +Call `editor.saver.save()` and handle returned Promise with saved data. + +```javascript +editor.saver.save() + .then((savedData) => { + console.log(savedData); + }); +``` + +## Example + +Take a look at the [example.html](../example/example.html) to view more detailed examples. -### Native JS -Zero-dependency — use everywhere you want diff --git a/build/codex-editor.js b/build/codex-editor.js new file mode 100644 index 00000000..7e826dd9 --- /dev/null +++ b/build/codex-editor.js @@ -0,0 +1,1540 @@ +!function(e,o){"object"==typeof exports&&"object"==typeof module?module.exports=o():"function"==typeof define&&define.amd?define([],o):"object"==typeof exports?exports.CodexEditor=o():e.CodexEditor=o()}(window,function(){return function(e){var o={};function t(n){if(o[n])return o[n].exports;var s=o[n]={i:n,l:!1,exports:{}};return e[n].call(s.exports,s,s.exports,t),s.l=!0,s.exports}return t.m=e,t.c=o,t.d=function(e,o,n){t.o(e,o)||Object.defineProperty(e,o,{enumerable:!0,get:n})},t.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},t.t=function(e,o){if(1&o&&(e=t(e)),8&o)return e;if(4&o&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(t.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&o&&"string"!=typeof e)for(var s in e)t.d(n,s,function(o){return e[o]}.bind(null,s));return n},t.n=function(e){var o=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(o,"a",o),o},t.o=function(e,o){return Object.prototype.hasOwnProperty.call(e,o)},t.p="",t(t.s=0)}({"./build/sprite.svg": +/*!**************************!*\ + !*** ./build/sprite.svg ***! + \**************************/ +/*! no static exports found */function(e,o){e.exports='\n\n\r\n \r\n\n\n\r\n \r\n\n\n\r\n \r\n\n\n\r\n \r\n\n\n\r\n \r\n \r\n \r\n \r\n \r\n\n\n\r\n \r\n\n\n\r\n \r\n\n\n\r\n \r\n\n\n\r\n \r\n\n'},"./node_modules/@codexteam/shortcuts/lib/shortcuts.js": +/*!************************************************************!*\ + !*** ./node_modules/@codexteam/shortcuts/lib/shortcuts.js ***! + \************************************************************/ +/*! no static exports found */function(e,o,t){ +/*! + * Library for handling keyboard shortcuts + * @copyright undefined + * @license MIT + * @author CodeX (https://ifmo.su) + * @version 1.0.0 + */ +"undefined"!=typeof self&&self,e.exports=function(e){function o(n){if(t[n])return t[n].exports;var s=t[n]={i:n,l:!1,exports:{}};return e[n].call(s.exports,s,s.exports,o),s.l=!0,s.exports}var t={};return o.m=e,o.c=t,o.d=function(e,t,n){o.o(e,t)||Object.defineProperty(e,t,{configurable:!1,enumerable:!0,get:n})},o.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(t,"a",t),t},o.o=function(e,o){return Object.prototype.hasOwnProperty.call(e,o)},o.p="",o(o.s=0)}([function(e,o,t){"use strict";Object.defineProperty(o,"__esModule",{value:!0});var n=function(){function e(e,o){for(var t=0;t1)for(var t in r)r[t].includes(e[o])&&(this.commands[t]=!0);else this.keys[e[o]]=!0}},{key:"execute",value:function(e){var o=e.ctrlKey||e.metaKey,t=e.shiftKey,n=e.altKey,r={CMD:o,SHIFT:t,ALT:n},i=void 0,u=!0;for(i in this.commands)u=u&&r[i];var l=void 0,c=!0;for(l in this.keys)c=c&&e.keyCode===s[l];u&&c&&this.callback(e)}},{key:"remove",value:function(){this.element.removeEventListener("keydown",this.executeShortcut)}}]),e}();o.default=i}])},"./node_modules/babel-core/register.js": +/*!*********************************************!*\ + !*** ./node_modules/babel-core/register.js ***! + \*********************************************/ +/*! no static exports found */function(e,o,t){e.exports=t(/*! babel-register */"./node_modules/babel-register/lib/browser.js")},"./node_modules/babel-polyfill/lib/index.js": +/*!**************************************************!*\ + !*** ./node_modules/babel-polyfill/lib/index.js ***! + \**************************************************/ +/*! no static exports found */function(e,o,t){"use strict";(function(e){if(t(/*! core-js/shim */"./node_modules/core-js/shim.js"),t(/*! regenerator-runtime/runtime */"./node_modules/regenerator-runtime/runtime.js"),t(/*! core-js/fn/regexp/escape */"./node_modules/core-js/fn/regexp/escape.js"),e._babelPolyfill)throw new Error("only one instance of babel-polyfill is allowed");e._babelPolyfill=!0;var o="defineProperty";function n(e,t,n){e[t]||Object[o](e,t,{writable:!0,configurable:!0,value:n})}n(String.prototype,"padLeft","".padStart),n(String.prototype,"padRight","".padEnd),"pop,reverse,shift,keys,values,entries,indexOf,every,some,forEach,map,filter,find,findIndex,includes,join,slice,concat,push,splice,unshift,sort,lastIndexOf,reduce,reduceRight,copyWithin,fill".split(",").forEach(function(e){[][e]&&n(Array,e,Function.call.bind([][e]))})}).call(this,t(/*! ./../../webpack/buildin/global.js */"./node_modules/webpack/buildin/global.js"))},"./node_modules/babel-register/lib/browser.js": +/*!****************************************************!*\ + !*** ./node_modules/babel-register/lib/browser.js ***! + \****************************************************/ +/*! no static exports found */function(e,o,t){"use strict";o.__esModule=!0,o.default=function(){},e.exports=o.default},"./node_modules/core-js/fn/regexp/escape.js": +/*!**************************************************!*\ + !*** ./node_modules/core-js/fn/regexp/escape.js ***! + \**************************************************/ +/*! no static exports found */function(e,o,t){t(/*! ../../modules/core.regexp.escape */"./node_modules/core-js/modules/core.regexp.escape.js"),e.exports=t(/*! ../../modules/_core */"./node_modules/core-js/modules/_core.js").RegExp.escape},"./node_modules/core-js/modules/_a-function.js": +/*!*****************************************************!*\ + !*** ./node_modules/core-js/modules/_a-function.js ***! + \*****************************************************/ +/*! no static exports found */function(e,o){e.exports=function(e){if("function"!=typeof e)throw TypeError(e+" is not a function!");return e}},"./node_modules/core-js/modules/_a-number-value.js": +/*!*********************************************************!*\ + !*** ./node_modules/core-js/modules/_a-number-value.js ***! + \*********************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_cof */"./node_modules/core-js/modules/_cof.js");e.exports=function(e,o){if("number"!=typeof e&&"Number"!=n(e))throw TypeError(o);return+e}},"./node_modules/core-js/modules/_add-to-unscopables.js": +/*!*************************************************************!*\ + !*** ./node_modules/core-js/modules/_add-to-unscopables.js ***! + \*************************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_wks */"./node_modules/core-js/modules/_wks.js")("unscopables"),s=Array.prototype;void 0==s[n]&&t(/*! ./_hide */"./node_modules/core-js/modules/_hide.js")(s,n,{}),e.exports=function(e){s[n][e]=!0}},"./node_modules/core-js/modules/_an-instance.js": +/*!******************************************************!*\ + !*** ./node_modules/core-js/modules/_an-instance.js ***! + \******************************************************/ +/*! no static exports found */function(e,o){e.exports=function(e,o,t,n){if(!(e instanceof o)||void 0!==n&&n in e)throw TypeError(t+": incorrect invocation!");return e}},"./node_modules/core-js/modules/_an-object.js": +/*!****************************************************!*\ + !*** ./node_modules/core-js/modules/_an-object.js ***! + \****************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_is-object */"./node_modules/core-js/modules/_is-object.js");e.exports=function(e){if(!n(e))throw TypeError(e+" is not an object!");return e}},"./node_modules/core-js/modules/_array-copy-within.js": +/*!************************************************************!*\ + !*** ./node_modules/core-js/modules/_array-copy-within.js ***! + \************************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_to-object */"./node_modules/core-js/modules/_to-object.js"),s=t(/*! ./_to-absolute-index */"./node_modules/core-js/modules/_to-absolute-index.js"),r=t(/*! ./_to-length */"./node_modules/core-js/modules/_to-length.js");e.exports=[].copyWithin||function(e,o){var t=n(this),i=r(t.length),u=s(e,i),l=s(o,i),c=arguments.length>2?arguments[2]:void 0,d=Math.min((void 0===c?i:s(c,i))-l,i-u),a=1;for(l0;)l in t?t[u]=t[l]:delete t[u],u+=a,l+=a;return t}},"./node_modules/core-js/modules/_array-fill.js": +/*!*****************************************************!*\ + !*** ./node_modules/core-js/modules/_array-fill.js ***! + \*****************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_to-object */"./node_modules/core-js/modules/_to-object.js"),s=t(/*! ./_to-absolute-index */"./node_modules/core-js/modules/_to-absolute-index.js"),r=t(/*! ./_to-length */"./node_modules/core-js/modules/_to-length.js");e.exports=function(e){for(var o=n(this),t=r(o.length),i=arguments.length,u=s(i>1?arguments[1]:void 0,t),l=i>2?arguments[2]:void 0,c=void 0===l?t:s(l,t);c>u;)o[u++]=e;return o}},"./node_modules/core-js/modules/_array-from-iterable.js": +/*!**************************************************************!*\ + !*** ./node_modules/core-js/modules/_array-from-iterable.js ***! + \**************************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_for-of */"./node_modules/core-js/modules/_for-of.js");e.exports=function(e,o){var t=[];return n(e,!1,t.push,t,o),t}},"./node_modules/core-js/modules/_array-includes.js": +/*!*********************************************************!*\ + !*** ./node_modules/core-js/modules/_array-includes.js ***! + \*********************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_to-iobject */"./node_modules/core-js/modules/_to-iobject.js"),s=t(/*! ./_to-length */"./node_modules/core-js/modules/_to-length.js"),r=t(/*! ./_to-absolute-index */"./node_modules/core-js/modules/_to-absolute-index.js");e.exports=function(e){return function(o,t,i){var u,l=n(o),c=s(l.length),d=r(i,c);if(e&&t!=t){for(;c>d;)if((u=l[d++])!=u)return!0}else for(;c>d;d++)if((e||d in l)&&l[d]===t)return e||d||0;return!e&&-1}}},"./node_modules/core-js/modules/_array-methods.js": +/*!********************************************************!*\ + !*** ./node_modules/core-js/modules/_array-methods.js ***! + \********************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_ctx */"./node_modules/core-js/modules/_ctx.js"),s=t(/*! ./_iobject */"./node_modules/core-js/modules/_iobject.js"),r=t(/*! ./_to-object */"./node_modules/core-js/modules/_to-object.js"),i=t(/*! ./_to-length */"./node_modules/core-js/modules/_to-length.js"),u=t(/*! ./_array-species-create */"./node_modules/core-js/modules/_array-species-create.js");e.exports=function(e,o){var t=1==e,l=2==e,c=3==e,d=4==e,a=6==e,f=5==e||a,m=o||u;return function(o,u,p){for(var j,_,h=r(o),v=s(h),b=n(u,p,3),g=i(v.length),y=0,k=t?m(o,g):l?m(o,0):void 0;g>y;y++)if((f||y in v)&&(_=b(j=v[y],y,h),e))if(t)k[y]=_;else if(_)switch(e){case 3:return!0;case 5:return j;case 6:return y;case 2:k.push(j)}else if(d)return!1;return a?-1:c||d?d:k}}},"./node_modules/core-js/modules/_array-reduce.js": +/*!*******************************************************!*\ + !*** ./node_modules/core-js/modules/_array-reduce.js ***! + \*******************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_a-function */"./node_modules/core-js/modules/_a-function.js"),s=t(/*! ./_to-object */"./node_modules/core-js/modules/_to-object.js"),r=t(/*! ./_iobject */"./node_modules/core-js/modules/_iobject.js"),i=t(/*! ./_to-length */"./node_modules/core-js/modules/_to-length.js");e.exports=function(e,o,t,u,l){n(o);var c=s(e),d=r(c),a=i(c.length),f=l?a-1:0,m=l?-1:1;if(t<2)for(;;){if(f in d){u=d[f],f+=m;break}if(f+=m,l?f<0:a<=f)throw TypeError("Reduce of empty array with no initial value")}for(;l?f>=0:a>f;f+=m)f in d&&(u=o(u,d[f],f,c));return u}},"./node_modules/core-js/modules/_array-species-constructor.js": +/*!********************************************************************!*\ + !*** ./node_modules/core-js/modules/_array-species-constructor.js ***! + \********************************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_is-object */"./node_modules/core-js/modules/_is-object.js"),s=t(/*! ./_is-array */"./node_modules/core-js/modules/_is-array.js"),r=t(/*! ./_wks */"./node_modules/core-js/modules/_wks.js")("species");e.exports=function(e){var o;return s(e)&&("function"!=typeof(o=e.constructor)||o!==Array&&!s(o.prototype)||(o=void 0),n(o)&&null===(o=o[r])&&(o=void 0)),void 0===o?Array:o}},"./node_modules/core-js/modules/_array-species-create.js": +/*!***************************************************************!*\ + !*** ./node_modules/core-js/modules/_array-species-create.js ***! + \***************************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_array-species-constructor */"./node_modules/core-js/modules/_array-species-constructor.js");e.exports=function(e,o){return new(n(e))(o)}},"./node_modules/core-js/modules/_bind.js": +/*!***********************************************!*\ + !*** ./node_modules/core-js/modules/_bind.js ***! + \***********************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_a-function */"./node_modules/core-js/modules/_a-function.js"),s=t(/*! ./_is-object */"./node_modules/core-js/modules/_is-object.js"),r=t(/*! ./_invoke */"./node_modules/core-js/modules/_invoke.js"),i=[].slice,u={};e.exports=Function.bind||function(e){var o=n(this),t=i.call(arguments,1),l=function(){var n=t.concat(i.call(arguments));return this instanceof l?function(e,o,t){if(!(o in u)){for(var n=[],s=0;s1?arguments[1]:void 0,3);t=t?t.n:this._f;)for(n(t.v,t.k,this);t&&t.r;)t=t.p},has:function(e){return!!_(p(this,o),e)}}),f&&n(d.prototype,"size",{get:function(){return p(this,o)[j]}}),d},def:function(e,o,t){var n,s,r=_(e,o);return r?r.v=t:(e._l=r={i:s=m(o,!0),k:o,v:t,p:n=e._l,n:void 0,r:!1},e._f||(e._f=r),n&&(n.n=r),e[j]++,"F"!==s&&(e._i[s]=r)),e},getEntry:_,setStrong:function(e,o,t){c(e,o,function(e,t){this._t=p(e,o),this._k=t,this._l=void 0},function(){for(var e=this._k,o=this._l;o&&o.r;)o=o.p;return this._t&&(this._l=o=o?o.n:this._t._f)?d(0,"keys"==e?o.k:"values"==e?o.v:[o.k,o.v]):(this._t=void 0,d(1))},t?"entries":"values",!t,!0),a(o)}}},"./node_modules/core-js/modules/_collection-to-json.js": +/*!*************************************************************!*\ + !*** ./node_modules/core-js/modules/_collection-to-json.js ***! + \*************************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_classof */"./node_modules/core-js/modules/_classof.js"),s=t(/*! ./_array-from-iterable */"./node_modules/core-js/modules/_array-from-iterable.js");e.exports=function(e){return function(){if(n(this)!=e)throw TypeError(e+"#toJSON isn't generic");return s(this)}}},"./node_modules/core-js/modules/_collection-weak.js": +/*!**********************************************************!*\ + !*** ./node_modules/core-js/modules/_collection-weak.js ***! + \**********************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_redefine-all */"./node_modules/core-js/modules/_redefine-all.js"),s=t(/*! ./_meta */"./node_modules/core-js/modules/_meta.js").getWeak,r=t(/*! ./_an-object */"./node_modules/core-js/modules/_an-object.js"),i=t(/*! ./_is-object */"./node_modules/core-js/modules/_is-object.js"),u=t(/*! ./_an-instance */"./node_modules/core-js/modules/_an-instance.js"),l=t(/*! ./_for-of */"./node_modules/core-js/modules/_for-of.js"),c=t(/*! ./_array-methods */"./node_modules/core-js/modules/_array-methods.js"),d=t(/*! ./_has */"./node_modules/core-js/modules/_has.js"),a=t(/*! ./_validate-collection */"./node_modules/core-js/modules/_validate-collection.js"),f=c(5),m=c(6),p=0,j=function(e){return e._l||(e._l=new _)},_=function(){this.a=[]},h=function(e,o){return f(e.a,function(e){return e[0]===o})};_.prototype={get:function(e){var o=h(this,e);if(o)return o[1]},has:function(e){return!!h(this,e)},set:function(e,o){var t=h(this,e);t?t[1]=o:this.a.push([e,o])},delete:function(e){var o=m(this.a,function(o){return o[0]===e});return~o&&this.a.splice(o,1),!!~o}},e.exports={getConstructor:function(e,o,t,r){var c=e(function(e,n){u(e,c,o,"_i"),e._t=o,e._i=p++,e._l=void 0,void 0!=n&&l(n,t,e[r],e)});return n(c.prototype,{delete:function(e){if(!i(e))return!1;var t=s(e);return!0===t?j(a(this,o)).delete(e):t&&d(t,this._i)&&delete t[this._i]},has:function(e){if(!i(e))return!1;var t=s(e);return!0===t?j(a(this,o)).has(e):t&&d(t,this._i)}}),c},def:function(e,o,t){var n=s(r(o),!0);return!0===n?j(e).set(o,t):n[e._i]=t,e},ufstore:j}},"./node_modules/core-js/modules/_collection.js": +/*!*****************************************************!*\ + !*** ./node_modules/core-js/modules/_collection.js ***! + \*****************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_global */"./node_modules/core-js/modules/_global.js"),s=t(/*! ./_export */"./node_modules/core-js/modules/_export.js"),r=t(/*! ./_redefine */"./node_modules/core-js/modules/_redefine.js"),i=t(/*! ./_redefine-all */"./node_modules/core-js/modules/_redefine-all.js"),u=t(/*! ./_meta */"./node_modules/core-js/modules/_meta.js"),l=t(/*! ./_for-of */"./node_modules/core-js/modules/_for-of.js"),c=t(/*! ./_an-instance */"./node_modules/core-js/modules/_an-instance.js"),d=t(/*! ./_is-object */"./node_modules/core-js/modules/_is-object.js"),a=t(/*! ./_fails */"./node_modules/core-js/modules/_fails.js"),f=t(/*! ./_iter-detect */"./node_modules/core-js/modules/_iter-detect.js"),m=t(/*! ./_set-to-string-tag */"./node_modules/core-js/modules/_set-to-string-tag.js"),p=t(/*! ./_inherit-if-required */"./node_modules/core-js/modules/_inherit-if-required.js");e.exports=function(e,o,t,j,_,h){var v=n[e],b=v,g=_?"set":"add",y=b&&b.prototype,k={},w=function(e){var o=y[e];r(y,e,"delete"==e?function(e){return!(h&&!d(e))&&o.call(this,0===e?0:e)}:"has"==e?function(e){return!(h&&!d(e))&&o.call(this,0===e?0:e)}:"get"==e?function(e){return h&&!d(e)?void 0:o.call(this,0===e?0:e)}:"add"==e?function(e){return o.call(this,0===e?0:e),this}:function(e,t){return o.call(this,0===e?0:e,t),this})};if("function"==typeof b&&(h||y.forEach&&!a(function(){(new b).entries().next()}))){var x=new b,E=x[g](h?{}:-0,1)!=x,S=a(function(){x.has(1)}),T=f(function(e){new b(e)}),O=!h&&a(function(){for(var e=new b,o=5;o--;)e[g](o,o);return!e.has(-0)});T||((b=o(function(o,t){c(o,b,e);var n=p(new v,o,b);return void 0!=t&&l(t,_,n[g],n),n})).prototype=y,y.constructor=b),(S||O)&&(w("delete"),w("has"),_&&w("get")),(O||E)&&w(g),h&&y.clear&&delete y.clear}else b=j.getConstructor(o,e,_,g),i(b.prototype,t),u.NEED=!0;return m(b,e),k[e]=b,s(s.G+s.W+s.F*(b!=v),k),h||j.setStrong(b,e,_),b}},"./node_modules/core-js/modules/_core.js": +/*!***********************************************!*\ + !*** ./node_modules/core-js/modules/_core.js ***! + \***********************************************/ +/*! no static exports found */function(e,o){var t=e.exports={version:"2.5.7"};"number"==typeof __e&&(__e=t)},"./node_modules/core-js/modules/_create-property.js": +/*!**********************************************************!*\ + !*** ./node_modules/core-js/modules/_create-property.js ***! + \**********************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_object-dp */"./node_modules/core-js/modules/_object-dp.js"),s=t(/*! ./_property-desc */"./node_modules/core-js/modules/_property-desc.js");e.exports=function(e,o,t){o in e?n.f(e,o,s(0,t)):e[o]=t}},"./node_modules/core-js/modules/_ctx.js": +/*!**********************************************!*\ + !*** ./node_modules/core-js/modules/_ctx.js ***! + \**********************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_a-function */"./node_modules/core-js/modules/_a-function.js");e.exports=function(e,o,t){if(n(e),void 0===o)return e;switch(t){case 1:return function(t){return e.call(o,t)};case 2:return function(t,n){return e.call(o,t,n)};case 3:return function(t,n,s){return e.call(o,t,n,s)}}return function(){return e.apply(o,arguments)}}},"./node_modules/core-js/modules/_date-to-iso-string.js": +/*!*************************************************************!*\ + !*** ./node_modules/core-js/modules/_date-to-iso-string.js ***! + \*************************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_fails */"./node_modules/core-js/modules/_fails.js"),s=Date.prototype.getTime,r=Date.prototype.toISOString,i=function(e){return e>9?e:"0"+e};e.exports=n(function(){return"0385-07-25T07:06:39.999Z"!=r.call(new Date(-5e13-1))})||!n(function(){r.call(new Date(NaN))})?function(){if(!isFinite(s.call(this)))throw RangeError("Invalid time value");var e=this,o=e.getUTCFullYear(),t=e.getUTCMilliseconds(),n=o<0?"-":o>9999?"+":"";return n+("00000"+Math.abs(o)).slice(n?-6:-4)+"-"+i(e.getUTCMonth()+1)+"-"+i(e.getUTCDate())+"T"+i(e.getUTCHours())+":"+i(e.getUTCMinutes())+":"+i(e.getUTCSeconds())+"."+(t>99?t:"0"+i(t))+"Z"}:r},"./node_modules/core-js/modules/_date-to-primitive.js": +/*!************************************************************!*\ + !*** ./node_modules/core-js/modules/_date-to-primitive.js ***! + \************************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_an-object */"./node_modules/core-js/modules/_an-object.js"),s=t(/*! ./_to-primitive */"./node_modules/core-js/modules/_to-primitive.js");e.exports=function(e){if("string"!==e&&"number"!==e&&"default"!==e)throw TypeError("Incorrect hint");return s(n(this),"number"!=e)}},"./node_modules/core-js/modules/_defined.js": +/*!**************************************************!*\ + !*** ./node_modules/core-js/modules/_defined.js ***! + \**************************************************/ +/*! no static exports found */function(e,o){e.exports=function(e){if(void 0==e)throw TypeError("Can't call method on "+e);return e}},"./node_modules/core-js/modules/_descriptors.js": +/*!******************************************************!*\ + !*** ./node_modules/core-js/modules/_descriptors.js ***! + \******************************************************/ +/*! no static exports found */function(e,o,t){e.exports=!t(/*! ./_fails */"./node_modules/core-js/modules/_fails.js")(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},"./node_modules/core-js/modules/_dom-create.js": +/*!*****************************************************!*\ + !*** ./node_modules/core-js/modules/_dom-create.js ***! + \*****************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_is-object */"./node_modules/core-js/modules/_is-object.js"),s=t(/*! ./_global */"./node_modules/core-js/modules/_global.js").document,r=n(s)&&n(s.createElement);e.exports=function(e){return r?s.createElement(e):{}}},"./node_modules/core-js/modules/_enum-bug-keys.js": +/*!********************************************************!*\ + !*** ./node_modules/core-js/modules/_enum-bug-keys.js ***! + \********************************************************/ +/*! no static exports found */function(e,o){e.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},"./node_modules/core-js/modules/_enum-keys.js": +/*!****************************************************!*\ + !*** ./node_modules/core-js/modules/_enum-keys.js ***! + \****************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_object-keys */"./node_modules/core-js/modules/_object-keys.js"),s=t(/*! ./_object-gops */"./node_modules/core-js/modules/_object-gops.js"),r=t(/*! ./_object-pie */"./node_modules/core-js/modules/_object-pie.js");e.exports=function(e){var o=n(e),t=s.f;if(t)for(var i,u=t(e),l=r.f,c=0;u.length>c;)l.call(e,i=u[c++])&&o.push(i);return o}},"./node_modules/core-js/modules/_export.js": +/*!*************************************************!*\ + !*** ./node_modules/core-js/modules/_export.js ***! + \*************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_global */"./node_modules/core-js/modules/_global.js"),s=t(/*! ./_core */"./node_modules/core-js/modules/_core.js"),r=t(/*! ./_hide */"./node_modules/core-js/modules/_hide.js"),i=t(/*! ./_redefine */"./node_modules/core-js/modules/_redefine.js"),u=t(/*! ./_ctx */"./node_modules/core-js/modules/_ctx.js"),l=function(e,o,t){var c,d,a,f,m=e&l.F,p=e&l.G,j=e&l.S,_=e&l.P,h=e&l.B,v=p?n:j?n[o]||(n[o]={}):(n[o]||{}).prototype,b=p?s:s[o]||(s[o]={}),g=b.prototype||(b.prototype={});for(c in p&&(t=o),t)a=((d=!m&&v&&void 0!==v[c])?v:t)[c],f=h&&d?u(a,n):_&&"function"==typeof a?u(Function.call,a):a,v&&i(v,c,a,e&l.U),b[c]!=a&&r(b,c,f),_&&g[c]!=a&&(g[c]=a)};n.core=s,l.F=1,l.G=2,l.S=4,l.P=8,l.B=16,l.W=32,l.U=64,l.R=128,e.exports=l},"./node_modules/core-js/modules/_fails-is-regexp.js": +/*!**********************************************************!*\ + !*** ./node_modules/core-js/modules/_fails-is-regexp.js ***! + \**********************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_wks */"./node_modules/core-js/modules/_wks.js")("match");e.exports=function(e){var o=/./;try{"/./"[e](o)}catch(t){try{return o[n]=!1,!"/./"[e](o)}catch(e){}}return!0}},"./node_modules/core-js/modules/_fails.js": +/*!************************************************!*\ + !*** ./node_modules/core-js/modules/_fails.js ***! + \************************************************/ +/*! no static exports found */function(e,o){e.exports=function(e){try{return!!e()}catch(e){return!0}}},"./node_modules/core-js/modules/_fix-re-wks.js": +/*!*****************************************************!*\ + !*** ./node_modules/core-js/modules/_fix-re-wks.js ***! + \*****************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_hide */"./node_modules/core-js/modules/_hide.js"),s=t(/*! ./_redefine */"./node_modules/core-js/modules/_redefine.js"),r=t(/*! ./_fails */"./node_modules/core-js/modules/_fails.js"),i=t(/*! ./_defined */"./node_modules/core-js/modules/_defined.js"),u=t(/*! ./_wks */"./node_modules/core-js/modules/_wks.js");e.exports=function(e,o,t){var l=u(e),c=t(i,l,""[e]),d=c[0],a=c[1];r(function(){var o={};return o[l]=function(){return 7},7!=""[e](o)})&&(s(String.prototype,e,d),n(RegExp.prototype,l,2==o?function(e,o){return a.call(e,this,o)}:function(e){return a.call(e,this)}))}},"./node_modules/core-js/modules/_flags.js": +/*!************************************************!*\ + !*** ./node_modules/core-js/modules/_flags.js ***! + \************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_an-object */"./node_modules/core-js/modules/_an-object.js");e.exports=function(){var e=n(this),o="";return e.global&&(o+="g"),e.ignoreCase&&(o+="i"),e.multiline&&(o+="m"),e.unicode&&(o+="u"),e.sticky&&(o+="y"),o}},"./node_modules/core-js/modules/_flatten-into-array.js": +/*!*************************************************************!*\ + !*** ./node_modules/core-js/modules/_flatten-into-array.js ***! + \*************************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_is-array */"./node_modules/core-js/modules/_is-array.js"),s=t(/*! ./_is-object */"./node_modules/core-js/modules/_is-object.js"),r=t(/*! ./_to-length */"./node_modules/core-js/modules/_to-length.js"),i=t(/*! ./_ctx */"./node_modules/core-js/modules/_ctx.js"),u=t(/*! ./_wks */"./node_modules/core-js/modules/_wks.js")("isConcatSpreadable");e.exports=function e(o,t,l,c,d,a,f,m){for(var p,j,_=d,h=0,v=!!f&&i(f,m,3);h0)_=e(o,t,p,r(p.length),_,a-1)-1;else{if(_>=9007199254740991)throw TypeError();o[_]=p}_++}h++}return _}},"./node_modules/core-js/modules/_for-of.js": +/*!*************************************************!*\ + !*** ./node_modules/core-js/modules/_for-of.js ***! + \*************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_ctx */"./node_modules/core-js/modules/_ctx.js"),s=t(/*! ./_iter-call */"./node_modules/core-js/modules/_iter-call.js"),r=t(/*! ./_is-array-iter */"./node_modules/core-js/modules/_is-array-iter.js"),i=t(/*! ./_an-object */"./node_modules/core-js/modules/_an-object.js"),u=t(/*! ./_to-length */"./node_modules/core-js/modules/_to-length.js"),l=t(/*! ./core.get-iterator-method */"./node_modules/core-js/modules/core.get-iterator-method.js"),c={},d={};(o=e.exports=function(e,o,t,a,f){var m,p,j,_,h=f?function(){return e}:l(e),v=n(t,a,o?2:1),b=0;if("function"!=typeof h)throw TypeError(e+" is not iterable!");if(r(h)){for(m=u(e.length);m>b;b++)if((_=o?v(i(p=e[b])[0],p[1]):v(e[b]))===c||_===d)return _}else for(j=h.call(e);!(p=j.next()).done;)if((_=s(j,v,p.value,o))===c||_===d)return _}).BREAK=c,o.RETURN=d},"./node_modules/core-js/modules/_global.js": +/*!*************************************************!*\ + !*** ./node_modules/core-js/modules/_global.js ***! + \*************************************************/ +/*! no static exports found */function(e,o){var t=e.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=t)},"./node_modules/core-js/modules/_has.js": +/*!**********************************************!*\ + !*** ./node_modules/core-js/modules/_has.js ***! + \**********************************************/ +/*! no static exports found */function(e,o){var t={}.hasOwnProperty;e.exports=function(e,o){return t.call(e,o)}},"./node_modules/core-js/modules/_hide.js": +/*!***********************************************!*\ + !*** ./node_modules/core-js/modules/_hide.js ***! + \***********************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_object-dp */"./node_modules/core-js/modules/_object-dp.js"),s=t(/*! ./_property-desc */"./node_modules/core-js/modules/_property-desc.js");e.exports=t(/*! ./_descriptors */"./node_modules/core-js/modules/_descriptors.js")?function(e,o,t){return n.f(e,o,s(1,t))}:function(e,o,t){return e[o]=t,e}},"./node_modules/core-js/modules/_html.js": +/*!***********************************************!*\ + !*** ./node_modules/core-js/modules/_html.js ***! + \***********************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_global */"./node_modules/core-js/modules/_global.js").document;e.exports=n&&n.documentElement},"./node_modules/core-js/modules/_ie8-dom-define.js": +/*!*********************************************************!*\ + !*** ./node_modules/core-js/modules/_ie8-dom-define.js ***! + \*********************************************************/ +/*! no static exports found */function(e,o,t){e.exports=!t(/*! ./_descriptors */"./node_modules/core-js/modules/_descriptors.js")&&!t(/*! ./_fails */"./node_modules/core-js/modules/_fails.js")(function(){return 7!=Object.defineProperty(t(/*! ./_dom-create */"./node_modules/core-js/modules/_dom-create.js")("div"),"a",{get:function(){return 7}}).a})},"./node_modules/core-js/modules/_inherit-if-required.js": +/*!**************************************************************!*\ + !*** ./node_modules/core-js/modules/_inherit-if-required.js ***! + \**************************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_is-object */"./node_modules/core-js/modules/_is-object.js"),s=t(/*! ./_set-proto */"./node_modules/core-js/modules/_set-proto.js").set;e.exports=function(e,o,t){var r,i=o.constructor;return i!==t&&"function"==typeof i&&(r=i.prototype)!==t.prototype&&n(r)&&s&&s(e,r),e}},"./node_modules/core-js/modules/_invoke.js": +/*!*************************************************!*\ + !*** ./node_modules/core-js/modules/_invoke.js ***! + \*************************************************/ +/*! no static exports found */function(e,o){e.exports=function(e,o,t){var n=void 0===t;switch(o.length){case 0:return n?e():e.call(t);case 1:return n?e(o[0]):e.call(t,o[0]);case 2:return n?e(o[0],o[1]):e.call(t,o[0],o[1]);case 3:return n?e(o[0],o[1],o[2]):e.call(t,o[0],o[1],o[2]);case 4:return n?e(o[0],o[1],o[2],o[3]):e.call(t,o[0],o[1],o[2],o[3])}return e.apply(t,o)}},"./node_modules/core-js/modules/_iobject.js": +/*!**************************************************!*\ + !*** ./node_modules/core-js/modules/_iobject.js ***! + \**************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_cof */"./node_modules/core-js/modules/_cof.js");e.exports=Object("z").propertyIsEnumerable(0)?Object:function(e){return"String"==n(e)?e.split(""):Object(e)}},"./node_modules/core-js/modules/_is-array-iter.js": +/*!********************************************************!*\ + !*** ./node_modules/core-js/modules/_is-array-iter.js ***! + \********************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_iterators */"./node_modules/core-js/modules/_iterators.js"),s=t(/*! ./_wks */"./node_modules/core-js/modules/_wks.js")("iterator"),r=Array.prototype;e.exports=function(e){return void 0!==e&&(n.Array===e||r[s]===e)}},"./node_modules/core-js/modules/_is-array.js": +/*!***************************************************!*\ + !*** ./node_modules/core-js/modules/_is-array.js ***! + \***************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_cof */"./node_modules/core-js/modules/_cof.js");e.exports=Array.isArray||function(e){return"Array"==n(e)}},"./node_modules/core-js/modules/_is-integer.js": +/*!*****************************************************!*\ + !*** ./node_modules/core-js/modules/_is-integer.js ***! + \*****************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_is-object */"./node_modules/core-js/modules/_is-object.js"),s=Math.floor;e.exports=function(e){return!n(e)&&isFinite(e)&&s(e)===e}},"./node_modules/core-js/modules/_is-object.js": +/*!****************************************************!*\ + !*** ./node_modules/core-js/modules/_is-object.js ***! + \****************************************************/ +/*! no static exports found */function(e,o){e.exports=function(e){return"object"==typeof e?null!==e:"function"==typeof e}},"./node_modules/core-js/modules/_is-regexp.js": +/*!****************************************************!*\ + !*** ./node_modules/core-js/modules/_is-regexp.js ***! + \****************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_is-object */"./node_modules/core-js/modules/_is-object.js"),s=t(/*! ./_cof */"./node_modules/core-js/modules/_cof.js"),r=t(/*! ./_wks */"./node_modules/core-js/modules/_wks.js")("match");e.exports=function(e){var o;return n(e)&&(void 0!==(o=e[r])?!!o:"RegExp"==s(e))}},"./node_modules/core-js/modules/_iter-call.js": +/*!****************************************************!*\ + !*** ./node_modules/core-js/modules/_iter-call.js ***! + \****************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_an-object */"./node_modules/core-js/modules/_an-object.js");e.exports=function(e,o,t,s){try{return s?o(n(t)[0],t[1]):o(t)}catch(o){var r=e.return;throw void 0!==r&&n(r.call(e)),o}}},"./node_modules/core-js/modules/_iter-create.js": +/*!******************************************************!*\ + !*** ./node_modules/core-js/modules/_iter-create.js ***! + \******************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_object-create */"./node_modules/core-js/modules/_object-create.js"),s=t(/*! ./_property-desc */"./node_modules/core-js/modules/_property-desc.js"),r=t(/*! ./_set-to-string-tag */"./node_modules/core-js/modules/_set-to-string-tag.js"),i={};t(/*! ./_hide */"./node_modules/core-js/modules/_hide.js")(i,t(/*! ./_wks */"./node_modules/core-js/modules/_wks.js")("iterator"),function(){return this}),e.exports=function(e,o,t){e.prototype=n(i,{next:s(1,t)}),r(e,o+" Iterator")}},"./node_modules/core-js/modules/_iter-define.js": +/*!******************************************************!*\ + !*** ./node_modules/core-js/modules/_iter-define.js ***! + \******************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_library */"./node_modules/core-js/modules/_library.js"),s=t(/*! ./_export */"./node_modules/core-js/modules/_export.js"),r=t(/*! ./_redefine */"./node_modules/core-js/modules/_redefine.js"),i=t(/*! ./_hide */"./node_modules/core-js/modules/_hide.js"),u=t(/*! ./_iterators */"./node_modules/core-js/modules/_iterators.js"),l=t(/*! ./_iter-create */"./node_modules/core-js/modules/_iter-create.js"),c=t(/*! ./_set-to-string-tag */"./node_modules/core-js/modules/_set-to-string-tag.js"),d=t(/*! ./_object-gpo */"./node_modules/core-js/modules/_object-gpo.js"),a=t(/*! ./_wks */"./node_modules/core-js/modules/_wks.js")("iterator"),f=!([].keys&&"next"in[].keys()),m=function(){return this};e.exports=function(e,o,t,p,j,_,h){l(t,o,p);var v,b,g,y=function(e){if(!f&&e in E)return E[e];switch(e){case"keys":case"values":return function(){return new t(this,e)}}return function(){return new t(this,e)}},k=o+" Iterator",w="values"==j,x=!1,E=e.prototype,S=E[a]||E["@@iterator"]||j&&E[j],T=S||y(j),O=j?w?y("entries"):T:void 0,P="Array"==o&&E.entries||S;if(P&&(g=d(P.call(new e)))!==Object.prototype&&g.next&&(c(g,k,!0),n||"function"==typeof g[a]||i(g,a,m)),w&&S&&"values"!==S.name&&(x=!0,T=function(){return S.call(this)}),n&&!h||!f&&!x&&E[a]||i(E,a,T),u[o]=T,u[k]=m,j)if(v={values:w?T:y("values"),keys:_?T:y("keys"),entries:O},h)for(b in v)b in E||r(E,b,v[b]);else s(s.P+s.F*(f||x),o,v);return v}},"./node_modules/core-js/modules/_iter-detect.js": +/*!******************************************************!*\ + !*** ./node_modules/core-js/modules/_iter-detect.js ***! + \******************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_wks */"./node_modules/core-js/modules/_wks.js")("iterator"),s=!1;try{var r=[7][n]();r.return=function(){s=!0},Array.from(r,function(){throw 2})}catch(e){}e.exports=function(e,o){if(!o&&!s)return!1;var t=!1;try{var r=[7],i=r[n]();i.next=function(){return{done:t=!0}},r[n]=function(){return i},e(r)}catch(e){}return t}},"./node_modules/core-js/modules/_iter-step.js": +/*!****************************************************!*\ + !*** ./node_modules/core-js/modules/_iter-step.js ***! + \****************************************************/ +/*! no static exports found */function(e,o){e.exports=function(e,o){return{value:o,done:!!e}}},"./node_modules/core-js/modules/_iterators.js": +/*!****************************************************!*\ + !*** ./node_modules/core-js/modules/_iterators.js ***! + \****************************************************/ +/*! no static exports found */function(e,o){e.exports={}},"./node_modules/core-js/modules/_library.js": +/*!**************************************************!*\ + !*** ./node_modules/core-js/modules/_library.js ***! + \**************************************************/ +/*! no static exports found */function(e,o){e.exports=!1},"./node_modules/core-js/modules/_math-expm1.js": +/*!*****************************************************!*\ + !*** ./node_modules/core-js/modules/_math-expm1.js ***! + \*****************************************************/ +/*! no static exports found */function(e,o){var t=Math.expm1;e.exports=!t||t(10)>22025.465794806718||t(10)<22025.465794806718||-2e-17!=t(-2e-17)?function(e){return 0==(e=+e)?e:e>-1e-6&&e<1e-6?e+e*e/2:Math.exp(e)-1}:t},"./node_modules/core-js/modules/_math-fround.js": +/*!******************************************************!*\ + !*** ./node_modules/core-js/modules/_math-fround.js ***! + \******************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_math-sign */"./node_modules/core-js/modules/_math-sign.js"),s=Math.pow,r=s(2,-52),i=s(2,-23),u=s(2,127)*(2-i),l=s(2,-126);e.exports=Math.fround||function(e){var o,t,s=Math.abs(e),c=n(e);return su||t!=t?c*(1/0):c*t}},"./node_modules/core-js/modules/_math-log1p.js": +/*!*****************************************************!*\ + !*** ./node_modules/core-js/modules/_math-log1p.js ***! + \*****************************************************/ +/*! no static exports found */function(e,o){e.exports=Math.log1p||function(e){return(e=+e)>-1e-8&&e<1e-8?e-e*e/2:Math.log(1+e)}},"./node_modules/core-js/modules/_math-scale.js": +/*!*****************************************************!*\ + !*** ./node_modules/core-js/modules/_math-scale.js ***! + \*****************************************************/ +/*! no static exports found */function(e,o){e.exports=Math.scale||function(e,o,t,n,s){return 0===arguments.length||e!=e||o!=o||t!=t||n!=n||s!=s?NaN:e===1/0||e===-1/0?e:(e-o)*(s-n)/(t-o)+n}},"./node_modules/core-js/modules/_math-sign.js": +/*!****************************************************!*\ + !*** ./node_modules/core-js/modules/_math-sign.js ***! + \****************************************************/ +/*! no static exports found */function(e,o){e.exports=Math.sign||function(e){return 0==(e=+e)||e!=e?e:e<0?-1:1}},"./node_modules/core-js/modules/_meta.js": +/*!***********************************************!*\ + !*** ./node_modules/core-js/modules/_meta.js ***! + \***********************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_uid */"./node_modules/core-js/modules/_uid.js")("meta"),s=t(/*! ./_is-object */"./node_modules/core-js/modules/_is-object.js"),r=t(/*! ./_has */"./node_modules/core-js/modules/_has.js"),i=t(/*! ./_object-dp */"./node_modules/core-js/modules/_object-dp.js").f,u=0,l=Object.isExtensible||function(){return!0},c=!t(/*! ./_fails */"./node_modules/core-js/modules/_fails.js")(function(){return l(Object.preventExtensions({}))}),d=function(e){i(e,n,{value:{i:"O"+ ++u,w:{}}})},a=e.exports={KEY:n,NEED:!1,fastKey:function(e,o){if(!s(e))return"symbol"==typeof e?e:("string"==typeof e?"S":"P")+e;if(!r(e,n)){if(!l(e))return"F";if(!o)return"E";d(e)}return e[n].i},getWeak:function(e,o){if(!r(e,n)){if(!l(e))return!0;if(!o)return!1;d(e)}return e[n].w},onFreeze:function(e){return c&&a.NEED&&l(e)&&!r(e,n)&&d(e),e}}},"./node_modules/core-js/modules/_metadata.js": +/*!***************************************************!*\ + !*** ./node_modules/core-js/modules/_metadata.js ***! + \***************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./es6.map */"./node_modules/core-js/modules/es6.map.js"),s=t(/*! ./_export */"./node_modules/core-js/modules/_export.js"),r=t(/*! ./_shared */"./node_modules/core-js/modules/_shared.js")("metadata"),i=r.store||(r.store=new(t(/*! ./es6.weak-map */"./node_modules/core-js/modules/es6.weak-map.js"))),u=function(e,o,t){var s=i.get(e);if(!s){if(!t)return;i.set(e,s=new n)}var r=s.get(o);if(!r){if(!t)return;s.set(o,r=new n)}return r};e.exports={store:i,map:u,has:function(e,o,t){var n=u(o,t,!1);return void 0!==n&&n.has(e)},get:function(e,o,t){var n=u(o,t,!1);return void 0===n?void 0:n.get(e)},set:function(e,o,t,n){u(t,n,!0).set(e,o)},keys:function(e,o){var t=u(e,o,!1),n=[];return t&&t.forEach(function(e,o){n.push(o)}),n},key:function(e){return void 0===e||"symbol"==typeof e?e:String(e)},exp:function(e){s(s.S,"Reflect",e)}}},"./node_modules/core-js/modules/_microtask.js": +/*!****************************************************!*\ + !*** ./node_modules/core-js/modules/_microtask.js ***! + \****************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_global */"./node_modules/core-js/modules/_global.js"),s=t(/*! ./_task */"./node_modules/core-js/modules/_task.js").set,r=n.MutationObserver||n.WebKitMutationObserver,i=n.process,u=n.Promise,l="process"==t(/*! ./_cof */"./node_modules/core-js/modules/_cof.js")(i);e.exports=function(){var e,o,t,c=function(){var n,s;for(l&&(n=i.domain)&&n.exit();e;){s=e.fn,e=e.next;try{s()}catch(n){throw e?t():o=void 0,n}}o=void 0,n&&n.enter()};if(l)t=function(){i.nextTick(c)};else if(!r||n.navigator&&n.navigator.standalone)if(u&&u.resolve){var d=u.resolve(void 0);t=function(){d.then(c)}}else t=function(){s.call(n,c)};else{var a=!0,f=document.createTextNode("");new r(c).observe(f,{characterData:!0}),t=function(){f.data=a=!a}}return function(n){var s={fn:n,next:void 0};o&&(o.next=s),e||(e=s,t()),o=s}}},"./node_modules/core-js/modules/_new-promise-capability.js": +/*!*****************************************************************!*\ + !*** ./node_modules/core-js/modules/_new-promise-capability.js ***! + \*****************************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_a-function */"./node_modules/core-js/modules/_a-function.js");e.exports.f=function(e){return new function(e){var o,t;this.promise=new e(function(e,n){if(void 0!==o||void 0!==t)throw TypeError("Bad Promise constructor");o=e,t=n}),this.resolve=n(o),this.reject=n(t)}(e)}},"./node_modules/core-js/modules/_object-assign.js": +/*!********************************************************!*\ + !*** ./node_modules/core-js/modules/_object-assign.js ***! + \********************************************************/ +/*! no static exports found */function(e,o,t){"use strict";var n=t(/*! ./_object-keys */"./node_modules/core-js/modules/_object-keys.js"),s=t(/*! ./_object-gops */"./node_modules/core-js/modules/_object-gops.js"),r=t(/*! ./_object-pie */"./node_modules/core-js/modules/_object-pie.js"),i=t(/*! ./_to-object */"./node_modules/core-js/modules/_to-object.js"),u=t(/*! ./_iobject */"./node_modules/core-js/modules/_iobject.js"),l=Object.assign;e.exports=!l||t(/*! ./_fails */"./node_modules/core-js/modules/_fails.js")(function(){var e={},o={},t=Symbol(),n="abcdefghijklmnopqrst";return e[t]=7,n.split("").forEach(function(e){o[e]=e}),7!=l({},e)[t]||Object.keys(l({},o)).join("")!=n})?function(e,o){for(var t=i(e),l=arguments.length,c=1,d=s.f,a=r.f;l>c;)for(var f,m=u(arguments[c++]),p=d?n(m).concat(d(m)):n(m),j=p.length,_=0;j>_;)a.call(m,f=p[_++])&&(t[f]=m[f]);return t}:l},"./node_modules/core-js/modules/_object-create.js": +/*!********************************************************!*\ + !*** ./node_modules/core-js/modules/_object-create.js ***! + \********************************************************/ +/*! no static exports found */function(e,o,t){var n=t(/*! ./_an-object */"./node_modules/core-js/modules/_an-object.js"),s=t(/*! ./_object-dps */"./node_modules/core-js/modules/_object-dps.js"),r=t(/*! ./_enum-bug-keys */"./node_modules/core-js/modules/_enum-bug-keys.js"),i=t(/*! ./_shared-key */"./node_modules/core-js/modules/_shared-key.js")("IE_PROTO"),u=function(){},l=function(){var e,o=t(/*! ./_dom-create */"./node_modules/core-js/modules/_dom-create.js")("iframe"),n=r.length;for(o.style.display="none",t(/*! ./_html */"./node_modules/core-js/modules/_html.js").appendChild(o),o.src="javascript:",(e=o.contentWindow.document).open(),e.write(" +``` + +### Save sources to project + +Copy [codex-editor.js](../build/codex-editor.js) file to your project and load it. + +```html + +``` + +## Load Tools + +Each Block at the CodeX Editor represented by [Tools](tools.md). There are simple external scripts with own logic. For example check out our [Paragraph](https://github.com/codex-editor/paragraph) Tool that represents simple text block. + +Each Tool should have an installation guide. You can install Paragraph Tool via the same ways as an Editor (Node.js, CDN, local file). + +Check [CodeX Editor's community](https://github.com/codex-editor) to see Tools examples. + +**Example:** use Paragragh from CDN + +```html + +``` + +## Create Editor instance + +Create an instance of CodeX Editor and pass [Configuration Object](../src/components/interfaces/editor-config.ts). +Minimal params is a `holderId`, `tools` list and `initialBlock` marker. + +```html +
+``` + +You can create a simple Editor only with a default Paragraph Tool by passing a string with element's Id (wrapper for Editor) as a configuration param or use default `codex-editor`. + +```javascript +var editor = new CodexEditor(); + +// equals + +var editor = new CodexEditor('codex-editor'); +```` + +Or pass a whole settings object. + +```javascript +var editor = new CodexEditor({ + /** + * Create a holder for the Editor and pass its ID + */ + holderId : 'codex-editor', + + /** + * Available Tools list. + * Pass Tool's class or Settings object for each Tool you want to use + */ + tools: { + paragraph: { + class: Paragraph, + inlineToolbar : true + }, + // ... + }, + + /** + * What Block will be inserted by default + */ + initialBlock : 'paragraph', + + /** + * Previously saved data that should be rendered + */ + data: {} +}); +``` + +## Ready callback + +CodeX Editor needs a bit time to initialize. It is an asynchronous action so it won't block execution of your main script. + +If you need to know when editor instance is ready you can use one of following ways: + +##### Pass `onReady` property to the configuration object. + +It must be a function: + +```javascript +var editor = new CodexEditor({ + // Other configuration properties + + /** + * onReady callback + */ + onReady: () => {console.log('CodeX Editor is ready to work!')} +}); +``` + +#### Use `isReady` promise. + +After you create new `CodexEditor` object it contains `isReady` property. +It is a Promise object resolved when editor is ready to work and rejected otherwise. +If there is an error during initialization `isReady` promise will be rejected with error message. + +```javascript +var editor = new CodexEditor(); + +editor.isReady + .then(() => { + /** Do anything you need after editor initialization */ + }) + .catch((reason) => { + console.log(`CodeX Editor initialization failed because of ${reason}`) + }); +``` + +You can use `async/await` to keep your code looking synchronous: + +```javascript +var editor = new CodexEditor(); + +try { + await editor.isReady; + /** Do anything you need after editor initialization */ +} catch (reason) { + console.log(`CodeX Editor initialization failed because of ${reason}`) +} +``` + + +## Saving Data + +Call `editor.saver.save()` and handle returned Promise with saved data. + +```javascript +editor.saver.save() + .then((savedData) => { + console.log(savedData); + }); +``` + +## Example + +Take a look at the [example.html](../example/example.html) to view more detailed examples. diff --git a/docs/sanitizer.md b/docs/sanitizer.md new file mode 100644 index 00000000..31081ac3 --- /dev/null +++ b/docs/sanitizer.md @@ -0,0 +1,45 @@ +# CodeX Editor Sanitizer Module + +The `Sanitizer` module represents a set of methods that clears taint strings. +Uses lightweight npm package with simple API [html-janitor](https://www.npmjs.com/package/html-janitor) + +Sanitizer class implements basic Module class that holds User configuration +and default CodeX Editor instances + +You can read more about Module class [here]() + +## Properties + +Default Editor Sanitizer configuration according to the html-janitor API +```javascript +defaultConfig +``` + +Custom User configuration which passed on Editor initialization. Data type must be according to the html-janitor API +```javascript +sanitizerConfig +``` + + +Property that holds an instance used in Module +```javascript +sanitizerInstance +``` + +## Methods + +### clean + +```javascript +clean(taintString, customConfig) +``` + +> Cleans up the passed taint string + +#### params + +| Param | Type | Description| +| -------------|------ |:-------------:| +| taintString | String | string that needs to be cleaned| +| customConfig | Object | Can be passed new config per usage (Default: uses default configuration)| + diff --git a/docs/toolbar-settings.md b/docs/toolbar-settings.md new file mode 100644 index 00000000..d3082b75 --- /dev/null +++ b/docs/toolbar-settings.md @@ -0,0 +1,91 @@ +# CodeX Editor Toolbar Block Settings Module + +Toolbar Module has space for Block settings. Settings divided into: + - space for plugin's settings, that is described by «Plugin»'s Developer + - space for default settings. This option is also can be implemented and expanded + +They difference between zones is that the first option is specified by plugin +and each Block can have different options, when second option is for every Block +regardless to the plugin's option. + +### Let's look the examples: + +«Plugin»'s Developers need to expand «renderSettings» method that returns HTML. +Every user action will be handled by itself. So, you can easily write +callbacks that switches your content or makes better. For more information +read [Tools](tools.md). + +--- + +«Tune»'s Developers need to implement core-provided interface to develop +tunes that will be appeared in Toolbar default settings zone. + +Tunes must expand two important methods: + - `render()` - returns HTML and it is appended to the default settings zone + - `save()` - extracts important information to be saved + +No restrictions. Handle user action by yourself + +Create Class that implements block-tune.ts + +Your Tune's constructor gets argument as object and it includes: + - {Object} api - object contains public methods from modules. @see [API](api.md) + - {Object} settings - settings contains block default state. +This object could have information about cover, anchor and so on. + +Example on TypeScript: + +```js + +import IBlockTune from './block-tune'; + +export default class YourCustomTune implements IBlockTune { + + public constructor({api, settings}) { + this.api = api; + this.settings = settings; + } + + render() { + let someHTML = '...'; + return someHTML; + } + + save() { + // Return the important data that needs to be saved + return object + } + + someMethod() { + // moves current block down + this.api.blocks.moveDown(); + } +} +``` + +Example on ES6 + +```js +export default class YourCustomTune { + + constructor({api, settings}) { + this.api = api; + this.settings = settings; + } + + render() { + let someHTML = '...'; + return someHTML; + } + + save() { + // Return the important data that needs to be saved + return object + } + + someMethod() { + // moves current block down + this.api.blocks.moveDown(); + } +} +``` diff --git a/docs/tools-inline.md b/docs/tools-inline.md new file mode 100644 index 00000000..ff34d02e --- /dev/null +++ b/docs/tools-inline.md @@ -0,0 +1,105 @@ +# Tools for the Inline Toolbar + +Similar with [Tools](tools.md) represented Blocks, you can create Tools for the Inline Toolbar. It will work with +selected fragment of text. The simplest example is `bold` or `italic` Tools. + +## Base structure + +First of all, Tool's class should have a `isInline` property (static getter) set as `true`. + +After that Inline Tool should implement next methods. + +- `render()` — create a button +- `surround()` — works with selected range +- `checkState()` — get Tool's activated state by selected range + +Also, you can provide optional methods + +- `renderActions()` — create additional element below the buttons +- `clear()` — clear Tool's stuff on opening/closing of Inline Toolbar +- `shortcut()` — shortcut that handles Tool + +At the constructor of Tool's class exemplar you will accept an object with the [API](api.md) as a parameter. + +--- + +### render() + +Method that returns button to append at the Inline Toolbar + +#### Parameters + +Method does not accept any parameters + +#### Return value + +type | description | +-- | -- | +`HTMLElement` | element that will be added to the Inline Toolbar | + +--- + +### surround(range: Range) + +Method that accepts selected range and wrap it somehow + +#### Parameters + +name | type | description | +-- |-- | -- | +range | Range | first range of current Selection | + +#### Return value + +There is no return value + +--- + +### checkState(selection: Selection) + +Get Selection and detect if Tool was applied. For example, after that Tool can highlight button or show some details. + +#### Parameters + +name | type | description | +-- |-- | -- | +selection | Selection | current Selection | + +#### Return value + +type | description | +-- | -- | +`Boolean` | `true` if Tool is active, otherwise `false` | + +--- + +### renderActions() + +Optional method that returns additional Element with actions. +For example, input for the 'link' tool or textarea for the 'comment' tool. +It will be places below the buttons list at Inline Toolbar. + +#### Parameters + +Method does not accept any parameters + +#### Return value + +type | description | +-- | -- | +`HTMLElement` | element that will be added to the Inline Toolbar | + +--- + +### clear() + +Optional method that will be called on opening/closing of Inline Toolbar. +Can contain logic for clearing Tool's stuff, such as inputs, states and other. + +#### Parameters + +Method does not accept any parameters + +#### Return value + +Method should not return a value. diff --git a/docs/tools.md b/docs/tools.md new file mode 100644 index 00000000..00f9c313 --- /dev/null +++ b/docs/tools.md @@ -0,0 +1,164 @@ +# CodeX Editor Tools + +CodeX Editor is a block-oriented editor. It means that entry composed with the list of `Blocks` of different types: `Texts`, `Headers`, `Images`, `Quotes` etc. + +`Tool` — is a class that provide custom `Block` type. All Tools represented by `Plugins`. + +## Tool class structure + +### constructor() + +Each Tool's instance called with an params object. + +| Param | Type | Description | +| ------ | ------------------- | ----------------------------------------------- | +| api | [`IAPI`][iapi-link] | CodeX Editor's API methods | +| config | `object` | Special configuration params passed in «config» | +| data | `object` | Data to be rendered in this Tool | + +[iapi-link]: ../src/components/interfaces/api.ts + +#### Example + +```javascript +constructor({data, config, api}) { + this.data = data; + this.api = api; + this.config = config; + // ... +} +``` + +### render() + +### save() + +### validate() _optional_ + +### merge() _optional_ + +Method that specifies how to merge two `Blocks` of the same type, for example on `Backspace` keypress. +Method does accept data object in same format as the `Render` and it should provide logic how to combine new +data with the currently stored value. + +### Internal Tool Settings + +Options that Tool can specify. All settings should be passed as static properties of Tool's class. + +| Name | Type | Default Value | Description | +| -- | -- | -- | -- | +| `displayInToolbox` | _Boolean_ | `false` | Pass `true` to display this `Tool` in the Editor's `Toolbox` | +| `toolboxIcon` | _String_ | — | Icon for Toolbox HTML string | +| `enableLineBreaks` | _Boolean_ | `false` | With this option, CodeX Editor won't handle Enter keydowns. Can be helpful for Tools like `` where line breaks should be handled by default behaviour. | +| `irreplaceable` | _Boolean_ | `false` | By default, **empty** `Blocks` can be **replaced** by other `Blocks` with the `Toolbox`. Some tools with media-content may prefer another behaviour. Pass `true` and `Toolbox` will add a new block below yours. | +| `contentless` | _Boolean_ | `false` | Pass `true` for Tool which represents decorative empty `Blocks` | +| `isInline` | _Boolean_ | `false` | Describes Tool as a [Tool for the Inline Toolbar](tools-inline.md) | + +### User configuration + +All Tools can be configured by users. You can set up some of available settings along with Tool's class +to the `tools` property of Editor Config. + +```javascript +var editor = new CodexEditor({ + holderId : 'codex-editor', + tools: { + text: { + class: Text, + inlineToolbar : true, + // other settings.. + }, + header: Header + }, + initialBlock : 'text', +}); +``` + +There are few options available by CodeX Editor. + +| Name | Type | Default Value | Description | +| -- | -- | -- | -- | +| `inlineToolbar` | _Boolean/Array_ | `false` | Pass `true` to enable the Inline Toolbar with all Tools, or pass an array with specified Tools list | +| `config` | _Object_ | `null` | User's configuration for Plugin. + +### Paste handling + +CodeX Editor handles paste on Blocks and provides API for Tools to process the pasted data. + +When user pastes content into Editor, pasted content is splitted into blocks. + +1. If plain text has been pasted, it is split by new line characters +2. If HTML string has been pasted, it is split by block tags + +Also Editor API allows you to define RegExp patterns to substitute them by your data. + +To provide paste handling for your Tool you need to define static getter `onPaste` in Tool class. +`onPaste` getter should return object with fields described below. + +##### HTML tags handling + +To handle pasted HTML elements object returned from `onPaste` getter should contain following fields: + +| Name | Type | Description | +| -- | -- | -- | +| `handler(content: HTMLElement)` | `Function` | _Optional_. Pasted HTML elements handler. Gets one argument `content`. `content` is HTML element extracted from pasted data. Handler should return the same object as Tool's `save` method | +| `tags` | `String[]` | _Optional_. Should contain all tag names you want to be extracted from pasted data and be passed to your `handler` method | + + +For correct work you MUST provide `onPaste.handler` at least for `initialBlock` Tool. + +> Example + +Header tool can handle `H1`-`H6` tags using paste handling API + +```javascript +static get onPaste() { + return { + tags: ['H1', 'H2', 'H3', 'H4', 'H5', 'H6'], + handler: (element) => ({ + type: element.tagName, + text: element.innerHTML + }) + } +} +``` + +> One tag can be handled by one Tool only. + +##### Patterns handling + +Your Tool can analyze text by RegExp patterns to substitute pasted string with data you want. Object returned from `onPaste` getter should contain following fields to use patterns: + +| Name | Type | Description | +| -- | -- | -- | +| `patterns` | `Object` | _Optional_. `patterns` object contains RegExp patterns with their names as object's keys | +| `patternHandler(text: string, key: string)` | `Function` | _Optional_. Gets pasted string and pattern name. Should return the same object as Tool `save` method | + +**Note** Editor will check pattern's full match, so don't forget to handle all available chars in there. + +Pattern will be processed only if paste was on `initialBlock` Tool and pasted string length is less than 450 characters. + +> Example + +You can handle youtube links and insert embeded video instead: + +```javascript +static get onPaste() { + return { + patterns: { + youtube: /http(?:s?):\/\/(?:www\.)?youtu(?:be\.com\/watch\?v=|\.be\/)([\w\-\_]*)(&(amp;)?[\w\?‌​=]*)?/ + }, + patternHandler: (text, key) => { + const urlData = Youtube.onPaste.patterns[key].exec(text); + + return { + iframe: Youtube.makeEmbededFromURL(urlData) + }; + } + } +} +``` + +> Both `onPaste.handler` and `onPaste.patternHandler` can be `async` or return a `Promise`. + +### Sanitize diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 00000000..d45a3286 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,58 @@ +# So how to use CodeX Editor + +## Basics + +CodeX Editor is a Block-Styled editor. Blocks is a structural units, of which the Entry is composed. +For example, `Paragraph`, `Heading`, `Image`, `Video`, `List` are Blocks. Each Block is represented by a Plugin. +We have [many](http://github.com/codex-editor) ready-to-use Plugins and the [simple API](tools.md) for creation new ones. + +So how to use the Editor after [Installation](installation.md). + +- Create new Blocks by Enter or with the Plus Button +- Press `TAB` or click on the Plus Button to view the Toolbox +- Press `TAB` again to leaf Toolbox and select a Block you need. Then press Enter. + + + ![](https://github.com/codex-editor/list/raw/master/assets/example.gif) + +- Select text fragment and apply a style or insert a link from the Inline Toolbar + +![](https://capella.pics/7ccbcfcd-1c49-4674-bea7-71021468a1bd.jpg) + +- Use «three-dots» button on the right to open Block Settings. From here, you can move and delete a Block +or apply Tool's settings, if it provided. For example, set a Heading level or List style. + +![](https://capella.pics/01a55381-46cd-47c7-b92e-34765434f2ca.jpg) + +## Shortcuts + +We really appreciate shortcuts. So there are few presets. + +Action | Shortcut | Restrictions +-- | -- | -- +`TAB` | Show/leaf a Toolbox. | On empty block +`SHIFT+TAB` | Leaf back a Toolbox. | While Toolbox is opened +`ENTER` | Create a Block | While Toolbox is opened and some Tool is selected +`CMD+B` | Bold style | On selection +`CMD+I` | Italic style | On selection +`CMD+K` | Insert a link | On selection + +Also we support shortcuts on the all type of Tools. Specify a shortcut with the Tools configuration. For example: + +```js +var editor = CodexEditor({ + //... + tools: { + header: { + class: Header, + shortcut: 'CMD+SHIFT+H' + }, + list: { + class: List, + shortcut: 'CMD+SHIFT+L' + } + } + //... + }); + +``` diff --git a/example.html b/example.html deleted file mode 100644 index 5f4732b3..00000000 --- a/example.html +++ /dev/null @@ -1,244 +0,0 @@ - - - - - CodeX Editor example - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/example/assets/demo.css b/example/assets/demo.css new file mode 100644 index 00000000..47c4f2fd --- /dev/null +++ b/example/assets/demo.css @@ -0,0 +1,118 @@ +/** + * Styles for the example page + */ +body { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif; + font-size: 14px; + line-height: 1.5em; + margin: 0; +} + +.ce-example { + font-size: 15px; +} + +.ce-example__header { + border-bottom: 1px solid #E8E8EB; + height: 50px; + line-height: 50px; + display: flex; + padding: 0 30px; + margin-bottom: 30px; +} + +.ce-example__header a { + color: inherit; + text-decoration: none; +} + +.ce-example__header-logo { + font-weight: bold; +} + +.ce-example__header-menu { + margin-left: auto; +} + +.ce-example__header-menu a { + margin-left: 20px; +} + +.ce-example__content { + max-width: 1100px; + margin: 0 auto; +} + +.ce-example__output { + background: #1B202B; + overflow-x: auto; + padding: 0 30px; +} + +.ce-example__output-content { + max-width: 650px; + margin: 30px auto; + color: #ABADC3; + font-family: 'PT Mono', Menlo, Monaco, Consolas, Courier New, monospace; + font-size: 13.3px; +} + +.ce-example__output-content:empty { + display: none; +} + +.ce-example__button { + display: block; + margin: 50px auto; + max-width: 180px; + background: #4A9DF8; + padding: 17px 30px; + box-shadow: 0 6px 4px -4px rgba(137, 207, 255, 0.77); + cursor: pointer; + border-radius: 31px; + color: #fff; + font-family: 'PT Mono', Menlo, Monaco, Consolas, Courier New, monospace; + text-align: center; +} + +.ce-example__button:hover { + background: #3D8DE5; +} + +.ce-example__output-footer { + padding: 30px 0; + font-size: 14.2px; + letter-spacing: 0.3px; + text-align: center; +} + +.ce-example__output-footer a { + color: #fff; + text-decoration: none; +} + +@media all and (max-width: 680px){ + .ce-example__header, + .ce-example__content{ + padding: 0 20px; + } +} + +/** + * JSON highlighter + */ +.sc_attr { + color: rgb(148, 162, 192); +} +.sc_key { + color: rgb(190, 213, 255); +} +.sc_toolname { + color: rgb(15, 205, 251); +} +.sc_tag { + color: rgb(4, 131, 216); +} +.sc_bool { + color: rgb(247, 60, 173); +} diff --git a/example/assets/json-preview.js b/example/assets/json-preview.js new file mode 100644 index 00000000..daebdd74 --- /dev/null +++ b/example/assets/json-preview.js @@ -0,0 +1,45 @@ +/** + * Module to compose output JSON preview + */ +const cPreview = (function (module) { + /** + * Shows JSON in pretty preview + * @param {object} output - what to show + * @param {Element} holder - where to show + */ + module.show = function(output, holder) { + /** Make JSON pretty */ + output = JSON.stringify( output, null, 4 ); + /** Encode HTML entities */ + output = encodeHTMLEntities( output ); + /** Stylize! */ + output = stylize( output ); + holder.innerHTML = output; + }; + + /** + * Converts '>', '<', '&' symbols to entities + */ + function encodeHTMLEntities(string) { + return string.replace(/&/g, '&').replace(//g, '>'); + } + + /** + * Some styling magic + */ + function stylize(string) { + /** Stylize JSON keys */ + string = string.replace( /"(\w+)"\s?:/g, '"$1" :'); + /** Stylize tool names */ + string = string.replace( /"(text|quote|list|header|link|code|image|delimiter)"/g, '"$1"'); + /** Stylize HTML tags */ + string = string.replace( /(<[\/a-z]+(>)?)/gi, '$1' ); + /** Stylize strings */ + string = string.replace( /"([^"]+)"/gi, '"$1"' ); + /** Boolean/Null */ + string = string.replace( /\b(true|false|null)\b/gi, '$1' ); + return string; + } + + return module; +})({}); diff --git a/example/example.html b/example/example.html new file mode 100644 index 00000000..4883d07b --- /dev/null +++ b/example/example.html @@ -0,0 +1,231 @@ + + + + + CodeX Editor 🤩🧦🤨 example + + + + + +
+ +
+
+ +
+ editor.saver.save() +
+
+
+

+
+      
+    
+
+ + + + + + + + + + + + + + + + + + diff --git a/example/tools/delimiter b/example/tools/delimiter new file mode 160000 index 00000000..489b8749 --- /dev/null +++ b/example/tools/delimiter @@ -0,0 +1 @@ +Subproject commit 489b87499ad9c60100e3dd0d832c78e3127536a5 diff --git a/example/tools/header b/example/tools/header new file mode 160000 index 00000000..92e5373c --- /dev/null +++ b/example/tools/header @@ -0,0 +1 @@ +Subproject commit 92e5373cf8826492a96b6ca5070552ddf6aa749d diff --git a/example/tools/inline-code b/example/tools/inline-code new file mode 160000 index 00000000..286b1e1a --- /dev/null +++ b/example/tools/inline-code @@ -0,0 +1 @@ +Subproject commit 286b1e1a0d37e17175ebc28b00f8d4e1c68497a9 diff --git a/example/tools/list b/example/tools/list new file mode 160000 index 00000000..7c5fb17b --- /dev/null +++ b/example/tools/list @@ -0,0 +1 @@ +Subproject commit 7c5fb17b11056171d121b7ce040b86b091b483bc diff --git a/example/tools/paragraph b/example/tools/paragraph new file mode 160000 index 00000000..67af2369 --- /dev/null +++ b/example/tools/paragraph @@ -0,0 +1 @@ +Subproject commit 67af23696c591b6dc922b3c6c1b43070293281ea diff --git a/example/tools/quote b/example/tools/quote new file mode 160000 index 00000000..293149ca --- /dev/null +++ b/example/tools/quote @@ -0,0 +1 @@ +Subproject commit 293149cac0f352fc4debdd62558ca14e0448889a diff --git a/example/tools/simple-image b/example/tools/simple-image new file mode 160000 index 00000000..b3ba0e5d --- /dev/null +++ b/example/tools/simple-image @@ -0,0 +1 @@ +Subproject commit b3ba0e5de15bed3d4354b4bf23f63158bd4167dd diff --git a/fonts/codex_editor/codex-editor.eot b/fonts/codex_editor/codex-editor.eot deleted file mode 100755 index 723ad6ab..00000000 Binary files a/fonts/codex_editor/codex-editor.eot and /dev/null differ diff --git a/fonts/codex_editor/codex-editor.svg b/fonts/codex_editor/codex-editor.svg deleted file mode 100755 index 1ae6cabc..00000000 --- a/fonts/codex_editor/codex-editor.svg +++ /dev/null @@ -1,72 +0,0 @@ - - - -Copyright (C) 2016 by original authors @ fontello.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/fonts/codex_editor/codex-editor.ttf b/fonts/codex_editor/codex-editor.ttf deleted file mode 100755 index e6c70265..00000000 Binary files a/fonts/codex_editor/codex-editor.ttf and /dev/null differ diff --git a/fonts/codex_editor/codex-editor.woff b/fonts/codex_editor/codex-editor.woff deleted file mode 100755 index 5ad5eaa5..00000000 Binary files a/fonts/codex_editor/codex-editor.woff and /dev/null differ diff --git a/fonts/codex_editor/codex-editor.woff2 b/fonts/codex_editor/codex-editor.woff2 deleted file mode 100755 index 15066535..00000000 Binary files a/fonts/codex_editor/codex-editor.woff2 and /dev/null differ diff --git a/fonts/codex_editor/icon-hash.svg b/fonts/codex_editor/icon-hash.svg deleted file mode 100644 index 60790072..00000000 --- a/fonts/codex_editor/icon-hash.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/fonts/codex_editor/icon-plus.svg b/fonts/codex_editor/icon-plus.svg deleted file mode 100644 index fdc0776c..00000000 --- a/fonts/codex_editor/icon-plus.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - Combined Shape - Created with Sketch. - - - - - - - - - - - \ No newline at end of file diff --git a/icons.css b/icons.css deleted file mode 100644 index b8914486..00000000 --- a/icons.css +++ /dev/null @@ -1,63 +0,0 @@ -@font-face { - font-family: 'codex_editor'; - src: url('fonts/codex_editor/codex-editor.eot?20895205'); - src: url('fonts/codex_editor/codex-editor.eot?20895205#iefix') format('embedded-opentype'), - url('fonts/codex_editor/codex-editor.woff?20895205') format('woff'), - url('fonts/codex_editor/codex-editor.ttf?20895205') format('truetype'), - url('fonts/codex_editor/codex-editor.svg?20895205#codex_editor') format('svg'); - font-weight: normal; - font-style: normal; -} -[class^="ce-icon-"]:before, -[class*="ce-icon-"]:before { - font-family: "codex_editor"; - font-style: normal; - font-weight: normal; - speak: none; - - display: inline-block; - text-decoration: inherit; - width: 1em; - margin-right: .2em; - text-align: center; - font-variant: normal; - text-transform: none; - - line-height: 1em; - - /* Animation center compensation - margins should be symmetric */ - margin-left: .2em; - - -moz-osx-font-smoothing: grayscale; -} -.ce-icon-instagram:before { content: '\e800'; } /* '' */ -.ce-icon-picture:before { content: '\e801'; } /* '' */ -.ce-icon-cog:before { content: '\e802'; } /* '' */ -.ce-icon-link:before { content: '\e803'; } /* '' */ -.ce-icon-unlink:before { content: '\e804'; } /* '' */ -.ce-icon-code:before { content: '\e805'; } /* '' */ -.ce-icon-quote:before { content: '\e806'; } /* '' */ -.ce-icon-trash:before { content: '\e807'; } /* '' */ -.ce-icon-down-big:before { content: '\e808'; } /* '' */ -.ce-icon-up-big:before { content: '\e809'; } /* '' */ -.ce-icon-header:before { content: '\e80a'; } /* '' */ -.ce-icon-paragraph:before { content: '\e80b'; } /* '' */ -.ce-icon-align-left:before { content: '\e80c'; } /* '' */ -.ce-icon-align-center:before { content: '\e80d'; } /* '' */ -.ce-icon-align-right:before { content: '\e80e'; } /* '' */ -.ce-icon-font:before { content: '\e80f'; } /* '' */ -.ce-icon-bold:before { content: '\e810'; } /* '' */ -.ce-icon-medium:before { content: '\e811'; } /* '' */ -.ce-icon-italic:before { content: '\e812'; } /* '' */ -.ce-icon-list-bullet:before { content: '\e813'; } /* '' */ -.ce-icon-list-numbered:before { content: '\e814'; } /* '' */ -.ce-icon-strike:before { content: '\e815'; } /* '' */ -.ce-icon-underline:before { content: '\e816'; } /* '' */ -.ce-icon-table:before { content: '\e817'; } /* '' */ -.ce-icon-ellipsis-vert:before { content: '\e818'; } /* '' */ -.ce-icon-columns:before { content: '\e819'; } /* '' */ -.ce-icon-smile:before { content: '\e81a'; } /* '' */ -.ce-icon-newspaper:before { content: '\e81b'; } /* '' */ -.ce-icon-twitter:before { content: '\e81c'; } /* '' */ -.ce-icon-facebook-squared:before { content: '\e81d'; } /* '' */ -.ce-icon-vkontakte:before { content: '\e81e'; } /* '' */ diff --git a/modules/anchors.js b/modules/anchors.js deleted file mode 100644 index d72deaa4..00000000 --- a/modules/anchors.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Codex Editor Anchors module - * - * @author Codex Team - * @version 1.0 - */ - -module.exports = function (anchors) { - - let editor = codex.editor; - - anchors.input = null; - anchors.currentNode = null; - - anchors.settingsOpened = function (currentBlock) { - - anchors.currentNode = currentBlock; - anchors.input.value = anchors.currentNode.dataset.anchor || ''; - - }; - - anchors.anchorChanged = function (e) { - - var newAnchor = e.target.value = anchors.rusToTranslit(e.target.value); - - anchors.currentNode.dataset.anchor = newAnchor; - - if (newAnchor.trim() !== '') { - - anchors.currentNode.classList.add(editor.ui.className.BLOCK_WITH_ANCHOR); - - } else { - - anchors.currentNode.classList.remove(editor.ui.className.BLOCK_WITH_ANCHOR); - - } - - }; - - anchors.keyDownOnAnchorInput = function (e) { - - if (e.keyCode == editor.core.keys.ENTER) { - - e.preventDefault(); - e.stopPropagation(); - - e.target.blur(); - editor.toolbar.settings.close(); - - } - - }; - - anchors.keyUpOnAnchorInput = function (e) { - - if (e.keyCode >= editor.core.keys.LEFT && e.keyCode <= editor.core.keys.DOWN) { - - e.stopPropagation(); - - } - - }; - - anchors.rusToTranslit = function (string) { - - var ru = [ - 'А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', 'З', 'И', 'Й', - 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', - 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ь', 'Ы', 'Ь', 'Э', 'Ю', 'Я' - ], - en = [ - 'A', 'B', 'V', 'G', 'D', 'E', 'E', 'Zh', 'Z', 'I', 'Y', - 'K', 'L', 'M', 'N', 'O', 'P', 'R', 'S', 'T', 'U', 'F', - 'H', 'C', 'Ch', 'Sh', 'Sch', '', 'Y', '', 'E', 'Yu', 'Ya' - ]; - - for (var i = 0; i < ru.length; i++) { - - string = string.split(ru[i]).join(en[i]); - string = string.split(ru[i].toLowerCase()).join(en[i].toLowerCase()); - - } - - string = string.replace(/[^0-9a-zA-Z_]+/g, '-'); - - return string; - - }; - - return anchors; - -}({}); \ No newline at end of file diff --git a/modules/callbacks.js b/modules/callbacks.js deleted file mode 100644 index 443759ba..00000000 --- a/modules/callbacks.js +++ /dev/null @@ -1,911 +0,0 @@ -/** - * @module Codex Editor Callbacks module - * @description Module works with editor added Elements - * - * @author Codex Team - * @version 1.4.0 - */ - -module.exports = (function (callbacks) { - - let editor = codex.editor; - - /** - * used by UI module - * @description Routes all keydowns on document - * @param {Object} event - */ - callbacks.globalKeydown = function (event) { - - switch (event.keyCode) { - case editor.core.keys.ENTER : enterKeyPressed_(event); break; - } - - }; - - /** - * used by UI module - * @description Routes all keydowns on redactors area - * @param {Object} event - */ - callbacks.redactorKeyDown = function (event) { - - switch (event.keyCode) { - case editor.core.keys.TAB : tabKeyPressedOnRedactorsZone_(event); break; - case editor.core.keys.ENTER : enterKeyPressedOnRedactorsZone_(event); break; - case editor.core.keys.ESC : escapeKeyPressedOnRedactorsZone_(event); break; - default : defaultKeyPressedOnRedactorsZone_(event); break; - } - - }; - - /** - * used by UI module - * @description Routes all keyup events - * @param {Object} event - */ - callbacks.globalKeyup = function (event) { - - switch (event.keyCode) { - case editor.core.keys.UP : - case editor.core.keys.LEFT : - case editor.core.keys.RIGHT : - case editor.core.keys.DOWN : arrowKeyPressed_(event); break; - } - - }; - - /** - * @param {Object} event - * @private - * - * Handles behaviour when tab pressed - * @description if Content is empty show toolbox (if it is closed) or leaf tools - * uses Toolbars toolbox module to handle the situation - */ - var tabKeyPressedOnRedactorsZone_ = function (event) { - - /** - * Wait for solution. Would like to know the behaviour - * @todo Add spaces - */ - event.preventDefault(); - - - if (!editor.core.isBlockEmpty(editor.content.currentNode)) { - - return; - - } - - if ( !editor.toolbar.opened ) { - - editor.toolbar.open(); - - } - - if (editor.toolbar.opened && !editor.toolbar.toolbox.opened) { - - editor.toolbar.toolbox.open(); - - } else { - - editor.toolbar.toolbox.leaf(); - - } - - }; - - /** - * Handles global EnterKey Press - * @see enterPressedOnBlock_ - * @param {Object} event - */ - var enterKeyPressed_ = function () { - - if (editor.content.editorAreaHightlighted) { - - /** - * it means that we lose input index, saved index before is not correct - * therefore we need to set caret when we insert new block - */ - editor.caret.inputIndex = -1; - - enterPressedOnBlock_(); - - } - - }; - - /** - * Callback for enter key pressing in first-level block area - * - * @param {Event} event - * @private - * - * @description Inserts new block with initial type from settings - */ - var enterPressedOnBlock_ = function () { - - var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin; - - editor.content.insertBlock({ - type : NEW_BLOCK_TYPE, - block : editor.tools[NEW_BLOCK_TYPE].render() - }, true ); - - editor.toolbar.move(); - editor.toolbar.open(); - - }; - - - /** - * ENTER key handler - * - * @param {Object} event - * @private - * - * @description Makes new block with initial type from settings - */ - var enterKeyPressedOnRedactorsZone_ = function (event) { - - if (event.target.contentEditable == 'true') { - - /** Update input index */ - editor.caret.saveCurrentInputIndex(); - - } - - var currentInputIndex = editor.caret.getCurrentInputIndex() || 0, - workingNode = editor.content.currentNode, - tool = workingNode.dataset.tool, - isEnterPressedOnToolbar = editor.toolbar.opened && - editor.toolbar.current && - event.target == editor.state.inputs[currentInputIndex]; - - /** The list of tools which needs the default browser behaviour */ - var enableLineBreaks = editor.tools[tool].enableLineBreaks; - - /** This type of block creates when enter is pressed */ - var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin; - - /** - * When toolbar is opened, select tool instead of making new paragraph - */ - if ( isEnterPressedOnToolbar ) { - - event.preventDefault(); - - editor.toolbar.toolbox.toolClicked(event); - - editor.toolbar.close(); - - /** - * Stop other listeners callback executions - */ - event.stopPropagation(); - event.stopImmediatePropagation(); - - return; - - } - - /** - * Allow paragraph lineBreaks with shift enter - * Or if shiftkey pressed and enter and enabledLineBreaks, the let new block creation - */ - if ( event.shiftKey || enableLineBreaks ) { - - event.stopPropagation(); - event.stopImmediatePropagation(); - return; - - } - - var currentSelection = window.getSelection(), - currentSelectedNode = currentSelection.anchorNode, - caretAtTheEndOfText = editor.caret.position.atTheEnd(), - isTextNodeHasParentBetweenContenteditable = false; - - /** - * Allow making new

in same block by SHIFT+ENTER and forbids to prevent default browser behaviour - */ - if ( event.shiftKey && !enableLineBreaks ) { - - editor.callback.enterPressedOnBlock(editor.content.currentBlock, event); - event.preventDefault(); - return; - - } - - /** - * Workaround situation when caret at the Text node that has some wrapper Elements - * Split block cant handle this. - * We need to save default behavior - */ - isTextNodeHasParentBetweenContenteditable = currentSelectedNode && currentSelectedNode.parentNode.contentEditable != 'true'; - - /** - * Split blocks when input has several nodes and caret placed in textNode - */ - if ( - currentSelectedNode.nodeType == editor.core.nodeTypes.TEXT && - !isTextNodeHasParentBetweenContenteditable && - !caretAtTheEndOfText - ) { - - event.preventDefault(); - - editor.core.log('Splitting Text node...'); - - editor.content.splitBlock(currentInputIndex); - - /** Show plus button when next input after split is empty*/ - if (!editor.state.inputs[currentInputIndex + 1].textContent.trim()) { - - editor.toolbar.showPlusButton(); - - } - - } else { - - var islastNode = editor.content.isLastNode(currentSelectedNode); - - if ( islastNode && caretAtTheEndOfText ) { - - event.preventDefault(); - event.stopPropagation(); - event.stopImmediatePropagation(); - - editor.core.log('ENTER clicked in last textNode. Create new BLOCK'); - - editor.content.insertBlock({ - type: NEW_BLOCK_TYPE, - block: editor.tools[NEW_BLOCK_TYPE].render() - }, true); - - editor.toolbar.move(); - editor.toolbar.open(); - - /** Show plus button with empty block */ - editor.toolbar.showPlusButton(); - - } - - } - - /** get all inputs after new appending block */ - editor.ui.saveInputs(); - - }; - - /** - * Escape behaviour - * @param event - * @private - * - * @description Closes toolbox and toolbar. Prevents default behaviour - */ - var escapeKeyPressedOnRedactorsZone_ = function (event) { - - /** Close all toolbar */ - editor.toolbar.close(); - - /** Close toolbox */ - editor.toolbar.toolbox.close(); - - event.preventDefault(); - - }; - - /** - * @param {Event} event - * @private - * - * closes and moves toolbar - */ - var arrowKeyPressed_ = function (event) { - - editor.content.workingNodeChanged(); - - /* Closing toolbar */ - editor.toolbar.close(); - editor.toolbar.move(); - - }; - - /** - * @private - * @param {Event} event - * - * @description Closes all opened bars from toolbar. - * If block is mark, clears highlightning - */ - var defaultKeyPressedOnRedactorsZone_ = function () { - - editor.toolbar.close(); - - if (!editor.toolbar.inline.actionsOpened) { - - editor.toolbar.inline.close(); - editor.content.clearMark(); - - } - - }; - - /** - * Handler when clicked on redactors area - * - * @protected - * @param event - * - * @description Detects clicked area. If it is first-level block area, marks as detected and - * on next enter press will be inserted new block - * Otherwise, save carets position (input index) and put caret to the editable zone. - * - * @see detectWhenClickedOnFirstLevelBlockArea_ - * - */ - callbacks.redactorClicked = function (event) { - - detectWhenClickedOnFirstLevelBlockArea_(); - - editor.content.workingNodeChanged(event.target); - editor.ui.saveInputs(); - - var selectedText = editor.toolbar.inline.getSelectionText(), - firstLevelBlock; - - /** If selection range took off, then we hide inline toolbar */ - if (selectedText.length === 0) { - - editor.toolbar.inline.close(); - - } - - /** Update current input index in memory when caret focused into existed input */ - if (event.target.contentEditable == 'true') { - - editor.caret.saveCurrentInputIndex(); - - } - - if (editor.content.currentNode === null) { - - /** - * If inputs in redactor does not exits, then we put input index 0 not -1 - */ - var indexOfLastInput = editor.state.inputs.length > 0 ? editor.state.inputs.length - 1 : 0; - - /** If we have any inputs */ - if (editor.state.inputs.length) { - - /** getting firstlevel parent of input */ - firstLevelBlock = editor.content.getFirstLevelBlock(editor.state.inputs[indexOfLastInput]); - - } - - /** If input is empty, then we set caret to the last input */ - if (editor.state.inputs.length && editor.state.inputs[indexOfLastInput].textContent === '' && firstLevelBlock.dataset.tool == editor.settings.initialBlockPlugin) { - - editor.caret.setToBlock(indexOfLastInput); - - } else { - - /** Create new input when caret clicked in redactors area */ - var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin; - - editor.content.insertBlock({ - type : NEW_BLOCK_TYPE, - block : editor.tools[NEW_BLOCK_TYPE].render() - }); - - /** If there is no inputs except inserted */ - if (editor.state.inputs.length === 1) { - - editor.caret.setToBlock(indexOfLastInput); - - } else { - - /** Set caret to this appended input */ - editor.caret.setToNextBlock(indexOfLastInput); - - } - - } - - } else { - - /** Close all panels */ - editor.toolbar.settings.close(); - editor.toolbar.toolbox.close(); - - } - - /** - * Move toolbar and open - */ - editor.toolbar.move(); - editor.toolbar.open(); - - var inputIsEmpty = !editor.content.currentNode.textContent.trim(), - currentNodeType = editor.content.currentNode.dataset.tool, - isInitialType = currentNodeType == editor.settings.initialBlockPlugin; - - - /** Hide plus buttons */ - editor.toolbar.hidePlusButton(); - - if (!inputIsEmpty) { - - /** Mark current block */ - editor.content.markBlock(); - - } - - if ( isInitialType && inputIsEmpty ) { - - /** Show plus button */ - editor.toolbar.showPlusButton(); - - } - - - }; - - /** - * This method allows to define, is caret in contenteditable element or not. - * - * @private - * - * @description Otherwise, if we get TEXT node from range container, that will means we have input index. - * In this case we use default browsers behaviour (if plugin allows that) or overwritten action. - * Therefore, to be sure that we've clicked first-level block area, we should have currentNode, which always - * specifies to the first-level block. Other cases we just ignore. - */ - var detectWhenClickedOnFirstLevelBlockArea_ = function () { - - var selection = window.getSelection(), - anchorNode = selection.anchorNode, - flag = false; - - if (selection.rangeCount === 0) { - - editor.content.editorAreaHightlighted = true; - - } else { - - if (!editor.core.isDomNode(anchorNode)) { - - anchorNode = anchorNode.parentNode; - - } - - /** Already founded, without loop */ - if (anchorNode.contentEditable == 'true') { - - flag = true; - - } - - while (anchorNode.contentEditable != 'true') { - - anchorNode = anchorNode.parentNode; - - if (anchorNode.contentEditable == 'true') { - - flag = true; - - } - - if (anchorNode == document.body) { - - break; - - } - - } - - /** If editable element founded, flag is "TRUE", Therefore we return "FALSE" */ - editor.content.editorAreaHightlighted = !flag; - - } - - }; - - /** - * Toolbar button click handler - * - * @param {Object} event - cursor to the button - * @protected - * - * @description gets current tool and calls render method - */ - callbacks.toolbarButtonClicked = function (event) { - - var button = this; - - editor.toolbar.current = button.dataset.type; - - editor.toolbar.toolbox.toolClicked(event); - editor.toolbar.close(); - - }; - - /** - * Show or Hide toolbox when plus button is clicked - */ - callbacks.plusButtonClicked = function () { - - if (!editor.nodes.toolbox.classList.contains('opened')) { - - editor.toolbar.toolbox.open(); - - } else { - - editor.toolbar.toolbox.close(); - - } - - }; - - /** - * Block handlers for KeyDown events - * - * @protected - * @param {Object} event - * - * Handles keydowns on block - * @see blockRightOrDownArrowPressed_ - * @see backspacePressed_ - * @see blockLeftOrUpArrowPressed_ - */ - callbacks.blockKeydown = function (event) { - - let block = event.target; // event.target is input - - switch (event.keyCode) { - - case editor.core.keys.DOWN: - case editor.core.keys.RIGHT: - blockRightOrDownArrowPressed_(event); - break; - - case editor.core.keys.BACKSPACE: - backspacePressed_(block, event); - break; - - case editor.core.keys.UP: - case editor.core.keys.LEFT: - blockLeftOrUpArrowPressed_(event); - break; - - } - - }; - - /** - * RIGHT or DOWN keydowns on block - * - * @param {Object} event - * @private - * - * @description watches the selection and gets closest editable element. - * Uses method getDeepestTextNodeFromPosition to get the last node of next block - * Sets caret if it is contenteditable - */ - var blockRightOrDownArrowPressed_ = function (event) { - - var selection = window.getSelection(), - inputs = editor.state.inputs, - focusedNode = selection.anchorNode, - focusedNodeHolder; - - /** Check for caret existance */ - if (!focusedNode) { - - return false; - - } - - /** Looking for closest (parent) contentEditable element of focused node */ - while (focusedNode.contentEditable != 'true') { - - focusedNodeHolder = focusedNode.parentNode; - focusedNode = focusedNodeHolder; - - } - - /** Input index in DOM level */ - var editableElementIndex = 0; - - while (focusedNode != inputs[editableElementIndex]) { - - editableElementIndex ++; - - } - - /** - * Founded contentEditable element doesn't have childs - * Or maybe New created block - */ - if (!focusedNode.textContent) { - - editor.caret.setToNextBlock(editableElementIndex); - return; - - } - - /** - * Do nothing when caret doesn not reaches the end of last child - */ - var caretInLastChild = false, - caretAtTheEndOfText = false; - - var lastChild, - deepestTextnode; - - lastChild = focusedNode.childNodes[focusedNode.childNodes.length - 1 ]; - - if (editor.core.isDomNode(lastChild)) { - - deepestTextnode = editor.content.getDeepestTextNodeFromPosition(lastChild, lastChild.childNodes.length); - - } else { - - deepestTextnode = lastChild; - - } - - caretInLastChild = selection.anchorNode == deepestTextnode; - caretAtTheEndOfText = deepestTextnode.length == selection.anchorOffset; - - if ( !caretInLastChild || !caretAtTheEndOfText ) { - - editor.core.log('arrow [down|right] : caret does not reached the end'); - return false; - - } - - editor.caret.setToNextBlock(editableElementIndex); - - }; - - /** - * LEFT or UP keydowns on block - * - * @param {Object} event - * @private - * - * watches the selection and gets closest editable element. - * Uses method getDeepestTextNodeFromPosition to get the last node of previous block - * Sets caret if it is contenteditable - * - */ - var blockLeftOrUpArrowPressed_ = function (event) { - - var selection = window.getSelection(), - inputs = editor.state.inputs, - focusedNode = selection.anchorNode, - focusedNodeHolder; - - /** Check for caret existance */ - if (!focusedNode) { - - return false; - - } - - /** - * LEFT or UP not at the beginning - */ - if ( selection.anchorOffset !== 0) { - - return false; - - } - - /** Looking for parent contentEditable block */ - while (focusedNode.contentEditable != 'true') { - - focusedNodeHolder = focusedNode.parentNode; - focusedNode = focusedNodeHolder; - - } - - /** Input index in DOM level */ - var editableElementIndex = 0; - - while (focusedNode != inputs[editableElementIndex]) { - - editableElementIndex ++; - - } - - /** - * Do nothing if caret is not at the beginning of first child - */ - var caretInFirstChild = false, - caretAtTheBeginning = false; - - var firstChild, - deepestTextnode; - - /** - * Founded contentEditable element doesn't have childs - * Or maybe New created block - */ - if (!focusedNode.textContent) { - - editor.caret.setToPreviousBlock(editableElementIndex); - return; - - } - - firstChild = focusedNode.childNodes[0]; - - if (editor.core.isDomNode(firstChild)) { - - deepestTextnode = editor.content.getDeepestTextNodeFromPosition(firstChild, 0); - - } else { - - deepestTextnode = firstChild; - - } - - caretInFirstChild = selection.anchorNode == deepestTextnode; - caretAtTheBeginning = selection.anchorOffset === 0; - - if ( caretInFirstChild && caretAtTheBeginning ) { - - editor.caret.setToPreviousBlock(editableElementIndex); - - } - - }; - - /** - * Handles backspace keydown - * - * @param {Element} block - * @param {Object} event - * @private - * - * @description if block is empty, delete the block and set caret to the previous block - * If block is not empty, try to merge two blocks - current and previous - * But it we try'n to remove first block, then we should set caret to the next block, not previous. - * If we removed the last block, create new one - */ - var backspacePressed_ = function (block, event) { - - var currentInputIndex = editor.caret.getCurrentInputIndex(), - range, - selectionLength, - firstLevelBlocksCount; - - if (editor.core.isNativeInput(event.target)) { - - /** If input value is empty - remove block */ - if (event.target.value.trim() == '') { - - block.remove(); - - } else { - - return; - - } - - } - - if (block.textContent.trim()) { - - range = editor.content.getRange(); - selectionLength = range.endOffset - range.startOffset; - - if (editor.caret.position.atStart() && !selectionLength && editor.state.inputs[currentInputIndex - 1]) { - - editor.content.mergeBlocks(currentInputIndex); - - } else { - - return; - - } - - } - - if (!selectionLength) { - - block.remove(); - - } - - - firstLevelBlocksCount = editor.nodes.redactor.childNodes.length; - - /** - * If all blocks are removed - */ - if (firstLevelBlocksCount === 0) { - - /** update currentNode variable */ - editor.content.currentNode = null; - - /** Inserting new empty initial block */ - editor.ui.addInitialBlock(); - - /** Updating inputs state after deleting last block */ - editor.ui.saveInputs(); - - /** Set to current appended block */ - window.setTimeout(function () { - - editor.caret.setToPreviousBlock(1); - - }, 10); - - } else { - - if (editor.caret.inputIndex !== 0) { - - /** Target block is not first */ - editor.caret.setToPreviousBlock(editor.caret.inputIndex); - - } else { - - /** If we try to delete first block */ - editor.caret.setToNextBlock(editor.caret.inputIndex); - - } - - } - - editor.toolbar.move(); - - if (!editor.toolbar.opened) { - - editor.toolbar.open(); - - } - - /** Updating inputs state */ - editor.ui.saveInputs(); - - /** Prevent default browser behaviour */ - event.preventDefault(); - - }; - - /** - * used by UI module - * Clicks on block settings button - * - * @param {Object} event - * @protected - * @description Opens toolbar settings - */ - callbacks.showSettingsButtonClicked = function (event) { - - /** - * Get type of current block - * It uses to append settings from tool.settings property. - * ... - * Type is stored in data-type attribute on block - */ - var currentToolType = editor.content.currentNode.dataset.tool; - - editor.toolbar.settings.toggle(currentToolType); - - /** Close toolbox when settings button is active */ - editor.toolbar.toolbox.close(); - editor.toolbar.settings.hideRemoveActions(); - - }; - - return callbacks; - -})({}); \ No newline at end of file diff --git a/modules/caret.js b/modules/caret.js deleted file mode 100644 index 21b7599c..00000000 --- a/modules/caret.js +++ /dev/null @@ -1,305 +0,0 @@ -/** - * Codex Editor Caret Module - * - * @author Codex Team - * @version 1.0 - */ - -module.exports = (function (caret) { - - let editor = codex.editor; - - /** - * @var {int} InputIndex - editable element in DOM - */ - caret.inputIndex = null; - - /** - * @var {int} offset - caret position in a text node. - */ - caret.offset = null; - - /** - * @var {int} focusedNodeIndex - we get index of child node from first-level block - */ - caret.focusedNodeIndex = null; - - /** - * Creates Document Range and sets caret to the element. - * @protected - * @uses caret.save — if you need to save caret position - * @param {Element} el - Changed Node. - */ - caret.set = function ( el, index, offset) { - - offset = offset || caret.offset || 0; - index = index || caret.focusedNodeIndex || 0; - - var childs = el.childNodes, - nodeToSet; - - if ( childs.length === 0 ) { - - nodeToSet = el; - - } else { - - nodeToSet = childs[index]; - - } - - /** If Element is INPUT */ - if (el.contentEditable != 'true') { - - el.focus(); - return; - - } - - if (editor.core.isDomNode(nodeToSet)) { - - nodeToSet = editor.content.getDeepestTextNodeFromPosition(nodeToSet, nodeToSet.childNodes.length); - - } - - var range = document.createRange(), - selection = window.getSelection(); - - window.setTimeout(function () { - - range.setStart(nodeToSet, offset); - range.setEnd(nodeToSet, offset); - - selection.removeAllRanges(); - selection.addRange(range); - - editor.caret.saveCurrentInputIndex(); - - }, 20); - - }; - - /** - * @protected - * Updates index of input and saves it in caret object - */ - caret.saveCurrentInputIndex = function () { - - /** Index of Input that we paste sanitized content */ - var selection = window.getSelection(), - inputs = editor.state.inputs, - focusedNode = selection.anchorNode, - focusedNodeHolder; - - if (!focusedNode) { - - return; - - } - - /** Looking for parent contentEditable block */ - while (focusedNode && focusedNode.contentEditable != 'true') { - - focusedNodeHolder = focusedNode.parentNode; - focusedNode = focusedNodeHolder; - - } - - /** Input index in DOM level */ - var editableElementIndex = 0; - - while (focusedNode != inputs[editableElementIndex]) { - - editableElementIndex ++; - - } - - caret.inputIndex = editableElementIndex; - - }; - - /** - * Returns current input index (caret object) - */ - caret.getCurrentInputIndex = function () { - - return caret.inputIndex; - - }; - - /** - * @param {int} index - index of first-level block after that we set caret into next input - */ - caret.setToNextBlock = function (index) { - - var inputs = editor.state.inputs, - nextInput = inputs[index + 1]; - - if (!nextInput) { - - editor.core.log('We are reached the end'); - return; - - } - - /** - * When new Block created or deleted content of input - * We should add some text node to set caret - */ - if (!nextInput.childNodes.length) { - - var emptyTextElement = document.createTextNode(''); - - nextInput.appendChild(emptyTextElement); - - } - - editor.caret.inputIndex = index + 1; - editor.caret.set(nextInput, 0, 0); - editor.content.workingNodeChanged(nextInput); - - }; - - /** - * @param {int} index - index of target input. - * Sets caret to input with this index - */ - caret.setToBlock = function (index) { - - var inputs = editor.state.inputs, - targetInput = inputs[index]; - - if ( !targetInput ) { - - return; - - } - - /** - * When new Block created or deleted content of input - * We should add some text node to set caret - */ - if (!targetInput.childNodes.length) { - - var emptyTextElement = document.createTextNode(''); - - targetInput.appendChild(emptyTextElement); - - } - - editor.caret.inputIndex = index; - editor.caret.set(targetInput, 0, 0); - editor.content.workingNodeChanged(targetInput); - - }; - - /** - * @param {int} index - index of input - */ - caret.setToPreviousBlock = function (index) { - - index = index || 0; - - var inputs = editor.state.inputs, - previousInput = inputs[index - 1], - lastChildNode, - lengthOfLastChildNode, - emptyTextElement; - - - if (!previousInput) { - - editor.core.log('We are reached first node'); - return; - - } - - lastChildNode = editor.content.getDeepestTextNodeFromPosition(previousInput, previousInput.childNodes.length); - lengthOfLastChildNode = lastChildNode.length; - - /** - * When new Block created or deleted content of input - * We should add some text node to set caret - */ - if (!previousInput.childNodes.length) { - - emptyTextElement = document.createTextNode(''); - previousInput.appendChild(emptyTextElement); - - } - editor.caret.inputIndex = index - 1; - editor.caret.set(previousInput, previousInput.childNodes.length - 1, lengthOfLastChildNode); - editor.content.workingNodeChanged(inputs[index - 1]); - - }; - - caret.position = { - - atStart : function () { - - var selection = window.getSelection(), - anchorOffset = selection.anchorOffset, - anchorNode = selection.anchorNode, - firstLevelBlock = editor.content.getFirstLevelBlock(anchorNode), - pluginsRender = firstLevelBlock.childNodes[0]; - - if (!editor.core.isDomNode(anchorNode)) { - - anchorNode = anchorNode.parentNode; - - } - - var isFirstNode = anchorNode === pluginsRender.childNodes[0], - isOffsetZero = anchorOffset === 0; - - return isFirstNode && isOffsetZero; - - }, - - atTheEnd : function () { - - var selection = window.getSelection(), - anchorOffset = selection.anchorOffset, - anchorNode = selection.anchorNode; - - /** Caret is at the end of input */ - return !anchorNode || !anchorNode.length || anchorOffset === anchorNode.length; - - } - }; - - - /** - * Inserts node at the caret location - * @param {HTMLElement|DocumentFragment} node - */ - caret.insertNode = function (node) { - - var selection, range, - lastNode = node; - - if (node.nodeType == editor.core.nodeTypes.DOCUMENT_FRAGMENT) { - - lastNode = node.lastChild; - - } - - selection = window.getSelection(); - - range = selection.getRangeAt(0); - range.deleteContents(); - - range.insertNode(node); - - range.setStartAfter(lastNode); - range.collapse(true); - - selection.removeAllRanges(); - selection.addRange(range); - - - }; - - return caret; - -})({}); diff --git a/modules/content.js b/modules/content.js deleted file mode 100644 index 1ed1a634..00000000 --- a/modules/content.js +++ /dev/null @@ -1,805 +0,0 @@ -/** - * Codex Editor Content Module - * Works with DOM - * - * @module Codex Editor content module - * - * @author Codex Team - * @version 1.3.13 - * - * @description Module works with Elements that have been appended to the main DOM - */ - -module.exports = (function (content) { - - let editor = codex.editor; - - /** - * Links to current active block - * @type {null | Element} - */ - content.currentNode = null; - - /** - * clicked in redactor area - * @type {null | Boolean} - */ - content.editorAreaHightlighted = null; - - /** - * @deprecated - * Synchronizes redactor with original textarea - */ - content.sync = function () { - - editor.core.log('syncing...'); - - /** - * Save redactor content to editor.state - */ - editor.state.html = editor.nodes.redactor.innerHTML; - - }; - - /** - * Appends background to the block - * - * @description add CSS class to highlight visually first-level block area - */ - content.markBlock = function () { - - editor.content.currentNode.classList.add(editor.ui.className.BLOCK_HIGHLIGHTED); - - }; - - /** - * Clear background - * - * @description clears styles that highlights block - */ - content.clearMark = function () { - - if (editor.content.currentNode) { - - editor.content.currentNode.classList.remove(editor.ui.className.BLOCK_HIGHLIGHTED); - - } - - }; - - /** - * Finds first-level block - * - * @param {Element} node - selected or clicked in redactors area node - * @protected - * - * @description looks for first-level block. - * gets parent while node is not first-level - */ - content.getFirstLevelBlock = function (node) { - - if (!editor.core.isDomNode(node)) { - - node = node.parentNode; - - } - - if (node === editor.nodes.redactor || node === document.body) { - - return null; - - } else { - - while(!node.classList.contains(editor.ui.className.BLOCK_CLASSNAME)) { - - node = node.parentNode; - - } - - return node; - - } - - }; - - /** - * Trigger this event when working node changed - * @param {Element} targetNode - first-level of this node will be current - * @protected - * - * @description If targetNode is first-level then we set it as current else we look for parents to find first-level - */ - content.workingNodeChanged = function (targetNode) { - - /** Clear background from previous marked block before we change */ - editor.content.clearMark(); - - if (!targetNode) { - - return; - - } - - content.currentNode = content.getFirstLevelBlock(targetNode); - - }; - - /** - * Replaces one redactor block with another - * @protected - * @param {Element} targetBlock - block to replace. Mostly currentNode. - * @param {Element} newBlock - * @param {string} newBlockType - type of new block; we need to store it to data-attribute - * - * [!] Function does not saves old block content. - * You can get it manually and pass with newBlock.innerHTML - */ - content.replaceBlock = function (targetBlock, newBlock) { - - if (!targetBlock || !newBlock) { - - editor.core.log('replaceBlock: missed params'); - return; - - } - - /** If target-block is not a frist-level block, then we iterate parents to find it */ - while(!targetBlock.classList.contains(editor.ui.className.BLOCK_CLASSNAME)) { - - targetBlock = targetBlock.parentNode; - - } - - /** Replacing */ - editor.nodes.redactor.replaceChild(newBlock, targetBlock); - - /** - * Set new node as current - */ - editor.content.workingNodeChanged(newBlock); - - /** - * Add block handlers - */ - editor.ui.addBlockHandlers(newBlock); - - /** - * Save changes - */ - editor.ui.saveInputs(); - - }; - - /** - * @protected - * - * Inserts new block to redactor - * Wrapps block into a DIV with BLOCK_CLASSNAME class - * - * @param blockData {object} - * @param blockData.block {Element} element with block content - * @param blockData.type {string} block plugin - * @param needPlaceCaret {bool} pass true to set caret in new block - * - */ - content.insertBlock = function ( blockData, needPlaceCaret ) { - - var workingBlock = editor.content.currentNode, - newBlockContent = blockData.block, - blockType = blockData.type, - isStretched = blockData.stretched; - - var newBlock = composeNewBlock_(newBlockContent, blockType, isStretched); - - if (workingBlock) { - - editor.core.insertAfter(workingBlock, newBlock); - - } else { - - /** - * If redactor is empty, append as first child - */ - editor.nodes.redactor.appendChild(newBlock); - - } - - /** - * Block handler - */ - editor.ui.addBlockHandlers(newBlock); - - /** - * Set new node as current - */ - editor.content.workingNodeChanged(newBlock); - - /** - * Save changes - */ - editor.ui.saveInputs(); - - - if ( needPlaceCaret ) { - - /** - * If we don't know input index then we set default value -1 - */ - var currentInputIndex = editor.caret.getCurrentInputIndex() || -1; - - - if (currentInputIndex == -1) { - - - var editableElement = newBlock.querySelector('[contenteditable]'), - emptyText = document.createTextNode(''); - - editableElement.appendChild(emptyText); - editor.caret.set(editableElement, 0, 0); - - editor.toolbar.move(); - editor.toolbar.showPlusButton(); - - - } else { - - if (currentInputIndex === editor.state.inputs.length - 1) - return; - - /** Timeout for browsers execution */ - window.setTimeout(function () { - - /** Setting to the new input */ - editor.caret.setToNextBlock(currentInputIndex); - editor.toolbar.move(); - editor.toolbar.open(); - - }, 10); - - } - - } - - /** - * Block is inserted, wait for new click that defined focusing on editors area - * @type {boolean} - */ - content.editorAreaHightlighted = false; - - }; - - /** - * Replaces blocks with saving content - * @protected - * @param {Element} noteToReplace - * @param {Element} newNode - * @param {Element} blockType - */ - content.switchBlock = function (blockToReplace, newBlock, tool) { - - tool = tool || editor.content.currentNode.dataset.tool; - var newBlockComposed = composeNewBlock_(newBlock, tool); - - /** Replacing */ - editor.content.replaceBlock(blockToReplace, newBlockComposed); - - /** Save new Inputs when block is changed */ - editor.ui.saveInputs(); - - }; - - /** - * Iterates between child noted and looking for #text node on deepest level - * @protected - * - * @param {Element} block - node where find - * @param {int} postiton - starting postion - * Example: childNodex.length to find from the end - * or 0 to find from the start - * @return {Text} block - * @uses DFS - */ - content.getDeepestTextNodeFromPosition = function (block, position) { - - /** - * Clear Block from empty and useless spaces with trim. - * Such nodes we should remove - */ - var blockChilds = block.childNodes, - index, - node, - text; - - for(index = 0; index < blockChilds.length; index++) { - - node = blockChilds[index]; - - if (node.nodeType == editor.core.nodeTypes.TEXT) { - - text = node.textContent.trim(); - - /** Text is empty. We should remove this child from node before we start DFS - * decrease the quantity of childs. - */ - if (text === '') { - - block.removeChild(node); - position--; - - } - - } - - } - - if (block.childNodes.length === 0) { - - return document.createTextNode(''); - - } - - /** Setting default position when we deleted all empty nodes */ - if ( position < 0 ) - position = 1; - - var lookingFromStart = false; - - /** For looking from START */ - if (position === 0) { - - lookingFromStart = true; - position = 1; - - } - - while ( position ) { - - /** initial verticle of node. */ - if ( lookingFromStart ) { - - block = block.childNodes[0]; - - } else { - - block = block.childNodes[position - 1]; - - } - - if ( block.nodeType == editor.core.nodeTypes.TAG ) { - - position = block.childNodes.length; - - } else if (block.nodeType == editor.core.nodeTypes.TEXT ) { - - position = 0; - - } - - } - - return block; - - }; - - /** - * @private - * @param {Element} block - current plugins render - * @param {String} tool - plugins name - * @param {Boolean} isStretched - make stretched block or not - * - * @description adds necessary information to wrap new created block by first-level holder - */ - var composeNewBlock_ = function (block, tool, isStretched) { - - var newBlock = editor.draw.node('DIV', editor.ui.className.BLOCK_CLASSNAME, {}), - blockContent = editor.draw.node('DIV', editor.ui.className.BLOCK_CONTENT, {}); - - blockContent.appendChild(block); - newBlock.appendChild(blockContent); - - if (isStretched) { - - blockContent.classList.add(editor.ui.className.BLOCK_STRETCHED); - - } - - newBlock.dataset.tool = tool; - return newBlock; - - }; - - /** - * Returns Range object of current selection - * @protected - */ - content.getRange = function () { - - var selection = window.getSelection().getRangeAt(0); - - return selection; - - }; - - /** - * Divides block in two blocks (after and before caret) - * - * @protected - * @param {int} inputIndex - target input index - * - * @description splits current input content to the separate blocks - * When enter is pressed among the words, that text will be splited. - */ - content.splitBlock = function (inputIndex) { - - var selection = window.getSelection(), - anchorNode = selection.anchorNode, - anchorNodeText = anchorNode.textContent, - caretOffset = selection.anchorOffset, - textBeforeCaret, - textNodeBeforeCaret, - textAfterCaret, - textNodeAfterCaret; - - var currentBlock = editor.content.currentNode.querySelector('[contentEditable]'); - - - textBeforeCaret = anchorNodeText.substring(0, caretOffset); - textAfterCaret = anchorNodeText.substring(caretOffset); - - textNodeBeforeCaret = document.createTextNode(textBeforeCaret); - - if (textAfterCaret) { - - textNodeAfterCaret = document.createTextNode(textAfterCaret); - - } - - var previousChilds = [], - nextChilds = [], - reachedCurrent = false; - - if (textNodeAfterCaret) { - - nextChilds.push(textNodeAfterCaret); - - } - - for ( var i = 0, child; !!(child = currentBlock.childNodes[i]); i++) { - - if ( child != anchorNode ) { - - if ( !reachedCurrent ) { - - previousChilds.push(child); - - } else { - - nextChilds.push(child); - - } - - } else { - - reachedCurrent = true; - - } - - } - - /** Clear current input */ - editor.state.inputs[inputIndex].innerHTML = ''; - - /** - * Append all childs founded before anchorNode - */ - var previousChildsLength = previousChilds.length; - - for(i = 0; i < previousChildsLength; i++) { - - editor.state.inputs[inputIndex].appendChild(previousChilds[i]); - - } - - editor.state.inputs[inputIndex].appendChild(textNodeBeforeCaret); - - /** - * Append text node which is after caret - */ - var nextChildsLength = nextChilds.length, - newNode = document.createElement('div'); - - for(i = 0; i < nextChildsLength; i++) { - - newNode.appendChild(nextChilds[i]); - - } - - newNode = newNode.innerHTML; - - /** This type of block creates when enter is pressed */ - var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin; - - /** - * Make new paragraph with text after caret - */ - editor.content.insertBlock({ - type : NEW_BLOCK_TYPE, - block : editor.tools[NEW_BLOCK_TYPE].render({ - text : newNode - }) - }, true ); - - }; - - /** - * Merges two blocks — current and target - * If target index is not exist, then previous will be as target - * - * @protected - * @param {int} currentInputIndex - * @param {int} targetInputIndex - * - * @description gets two inputs indexes and merges into one - */ - content.mergeBlocks = function (currentInputIndex, targetInputIndex) { - - /** If current input index is zero, then prevent method execution */ - if (currentInputIndex === 0) { - - return; - - } - - var targetInput, - currentInputContent = editor.state.inputs[currentInputIndex].innerHTML; - - if (!targetInputIndex) { - - targetInput = editor.state.inputs[currentInputIndex - 1]; - - } else { - - targetInput = editor.state.inputs[targetInputIndex]; - - } - - targetInput.innerHTML += currentInputContent; - - }; - - /** - * Iterates all right siblings and parents, which has right siblings - * while it does not reached the first-level block - * - * @param {Element} node - * @return {boolean} - */ - content.isLastNode = function (node) { - - // console.log('погнали перебор родителей'); - - var allChecked = false; - - while ( !allChecked ) { - - // console.log('Смотрим на %o', node); - // console.log('Проверим, пустые ли соседи справа'); - - if ( !allSiblingsEmpty_(node) ) { - - // console.log('Есть непустые соседи. Узел не последний. Выходим.'); - return false; - - } - - node = node.parentNode; - - /** - * Проверяем родителей до тех пор, пока не найдем блок первого уровня - */ - if ( node.classList.contains(editor.ui.className.BLOCK_CONTENT) ) { - - allChecked = true; - - } - - } - - return true; - - }; - - /** - * Checks if all element right siblings is empty - * @param node - */ - var allSiblingsEmpty_ = function (node) { - - /** - * Нужно убедиться, что после пустого соседа ничего нет - */ - var sibling = node.nextSibling; - - while ( sibling ) { - - if (sibling.textContent.length) { - - return false; - - } - - sibling = sibling.nextSibling; - - } - - return true; - - }; - - /** - * @public - * - * @param {string} htmlData - html content as string - * @param {string} plainData - plain text - * @return {string} - html content as string - */ - content.wrapTextWithParagraphs = function (htmlData, plainData) { - - if (!htmlData.trim()) { - - return wrapPlainTextWithParagraphs(plainData); - - } - - var wrapper = document.createElement('DIV'), - newWrapper = document.createElement('DIV'), - i, - paragraph, - firstLevelBlocks = ['DIV', 'P'], - blockTyped, - node; - - /** - * Make HTML Element to Wrap Text - * It allows us to work with input data as HTML content - */ - wrapper.innerHTML = htmlData; - paragraph = document.createElement('P'); - - for (i = 0; i < wrapper.childNodes.length; i++) { - - node = wrapper.childNodes[i]; - - blockTyped = firstLevelBlocks.indexOf(node.tagName) != -1; - - /** - * If node is first-levet - * we add this node to our new wrapper - */ - if ( blockTyped ) { - - /** - * If we had splitted inline nodes to paragraph before - */ - if ( paragraph.childNodes.length ) { - - newWrapper.appendChild(paragraph.cloneNode(true)); - - /** empty paragraph */ - paragraph = null; - paragraph = document.createElement('P'); - - } - - newWrapper.appendChild(node.cloneNode(true)); - - } else { - - /** Collect all inline nodes to one as paragraph */ - paragraph.appendChild(node.cloneNode(true)); - - /** if node is last we should append this node to paragraph and paragraph to new wrapper */ - if ( i == wrapper.childNodes.length - 1 ) { - - newWrapper.appendChild(paragraph.cloneNode(true)); - - } - - } - - } - - return newWrapper.innerHTML; - - }; - - /** - * Splits strings on new line and wraps paragraphs with

tag - * @param plainText - * @returns {string} - */ - var wrapPlainTextWithParagraphs = function (plainText) { - - if (!plainText) return ''; - - return '

' + plainText.split('\n\n').join('

') + '

'; - - }; - - /** - * Finds closest Contenteditable parent from Element - * @param {Element} node element looking from - * @return {Element} node contenteditable - */ - content.getEditableParent = function (node) { - - while (node && node.contentEditable != 'true') { - - node = node.parentNode; - - } - - return node; - - }; - - /** - * Clear editors content - * - * @param {Boolean} all — if true, delete all article data (content, id, etc.) - */ - content.clear = function (all) { - - editor.nodes.redactor.innerHTML = ''; - editor.content.sync(); - editor.ui.saveInputs(); - if (all) { - - editor.state.blocks = {}; - - } else if (editor.state.blocks) { - - editor.state.blocks.items = []; - - } - - editor.content.currentNode = null; - - }; - - /** - * - * Load new data to editor - * If editor is not empty, just append articleData.items - * - * @param articleData.items - */ - content.load = function (articleData) { - - var currentContent = Object.assign({}, editor.state.blocks); - - editor.content.clear(); - - if (!Object.keys(currentContent).length) { - - editor.state.blocks = articleData; - - } else if (!currentContent.items) { - - currentContent.items = articleData.items; - editor.state.blocks = currentContent; - - } else { - - currentContent.items = currentContent.items.concat(articleData.items); - editor.state.blocks = currentContent; - - } - - editor.renderer.makeBlocksFromData(); - - }; - - return content; - -})({}); \ No newline at end of file diff --git a/modules/core.js b/modules/core.js deleted file mode 100644 index ad4a08b8..00000000 --- a/modules/core.js +++ /dev/null @@ -1,389 +0,0 @@ -/** - * Codex Editor Core - * - * @author Codex Team - * @version 1.1.3 - */ - -module.exports = (function (core) { - - let editor = codex.editor; - - /** - * @public - * - * Editor preparing method - * @return Promise - */ - core.prepare = function (userSettings) { - - return new Promise(function (resolve, reject) { - - if ( userSettings ) { - - editor.settings.tools = userSettings.tools || editor.settings.tools; - - } - - if (userSettings.data) { - - editor.state.blocks = userSettings.data; - - } - - if (userSettings.initialBlockPlugin) { - - editor.settings.initialBlockPlugin = userSettings.initialBlockPlugin; - - } - - if (userSettings.sanitizer) { - - editor.settings.sanitizer = userSettings.sanitizer; - - } - - editor.hideToolbar = userSettings.hideToolbar; - - editor.settings.placeholder = userSettings.placeholder || ''; - - editor.nodes.holder = document.getElementById(userSettings.holderId || editor.settings.holderId); - - if (typeof editor.nodes.holder === undefined || editor.nodes.holder === null) { - - reject(Error("Holder wasn't found by ID: #" + userSettings.holderId)); - - } else { - - resolve(); - - } - - }); - - }; - - /** - * Logging method - * @param type = ['log', 'info', 'warn'] - */ - core.log = function (msg, type, arg) { - - type = type || 'log'; - - if (!arg) { - - arg = msg || 'undefined'; - msg = '[codex-editor]: %o'; - - } else { - - msg = '[codex-editor]: ' + msg; - - } - - try{ - - if ( 'console' in window && window.console[ type ] ) { - - if ( arg ) window.console[ type ]( msg, arg ); - else window.console[ type ]( msg ); - - } - - }catch(e) {} - - }; - - /** - * @protected - * - * Helper for insert one element after another - */ - core.insertAfter = function (target, element) { - - target.parentNode.insertBefore(element, target.nextSibling); - - }; - - /** - * @const - * - * Readable DOM-node types map - */ - core.nodeTypes = { - TAG : 1, - TEXT : 3, - COMMENT : 8, - DOCUMENT_FRAGMENT: 11 - }; - - /** - * @const - * Readable keys map - */ - core.keys = { BACKSPACE: 8, TAB: 9, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, ESC: 27, SPACE: 32, LEFT: 37, UP: 38, DOWN: 40, RIGHT: 39, DELETE: 46, META: 91 }; - - /** - * @protected - * - * Check object for DOM node - */ - core.isDomNode = function (el) { - - return el && typeof el === 'object' && el.nodeType && el.nodeType == this.nodeTypes.TAG; - - }; - - /** - * Checks passed object for emptiness - * @require ES5 - Object.keys - * @param {object} - */ - core.isEmpty = function ( obj ) { - - return Object.keys(obj).length === 0; - - }; - - /** - * Native Ajax - * @param {String} settings.url - request URL - * @param {function} settings.beforeSend - returned value will be passed as context to the Success, Error and Progress callbacks - * @param {function} settings.success - * @param {function} settings.progress - */ - core.ajax = function (settings) { - - if (!settings || !settings.url) { - - return; - - } - - var XMLHTTP = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'), - encodedString, - isFormData, - prop; - - - settings.async = true; - settings.type = settings.type || 'GET'; - settings.data = settings.data || ''; - settings['content-type'] = settings['content-type'] || 'application/json; charset=utf-8'; - - if (settings.type == 'GET' && settings.data) { - - settings.url = /\?/.test(settings.url) ? settings.url + '&' + settings.data : settings.url + '?' + settings.data; - - } else { - - encodedString = ''; - for(prop in settings.data) { - - encodedString += (prop + '=' + encodeURIComponent(settings.data[prop]) + '&'); - - } - - } - - if (settings.withCredentials) { - - XMLHTTP.withCredentials = true; - - } - - /** - * Value returned in beforeSend funtion will be passed as context to the other response callbacks - * If beforeSend returns false, AJAX will be blocked - */ - let responseContext, - beforeSendResult; - - if (typeof settings.beforeSend === 'function') { - - beforeSendResult = settings.beforeSend.call(); - - if (beforeSendResult === false) { - - return; - - } - - } - - XMLHTTP.open( settings.type, settings.url, settings.async ); - - /** - * If we send FormData, we need no content-type header - */ - isFormData = isFormData_(settings.data); - - if (!isFormData) { - - if (settings.type !== 'POST') { - - XMLHTTP.setRequestHeader('Content-type', settings['content-type']); - - } else { - - XMLHTTP.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - - } - - } - - XMLHTTP.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - - responseContext = beforeSendResult || XMLHTTP; - - if (typeof settings.progress === 'function') { - - XMLHTTP.upload.onprogress = settings.progress.bind(responseContext); - - } - - XMLHTTP.onreadystatechange = function () { - - if (XMLHTTP.readyState === 4) { - - if (XMLHTTP.status === 200) { - - if (typeof settings.success === 'function') { - - settings.success.call(responseContext, XMLHTTP.responseText); - - } - - } else { - - if (typeof settings.error === 'function') { - - settings.error.call(responseContext, XMLHTTP.responseText, XMLHTTP.status); - - } - - } - - } - - }; - - if (isFormData) { - - // Sending FormData - XMLHTTP.send(settings.data); - - } else { - - // POST requests - XMLHTTP.send(encodedString); - - } - - return XMLHTTP; - - }; - - /** - * Appends script to head of document - * @return Promise - */ - core.importScript = function (scriptPath, instanceName) { - - return new Promise(function (resolve, reject) { - - let script; - - /** Script is already loaded */ - if ( !instanceName ) { - - reject('Instance name is missed'); - - } else if ( document.getElementById(editor.scriptPrefix + instanceName) ) { - - resolve(scriptPath); - - } - - script = document.createElement('SCRIPT'); - script.async = true; - script.defer = true; - script.id = editor.scriptPrefix + instanceName; - - script.onload = function () { - - resolve(scriptPath); - - }; - - script.onerror = function () { - - reject(scriptPath); - - }; - - script.src = scriptPath; - document.head.appendChild(script); - - }); - - }; - - /** - * Function for checking is it FormData object to send. - * @param {Object} object to check - * @return boolean - */ - var isFormData_ = function (object) { - - return object instanceof FormData; - - }; - - /** - * Check block - * @param target - * @description Checks target is it native input - */ - core.isNativeInput = function (target) { - - var nativeInputAreas = ['INPUT', 'TEXTAREA']; - - return nativeInputAreas.indexOf(target.tagName) != -1; - - }; - - /** - * Check if block is empty - * We should check block textContent, child native inputs and some exceptions like IMG and IFRAME - * - * @param block - * @returns {boolean} - */ - core.isBlockEmpty = function (block) { - - const EXCEPTION_TAGS = ['IMG', 'IFRAME']; - - var nativeInputs = block.querySelectorAll('textarea, input'), - nativeInputsAreEmpty = true, - textContentIsEmpty = !block.textContent.trim(); - - Array.prototype.forEach.call(nativeInputs, function (input) { - - if (input.type == 'textarea' || input.type == 'text') { - - nativeInputsAreEmpty = nativeInputsAreEmpty && !input.value.trim(); - - } - - }); - - return textContentIsEmpty && nativeInputsAreEmpty && !EXCEPTION_TAGS.includes(block.tagName); - - }; - - - return core; - -})({}); diff --git a/modules/destroyer.js b/modules/destroyer.js deleted file mode 100644 index 989f9478..00000000 --- a/modules/destroyer.js +++ /dev/null @@ -1,98 +0,0 @@ -/** - * Codex Editor Destroyer module - * - * @auhor Codex Team - * @version 1.0 - */ - -module.exports = function (destroyer) { - - let editor = codex.editor; - - destroyer.removeNodes = function () { - - editor.nodes.wrapper.remove(); - editor.nodes.notifications.remove(); - - }; - - destroyer.destroyPlugins = function () { - - for (var tool in editor.tools) { - - if (typeof editor.tools[tool].destroy === 'function') { - - editor.tools[tool].destroy(); - - } - - } - - }; - - destroyer.destroyScripts = function () { - - var scripts = document.getElementsByTagName('SCRIPT'); - - for (var i = 0; i < scripts.length; i++) { - - if (scripts[i].id.indexOf(editor.scriptPrefix) + 1) { - - scripts[i].remove(); - i--; - - } - - } - - }; - - - /** - * Delete editor data from webpage. - * You should send settings argument with boolean flags: - * @param settings.ui- remove redactor event listeners and DOM nodes - * @param settings.scripts - remove redactor scripts from DOM - * @param settings.plugins - remove plugin's objects - * @param settings.core - remove editor core. You can remove core only if UI and scripts flags is true - * } - * - */ - destroyer.destroy = function (settings) { - - if (!settings || typeof settings !== 'object') { - - return; - - } - - if (settings.ui) { - - destroyer.removeNodes(); - editor.listeners.removeAll(); - - } - - if (settings.scripts) { - - destroyer.destroyScripts(); - - } - - if (settings.plugins) { - - destroyer.destroyPlugins(); - - } - - if (settings.ui && settings.scripts && settings.core) { - - delete codex.editor; - - } - - }; - - return destroyer; - -}({}); \ No newline at end of file diff --git a/modules/draw.js b/modules/draw.js deleted file mode 100644 index 9c0d87cd..00000000 --- a/modules/draw.js +++ /dev/null @@ -1,316 +0,0 @@ -/** - * Codex Editor Draw module - * - * @author Codex Team - * @version 1.0. - */ - -module.exports = (function (draw) { - - /** - * Base editor wrapper - */ - draw.wrapper = function () { - - var wrapper = document.createElement('div'); - - wrapper.className += 'codex-editor'; - - return wrapper; - - }; - - /** - * Content-editable holder - */ - draw.redactor = function () { - - var redactor = document.createElement('div'); - - redactor.className += 'ce-redactor'; - - return redactor; - - }; - - draw.ceBlock = function () { - - var block = document.createElement('DIV'); - - block.className += 'ce_block'; - - return block; - - }; - - /** - * Empty toolbar with toggler - */ - draw.toolbar = function () { - - var bar = document.createElement('div'); - - bar.className += 'ce-toolbar'; - - return bar; - - }; - - draw.toolbarContent = function () { - - var wrapper = document.createElement('DIV'); - - wrapper.classList.add('ce-toolbar__content'); - - return wrapper; - - }; - - /** - * Inline toolbar - */ - draw.inlineToolbar = function () { - - var bar = document.createElement('DIV'); - - bar.className += 'ce-toolbar-inline'; - - return bar; - - }; - - /** - * Wrapper for inline toobar buttons - */ - draw.inlineToolbarButtons = function () { - - var wrapper = document.createElement('DIV'); - - wrapper.className += 'ce-toolbar-inline__buttons'; - - return wrapper; - - }; - - /** - * For some actions - */ - draw.inlineToolbarActions = function () { - - var wrapper = document.createElement('DIV'); - - wrapper.className += 'ce-toolbar-inline__actions'; - - return wrapper; - - }; - - draw.inputForLink = function () { - - var input = document.createElement('INPUT'); - - input.type = 'input'; - input.className += 'inputForLink'; - input.placeholder = 'Вставьте ссылку ...'; - input.setAttribute('form', 'defaultForm'); - - input.setAttribute('autofocus', 'autofocus'); - - return input; - - }; - - /** - * @todo Desc - */ - draw.blockButtons = function () { - - var block = document.createElement('div'); - - block.className += 'ce-toolbar__actions'; - - return block; - - }; - - /** - * Block settings panel - */ - draw.blockSettings = function () { - - var settings = document.createElement('div'); - - settings.className += 'ce-settings'; - - return settings; - - }; - - draw.defaultSettings = function () { - - var div = document.createElement('div'); - - div.classList.add('ce-settings_default'); - - return div; - - }; - - draw.pluginsSettings = function () { - - var div = document.createElement('div'); - - div.classList.add('ce-settings_plugin'); - - return div; - - }; - - draw.plusButton = function () { - - var button = document.createElement('span'); - - button.className = 'ce-toolbar__plus'; - // button.innerHTML = ''; - - return button; - - }; - - /** - * Settings button in toolbar - */ - draw.settingsButton = function () { - - var toggler = document.createElement('span'); - - toggler.className = 'ce-toolbar__settings-btn'; - - /** Toggler button*/ - toggler.innerHTML = ''; - - return toggler; - - }; - - /** - * Redactor tools wrapper - */ - - draw.toolbox = function () { - - var wrapper = document.createElement('div'); - - wrapper.className = 'ce-toolbar__tools'; - - return wrapper; - - }; - - /** - * @protected - * - * Draws tool buttons for toolbox - * - * @param {String} type - * @param {String} classname - * @returns {Element} - */ - draw.toolbarButton = function (type, classname) { - - var button = document.createElement('li'), - toolIcon = document.createElement('i'), - toolTitle = document.createElement('span'); - - button.dataset.type = type; - button.setAttribute('title', type); - - toolIcon.classList.add(classname); - toolTitle.classList.add('ce_toolbar_tools--title'); - - - button.appendChild(toolIcon); - button.appendChild(toolTitle); - - return button; - - }; - - /** - * @protected - * - * Draws tools for inline toolbar - * - * @param {String} type - * @param {String} classname - */ - draw.toolbarButtonInline = function (type, classname) { - - var button = document.createElement('BUTTON'), - toolIcon = document.createElement('I'); - - button.type = 'button'; - button.dataset.type = type; - toolIcon.classList.add(classname); - - button.appendChild(toolIcon); - - return button; - - }; - - /** - * Redactor block - */ - draw.block = function (tagName, content) { - - var node = document.createElement(tagName); - - node.innerHTML = content || ''; - - return node; - - }; - - /** - * Creates Node with passed tagName and className - * @param {string} tagName - * @param {string} className - * @param {object} properties - allow to assign properties - */ - draw.node = function ( tagName, className, properties ) { - - var el = document.createElement( tagName ); - - if ( className ) el.className = className; - - if ( properties ) { - - for (var name in properties) { - - el[name] = properties[name]; - - } - - } - - return el; - - }; - - /** - * Unavailable plugin block - */ - draw.unavailableBlock = function () { - - var wrapper = document.createElement('DIV'); - - wrapper.classList.add('cdx-unavailable-block'); - - return wrapper; - - }; - - return draw; - -})({}); \ No newline at end of file diff --git a/modules/listeners.js b/modules/listeners.js deleted file mode 100644 index 6b353ae2..00000000 --- a/modules/listeners.js +++ /dev/null @@ -1,192 +0,0 @@ -/** - * Codex Editor Listeners module - * - * @author Codex Team - * @version 1.0 - */ - -/** - * Module-decorator for event listeners assignment - */ -module.exports = function (listeners) { - - var allListeners = []; - - /** - * Search methods - * - * byElement, byType and byHandler returns array of suitable listeners - * one and all takes element, eventType, and handler and returns first (all) suitable listener - * - */ - listeners.search = function () { - - var byElement = function (element, context) { - - var listenersOnElement = []; - - context = context || allListeners; - - for (var i = 0; i < context.length; i++) { - - var listener = context[i]; - - if (listener.element === element) { - - listenersOnElement.push(listener); - - } - - } - - return listenersOnElement; - - }; - - var byType = function (eventType, context) { - - var listenersWithType = []; - - context = context || allListeners; - - for (var i = 0; i < context.length; i++) { - - var listener = context[i]; - - if (listener.type === eventType) { - - listenersWithType.push(listener); - - } - - } - - return listenersWithType; - - }; - - var byHandler = function (handler, context) { - - var listenersWithHandler = []; - - context = context || allListeners; - - for (var i = 0; i < context.length; i++) { - - var listener = context[i]; - - if (listener.handler === handler) { - - listenersWithHandler.push(listener); - - } - - } - - return listenersWithHandler; - - }; - - var one = function (element, eventType, handler) { - - var result = allListeners; - - if (element) - result = byElement(element, result); - - if (eventType) - result = byType(eventType, result); - - if (handler) - result = byHandler(handler, result); - - return result[0]; - - }; - - var all = function (element, eventType, handler) { - - var result = allListeners; - - if (element) - result = byElement(element, result); - - if (eventType) - result = byType(eventType, result); - - if (handler) - result = byHandler(handler, result); - - return result; - - }; - - return { - byElement : byElement, - byType : byType, - byHandler : byHandler, - one : one, - all : all - }; - - }(); - - listeners.add = function (element, eventType, handler, isCapture) { - - element.addEventListener(eventType, handler, isCapture); - - var data = { - element: element, - type: eventType, - handler: handler - }; - - var alreadyAddedListener = listeners.search.one(element, eventType, handler); - - if (!alreadyAddedListener) { - - allListeners.push(data); - - } - - }; - - listeners.remove = function (element, eventType, handler) { - - element.removeEventListener(eventType, handler); - - var existingListeners = listeners.search.all(element, eventType, handler); - - for (var i = 0; i < existingListeners.length; i++) { - - var index = allListeners.indexOf(existingListeners[i]); - - if (index > 0) { - - allListeners.splice(index, 1); - - } - - } - - }; - - listeners.removeAll = function () { - - allListeners.map(function (current) { - - listeners.remove(current.element, current.type, current.handler); - - }); - - }; - - listeners.get = function (element, eventType, handler) { - - return listeners.search.all(element, eventType, handler); - - }; - - return listeners; - -}({}); \ No newline at end of file diff --git a/modules/notifications.js b/modules/notifications.js deleted file mode 100644 index ffeda9b8..00000000 --- a/modules/notifications.js +++ /dev/null @@ -1,231 +0,0 @@ -/** - * Codex Editor Notification Module - * - * @author Codex Team - * @version 1.0 - */ - -module.exports = (function (notifications) { - - let editor = codex.editor; - - var queue = []; - - var addToQueue = function (settings) { - - queue.push(settings); - - var index = 0; - - while ( index < queue.length && queue.length > 5) { - - if (queue[index].type == 'confirm' || queue[index].type == 'prompt') { - - index++; - continue; - - } - - queue[index].close(); - queue.splice(index, 1); - - } - - }; - - notifications.createHolder = function () { - - var holder = editor.draw.node('DIV', 'cdx-notifications-block'); - - editor.nodes.notifications = document.body.appendChild(holder); - - return holder; - - }; - - - /** - * Error notificator. Shows block with message - * @protected - */ - notifications.errorThrown = function (errorMsg, event) { - - editor.notifications.notification({message: 'This action is not available currently', type: event.type}); - - }; - - /** - * - * Appends notification - * - * settings = { - * type - notification type (reserved types: alert, confirm, prompt). Just add class 'cdx-notification-'+type - * message - notification message - * okMsg - confirm button text (default - 'Ok') - * cancelBtn - cancel button text (default - 'Cancel'). Only for confirm and prompt types - * confirm - function-handler for ok button click - * cancel - function-handler for cancel button click. Only for confirm and prompt types - * time - time (in seconds) after which notification will close (default - 10s) - * } - * - * @param settings - */ - notifications.notification = function (constructorSettings) { - - /** Private vars and methods */ - var notification = null, - cancel = null, - type = null, - confirm = null, - inputField = null; - - var confirmHandler = function () { - - close(); - - if (typeof confirm !== 'function' ) { - - return; - - } - - if (type == 'prompt') { - - confirm(inputField.value); - return; - - } - - confirm(); - - }; - - var cancelHandler = function () { - - close(); - - if (typeof cancel !== 'function' ) { - - return; - - } - - cancel(); - - }; - - - /** Public methods */ - function create(settings) { - - if (!(settings && settings.message)) { - - editor.core.log('Can\'t create notification. Message is missed'); - return; - - } - - settings.type = settings.type || 'alert'; - settings.time = settings.time*1000 || 10000; - - var wrapper = editor.draw.node('DIV', 'cdx-notification'), - message = editor.draw.node('DIV', 'cdx-notification__message'), - input = editor.draw.node('INPUT', 'cdx-notification__input'), - okBtn = editor.draw.node('SPAN', 'cdx-notification__ok-btn'), - cancelBtn = editor.draw.node('SPAN', 'cdx-notification__cancel-btn'); - - message.textContent = settings.message; - okBtn.textContent = settings.okMsg || 'ОК'; - cancelBtn.textContent = settings.cancelMsg || 'Отмена'; - - editor.listeners.add(okBtn, 'click', confirmHandler); - editor.listeners.add(cancelBtn, 'click', cancelHandler); - - wrapper.appendChild(message); - - if (settings.type == 'prompt') { - - wrapper.appendChild(input); - - } - - wrapper.appendChild(okBtn); - - if (settings.type == 'prompt' || settings.type == 'confirm') { - - wrapper.appendChild(cancelBtn); - - } - - wrapper.classList.add('cdx-notification-' + settings.type); - wrapper.dataset.type = settings.type; - - notification = wrapper; - type = settings.type; - confirm = settings.confirm; - cancel = settings.cancel; - inputField = input; - - if (settings.type != 'prompt' && settings.type != 'confirm') { - - window.setTimeout(close, settings.time); - - } - - }; - - /** - * Show notification block - */ - function send() { - - editor.nodes.notifications.appendChild(notification); - inputField.focus(); - - editor.nodes.notifications.classList.add('cdx-notification__notification-appending'); - - window.setTimeout(function () { - - editor.nodes.notifications.classList.remove('cdx-notification__notification-appending'); - - }, 100); - - addToQueue({type: type, close: close}); - - }; - - /** - * Remove notification block - */ - function close() { - - notification.remove(); - - }; - - - if (constructorSettings) { - - create(constructorSettings); - send(); - - } - - return { - create: create, - send: send, - close: close - }; - - }; - - notifications.clear = function () { - - editor.nodes.notifications.innerHTML = ''; - queue = []; - - }; - - return notifications; - -})({}); \ No newline at end of file diff --git a/modules/parser.js b/modules/parser.js deleted file mode 100644 index 70debc9c..00000000 --- a/modules/parser.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Codex Editor Parser Module - * - * @author Codex Team - * @version 1.1 - */ - -module.exports = (function (parser) { - - let editor = codex.editor; - - /** inserting text */ - parser.insertPastedContent = function (blockType, tag) { - - editor.content.insertBlock({ - type : blockType.type, - block : blockType.render({ - text : tag.innerHTML - }) - }); - - }; - - /** - * Check DOM node for display style: separated block or child-view - */ - parser.isFirstLevelBlock = function (node) { - - return node.nodeType == editor.core.nodeTypes.TAG && - node.classList.contains(editor.ui.className.BLOCK_CLASSNAME); - - }; - - return parser; - -})({}); diff --git a/modules/paste.js b/modules/paste.js deleted file mode 100644 index ae810185..00000000 --- a/modules/paste.js +++ /dev/null @@ -1,278 +0,0 @@ -/** - * Codex Editor Paste module - * - * @author Codex Team - * @version 1.1.1 - */ - -module.exports = function (paste) { - - let editor = codex.editor; - - var patterns = []; - - paste.prepare = function () { - - var tools = editor.tools; - - for (var tool in tools) { - - if (!tools[tool].renderOnPastePatterns || !Array.isArray(tools[tool].renderOnPastePatterns)) { - - continue; - - } - - tools[tool].renderOnPastePatterns.map(function (pattern) { - - - patterns.push(pattern); - - }); - - } - - return Promise.resolve(); - - }; - - /** - * Saves data - * @param event - */ - paste.pasted = function (event) { - - var clipBoardData = event.clipboardData || window.clipboardData, - content = clipBoardData.getData('Text'); - - var result = analize(content); - - if (result) { - - event.preventDefault(); - event.stopImmediatePropagation(); - - } - - return result; - - }; - - /** - * Analizes pated string and calls necessary method - */ - - var analize = function (string) { - - var result = false, - content = editor.content.currentNode, - plugin = content.dataset.tool; - - patterns.map( function (pattern) { - - var execArray = pattern.regex.exec(string), - match = execArray && execArray[0]; - - if ( match && match === string.trim()) { - - /** current block is not empty */ - if ( content.textContent.trim() && plugin == editor.settings.initialBlockPlugin ) { - - pasteToNewBlock_(); - - } - - pattern.callback(string, pattern); - result = true; - - } - - }); - - return result; - - }; - - var pasteToNewBlock_ = function () { - - /** Create new initial block */ - editor.content.insertBlock({ - - type : editor.settings.initialBlockPlugin, - block : editor.tools[editor.settings.initialBlockPlugin].render({ - text : '' - }) - - }, false); - - }; - - /** - * This method prevents default behaviour. - * - * @param {Object} event - * @protected - * - * @description We get from clipboard pasted data, sanitize, make a fragment that contains of this sanitized nodes. - * Firstly, we need to memorize the caret position. We can do that by getting the range of selection. - * After all, we insert clear fragment into caret placed position. Then, we should move the caret to the last node - */ - paste.blockPasteCallback = function (event) { - - - if (!needsToHandlePasteEvent(event.target)) { - - return; - - } - - /** Prevent default behaviour */ - event.preventDefault(); - - /** get html pasted data - dirty data */ - var htmlData = event.clipboardData.getData('text/html'), - plainData = event.clipboardData.getData('text/plain'); - - /** Temporary DIV that is used to work with text's paragraphs as DOM-elements*/ - var paragraphs = editor.draw.node('DIV', '', {}), - cleanData, - wrappedData; - - /** Create fragment, that we paste to range after proccesing */ - cleanData = editor.sanitizer.clean(htmlData); - - /** - * We wrap pasted text with

tags to split it logically - * @type {string} - */ - wrappedData = editor.content.wrapTextWithParagraphs(cleanData, plainData); - paragraphs.innerHTML = wrappedData; - - /** - * If there only one paragraph, just insert in at the caret location - */ - if (paragraphs.childNodes.length == 1) { - - emulateUserAgentBehaviour(paragraphs.firstChild); - return; - - } - - insertPastedParagraphs(paragraphs.childNodes); - - }; - - /** - * Checks if we should handle paste event on block - * @param block - * - * @return {boolean} - */ - var needsToHandlePasteEvent = function (block) { - - /** If area is input or textarea then allow default behaviour */ - if ( editor.core.isNativeInput(block) ) { - - return false; - - } - - var editableParent = editor.content.getEditableParent(block); - - /** Allow paste when event target placed in Editable element */ - if (!editableParent) { - - return false; - - } - - return true; - - }; - - /** - * Inserts new initial plugin blocks with data in paragraphs - * - * @param {Array} paragraphs - array of paragraphs (

) whit content, that should be inserted - */ - var insertPastedParagraphs = function (paragraphs) { - - var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin, - currentNode = editor.content.currentNode; - - - paragraphs.forEach(function (paragraph) { - - /** Don't allow empty paragraphs */ - if (editor.core.isBlockEmpty(paragraph)) { - - return; - - } - - editor.content.insertBlock({ - type : NEW_BLOCK_TYPE, - block : editor.tools[NEW_BLOCK_TYPE].render({ - text : paragraph.innerHTML - }) - }); - - editor.caret.inputIndex++; - - }); - - editor.caret.setToPreviousBlock(editor.caret.getCurrentInputIndex() + 1); - - - /** - * If there was no data in working node, remove it - */ - if (editor.core.isBlockEmpty(currentNode)) { - - currentNode.remove(); - editor.ui.saveInputs(); - - } - - - }; - - /** - * Inserts node content at the caret position - * - * @param {Node} node - DOM node (could be DocumentFragment), that should be inserted at the caret location - */ - var emulateUserAgentBehaviour = function (node) { - - var newNode; - - if (node.childElementCount) { - - newNode = document.createDocumentFragment(); - - node.childNodes.forEach(function (current) { - - if (!editor.core.isDomNode(current) && current.data.trim() === '') { - - return; - - } - - newNode.appendChild(current.cloneNode(true)); - - }); - - } else { - - newNode = document.createTextNode(node.textContent); - - } - - editor.caret.insertNode(newNode); - - }; - - - return paste; - -}({}); \ No newline at end of file diff --git a/modules/renderer.js b/modules/renderer.js deleted file mode 100644 index 1704755b..00000000 --- a/modules/renderer.js +++ /dev/null @@ -1,203 +0,0 @@ -/** - * Codex Editor Renderer Module - * - * @author Codex Team - * @version 1.0 - */ - -module.exports = (function (renderer) { - - let editor = codex.editor; - - /** - * Asyncronously parses input JSON to redactor blocks - */ - renderer.makeBlocksFromData = function () { - - /** - * If redactor is empty, add first paragraph to start writing - */ - if (editor.core.isEmpty(editor.state.blocks) || !editor.state.blocks.items.length) { - - editor.ui.addInitialBlock(); - return; - - } - - Promise.resolve() - - /** First, get JSON from state */ - .then(function () { - - return editor.state.blocks; - - }) - - /** Then, start to iterate they */ - .then(editor.renderer.appendBlocks) - - /** Write log if something goes wrong */ - .catch(function (error) { - - editor.core.log('Error while parsing JSON: %o', 'error', error); - - }); - - }; - - /** - * Parses JSON to blocks - * @param {object} data - * @return Primise -> nodeList - */ - renderer.appendBlocks = function (data) { - - var blocks = data.items; - - /** - * Sequence of one-by-one blocks appending - * Uses to save blocks order after async-handler - */ - var nodeSequence = Promise.resolve(); - - for (var index = 0; index < blocks.length ; index++ ) { - - /** Add node to sequence at specified index */ - editor.renderer.appendNodeAtIndex(nodeSequence, blocks, index); - - } - - }; - - /** - * Append node at specified index - */ - renderer.appendNodeAtIndex = function (nodeSequence, blocks, index) { - - /** We need to append node to sequence */ - nodeSequence - - /** first, get node async-aware */ - .then(function () { - - return editor.renderer.getNodeAsync(blocks, index); - - }) - - /** - * second, compose editor-block from JSON object - */ - .then(editor.renderer.createBlockFromData) - - /** - * now insert block to redactor - */ - .then(function (blockData) { - - /** - * blockData has 'block', 'type' and 'stretched' information - */ - editor.content.insertBlock(blockData); - - /** Pass created block to next step */ - return blockData.block; - - }) - - /** Log if something wrong with node */ - .catch(function (error) { - - editor.core.log('Node skipped while parsing because %o', 'error', error); - - }); - - }; - - /** - * Asynchronously returns block data from blocksList by index - * @return Promise to node - */ - renderer.getNodeAsync = function (blocksList, index) { - - return Promise.resolve().then(function () { - - return { - tool : blocksList[index], - position : index - }; - - }); - - }; - - /** - * Creates editor block by JSON-data - * - * @uses render method of each plugin - * - * @param {Object} toolData.tool - * { header : { - * text: '', - * type: 'H3', ... - * } - * } - * @param {Number} toolData.position - index in input-blocks array - * @return {Object} with type and Element - */ - renderer.createBlockFromData = function ( toolData ) { - - /** New parser */ - var block, - tool = toolData.tool, - pluginName = tool.type; - - /** Get first key of object that stores plugin name */ - // for (var pluginName in blockData) break; - - /** Check for plugin existance */ - if (!editor.tools[pluginName]) { - - throw Error(`Plugin «${pluginName}» not found`); - - } - - /** Check for plugin having render method */ - if (typeof editor.tools[pluginName].render != 'function') { - - throw Error(`Plugin «${pluginName}» must have «render» method`); - - } - - if ( editor.tools[pluginName].available === false ) { - - block = editor.draw.unavailableBlock(); - - block.innerHTML = editor.tools[pluginName].loadingMessage; - - /** - * Saver will extract data from initial block data by position in array - */ - block.dataset.inputPosition = toolData.position; - - } else { - - /** New Parser */ - block = editor.tools[pluginName].render(tool.data); - - } - - /** is first-level block stretched */ - var stretched = editor.tools[pluginName].isStretched || false; - - /** Retrun type and block */ - return { - type : pluginName, - block : block, - stretched : stretched - }; - - }; - - return renderer; - -})({}); \ No newline at end of file diff --git a/modules/sanitizer.js b/modules/sanitizer.js deleted file mode 100644 index fe7ed7fc..00000000 --- a/modules/sanitizer.js +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Codex Sanitizer - */ - -module.exports = (function (sanitizer) { - - /** HTML Janitor library */ - let janitor = require('html-janitor'); - - /** Codex Editor */ - let editor = codex.editor; - - sanitizer.prepare = function () { - - if (editor.settings.sanitizer && !editor.core.isEmpty(editor.settings.sanitizer)) { - - Config.CUSTOM = editor.settings.sanitizer; - - } - - }; - - /** - * Basic config - */ - var Config = { - - /** User configuration */ - CUSTOM : null, - - BASIC : { - - tags: { - p: {}, - a: { - href: true, - target: '_blank', - rel: 'nofollow' - } - } - } - }; - - sanitizer.Config = Config; - - /** - * - * @param userCustomConfig - * @returns {*} - * @private - * - * @description If developer uses editor's API, then he can customize sane restrictions. - * Or, sane config can be defined globally in editors initialization. That config will be used everywhere - * At least, if there is no config overrides, that API uses BASIC Default configation - */ - let init_ = function (userCustomConfig) { - - let configuration = userCustomConfig || Config.CUSTOM || Config.BASIC; - - return new janitor(configuration); - - }; - - /** - * Cleans string from unwanted tags - * @protected - * @param {String} dirtyString - taint string - * @param {Object} customConfig - allowed tags - */ - sanitizer.clean = function (dirtyString, customConfig) { - - let janitorInstance = init_(customConfig); - - return janitorInstance.clean(dirtyString); - - }; - - return sanitizer; - -})({}); \ No newline at end of file diff --git a/modules/saver.js b/modules/saver.js deleted file mode 100644 index d3358931..00000000 --- a/modules/saver.js +++ /dev/null @@ -1,165 +0,0 @@ -/** - * Codex Editor Saver - * - * @author Codex Team - * @version 1.1.0 - */ - -module.exports = (function (saver) { - - let editor = codex.editor; - - /** - * @public - * Save blocks - */ - saver.save = function () { - - /** Save html content of redactor to memory */ - editor.state.html = editor.nodes.redactor.innerHTML; - - /** Clean jsonOutput state */ - editor.state.jsonOutput = []; - - return saveBlocks(editor.nodes.redactor.childNodes); - - }; - - /** - * @private - * Save each block data - * - * @param blocks - * @returns {Promise.} - */ - let saveBlocks = function (blocks) { - - let data = []; - - for(let index = 0; index < blocks.length; index++) { - - data.push(getBlockData(blocks[index])); - - } - - return Promise.all(data) - .then(makeOutput) - .catch(editor.core.log); - - }; - - /** Save and validate block data */ - let getBlockData = function (block) { - - return saveBlockData(block) - .then(validateBlockData) - .catch(editor.core.log); - - }; - - /** - * @private - * Call block`s plugin save method and return saved data - * - * @param block - * @returns {Object} - */ - let saveBlockData = function (block) { - - let pluginName = block.dataset.tool; - - /** Check for plugin existence */ - if (!editor.tools[pluginName]) { - - editor.core.log(`Plugin «${pluginName}» not found`, 'error'); - return {data: null, pluginName: null}; - - } - - /** Check for plugin having save method */ - if (typeof editor.tools[pluginName].save !== 'function') { - - editor.core.log(`Plugin «${pluginName}» must have save method`, 'error'); - return {data: null, pluginName: null}; - - } - - /** Result saver */ - let blockContent = block.childNodes[0], - pluginsContent = blockContent.childNodes[0], - position = pluginsContent.dataset.inputPosition; - - /** If plugin wasn't available then return data from cache */ - if ( editor.tools[pluginName].available === false ) { - - return Promise.resolve({data: codex.editor.state.blocks.items[position].data, pluginName}); - - } - - return Promise.resolve(pluginsContent) - .then(editor.tools[pluginName].save) - .then(data => Object({data, pluginName})); - - }; - - /** - * Call plugin`s validate method. Return false if validation failed - * - * @param data - * @param pluginName - * @returns {Object|Boolean} - */ - let validateBlockData = function ({data, pluginName}) { - - if (!data || !pluginName) { - - return false; - - } - - if (editor.tools[pluginName].validate) { - - let result = editor.tools[pluginName].validate(data); - - /** - * Do not allow invalid data - */ - if (!result) { - - return false; - - } - - } - - return {data, pluginName}; - - - }; - - /** - * Compile article output - * - * @param savedData - * @returns {{time: number, version, items: (*|Array)}} - */ - let makeOutput = function (savedData) { - - savedData = savedData.filter(blockData => blockData); - - let items = savedData.map(blockData => Object({type: blockData.pluginName, data: blockData.data})); - - editor.state.jsonOutput = items; - - return { - id: editor.state.blocks.id || null, - time: +new Date(), - version: editor.version, - items - }; - - }; - - return saver; - -})({}); diff --git a/modules/toolbar/inline.js b/modules/toolbar/inline.js deleted file mode 100644 index 36e50024..00000000 --- a/modules/toolbar/inline.js +++ /dev/null @@ -1,599 +0,0 @@ -/** - * Inline toolbar - * - * Contains from tools: - * Bold, Italic, Underline and Anchor - * - * @author Codex Team - * @version 1.0 - */ - -module.exports = (function (inline) { - - let editor = codex.editor; - - inline.buttonsOpened = null; - inline.actionsOpened = null; - inline.wrappersOffset = null; - - /** - * saving selection that need for execCommand for styling - * - */ - inline.storedSelection = null; - - /** - * @protected - * - * Open inline toobar - */ - inline.show = function () { - - var currentNode = editor.content.currentNode, - tool = currentNode.dataset.tool, - plugin; - - /** - * tool allowed to open inline toolbar - */ - plugin = editor.tools[tool]; - - if (!plugin.showInlineToolbar) - return; - - var selectedText = inline.getSelectionText(), - toolbar = editor.nodes.inlineToolbar.wrapper; - - if (selectedText.length > 0) { - - /** Move toolbar and open */ - editor.toolbar.inline.move(); - - /** Open inline toolbar */ - toolbar.classList.add('opened'); - - /** show buttons of inline toolbar */ - editor.toolbar.inline.showButtons(); - - } - - }; - - /** - * @protected - * - * Closes inline toolbar - */ - inline.close = function () { - - var toolbar = editor.nodes.inlineToolbar.wrapper; - - toolbar.classList.remove('opened'); - - }; - - /** - * @private - * - * Moving toolbar - */ - inline.move = function () { - - if (!this.wrappersOffset) { - - this.wrappersOffset = this.getWrappersOffset(); - - } - - var coords = this.getSelectionCoords(), - defaultOffset = 0, - toolbar = editor.nodes.inlineToolbar.wrapper, - newCoordinateX, - newCoordinateY; - - if (!coords) { - - return; - - } - - if (toolbar.offsetHeight === 0) { - - defaultOffset = 40; - - } - - newCoordinateX = coords.x - this.wrappersOffset.left; - newCoordinateY = coords.y + window.scrollY - this.wrappersOffset.top - defaultOffset - toolbar.offsetHeight; - - toolbar.style.transform = `translate3D(${Math.floor(newCoordinateX)}px, ${Math.floor(newCoordinateY)}px, 0)`; - - /** Close everything */ - editor.toolbar.inline.closeButtons(); - editor.toolbar.inline.closeAction(); - - }; - - /** - * @private - * - * Tool Clicked - */ - - inline.toolClicked = function (event, type) { - - /** - * For simple tools we use default browser function - * For more complicated tools, we should write our own behavior - */ - switch (type) { - case 'createLink' : editor.toolbar.inline.createLinkAction(event, type); break; - default : editor.toolbar.inline.defaultToolAction(type); break; - } - - /** - * highlight buttons - * after making some action - */ - editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight); - - }; - - /** - * @private - * - * Saving wrappers offset in DOM - */ - inline.getWrappersOffset = function () { - - var wrapper = editor.nodes.wrapper, - offset = this.getOffset(wrapper); - - this.wrappersOffset = offset; - return offset; - - }; - - /** - * @private - * - * Calculates offset of DOM element - * - * @param el - * @returns {{top: number, left: number}} - */ - inline.getOffset = function ( el ) { - - var _x = 0; - var _y = 0; - - while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) { - - _x += (el.offsetLeft + el.clientLeft); - _y += (el.offsetTop + el.clientTop); - el = el.offsetParent; - - } - return { top: _y, left: _x }; - - }; - - /** - * @private - * - * Calculates position of selected text - * @returns {{x: number, y: number}} - */ - inline.getSelectionCoords = function () { - - var sel = document.selection, range; - var x = 0, y = 0; - - if (sel) { - - if (sel.type != 'Control') { - - range = sel.createRange(); - range.collapse(true); - x = range.boundingLeft; - y = range.boundingTop; - - } - - } else if (window.getSelection) { - - sel = window.getSelection(); - - if (sel.rangeCount) { - - range = sel.getRangeAt(0).cloneRange(); - if (range.getClientRects) { - - range.collapse(true); - var rect = range.getClientRects()[0]; - - if (!rect) { - - return; - - } - - x = rect.left; - y = rect.top; - - } - - } - - } - return { x: x, y: y }; - - }; - - /** - * @private - * - * Returns selected text as String - * @returns {string} - */ - inline.getSelectionText = function () { - - var selectedText = ''; - - // all modern browsers and IE9+ - if (window.getSelection) { - - selectedText = window.getSelection().toString(); - - } - - return selectedText; - - }; - - /** Opens buttons block */ - inline.showButtons = function () { - - var buttons = editor.nodes.inlineToolbar.buttons; - - buttons.classList.add('opened'); - - editor.toolbar.inline.buttonsOpened = true; - - /** highlight buttons */ - editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight); - - }; - - /** Makes buttons disappear */ - inline.closeButtons = function () { - - var buttons = editor.nodes.inlineToolbar.buttons; - - buttons.classList.remove('opened'); - - editor.toolbar.inline.buttonsOpened = false; - - }; - - /** Open buttons defined action if exist */ - inline.showActions = function () { - - var action = editor.nodes.inlineToolbar.actions; - - action.classList.add('opened'); - - editor.toolbar.inline.actionsOpened = true; - - }; - - /** Close actions block */ - inline.closeAction = function () { - - var action = editor.nodes.inlineToolbar.actions; - - action.innerHTML = ''; - action.classList.remove('opened'); - editor.toolbar.inline.actionsOpened = false; - - }; - - - /** - * Callback for keydowns in inline toolbar "Insert link..." input - */ - let inlineToolbarAnchorInputKeydown_ = function (event) { - - if (event.keyCode != editor.core.keys.ENTER) { - - return; - - } - - let editable = editor.content.currentNode, - storedSelection = editor.toolbar.inline.storedSelection; - - editor.toolbar.inline.restoreSelection(editable, storedSelection); - editor.toolbar.inline.setAnchor(this.value); - - /** - * Preventing events that will be able to happen - */ - event.preventDefault(); - event.stopImmediatePropagation(); - - editor.toolbar.inline.clearRange(); - - }; - - /** Action for link creation or for setting anchor */ - inline.createLinkAction = function (event) { - - var isActive = this.isLinkActive(); - - var editable = editor.content.currentNode, - storedSelection = editor.toolbar.inline.saveSelection(editable); - - /** Save globally selection */ - editor.toolbar.inline.storedSelection = storedSelection; - - if (isActive) { - - - /** - * Changing stored selection. if we want to remove anchor from word - * we should remove anchor from whole word, not only selected part. - * The solution is than we get the length of current link - * Change start position to - end of selection minus length of anchor - */ - editor.toolbar.inline.restoreSelection(editable, storedSelection); - - editor.toolbar.inline.defaultToolAction('unlink'); - - } else { - - /** Create input and close buttons */ - var action = editor.draw.inputForLink(); - - editor.nodes.inlineToolbar.actions.appendChild(action); - - editor.toolbar.inline.closeButtons(); - editor.toolbar.inline.showActions(); - - /** - * focus to input - * Solution: https://developer.mozilla.org/ru/docs/Web/API/HTMLElement/focus - * Prevents event after showing input and when we need to focus an input which is in unexisted form - */ - action.focus(); - event.preventDefault(); - - /** Callback to link action */ - editor.listeners.add(action, 'keydown', inlineToolbarAnchorInputKeydown_, false); - - } - - }; - - inline.isLinkActive = function () { - - var isActive = false; - - editor.nodes.inlineToolbar.buttons.childNodes.forEach(function (tool) { - - var dataType = tool.dataset.type; - - if (dataType == 'link' && tool.classList.contains('hightlighted')) { - - isActive = true; - - } - - }); - - return isActive; - - }; - - /** default action behavior of tool */ - inline.defaultToolAction = function (type) { - - document.execCommand(type, false, null); - - }; - - /** - * @private - * - * Sets URL - * - * @param {String} url - URL - */ - inline.setAnchor = function (url) { - - document.execCommand('createLink', false, url); - - /** Close after URL inserting */ - editor.toolbar.inline.closeAction(); - - }; - - /** - * @private - * - * Saves selection - */ - inline.saveSelection = function (containerEl) { - - var range = window.getSelection().getRangeAt(0), - preSelectionRange = range.cloneRange(), - start; - - preSelectionRange.selectNodeContents(containerEl); - preSelectionRange.setEnd(range.startContainer, range.startOffset); - - start = preSelectionRange.toString().length; - - return { - start: start, - end: start + range.toString().length - }; - - }; - - /** - * @private - * - * Sets to previous selection (Range) - * - * @param {Element} containerEl - editable element where we restore range - * @param {Object} savedSel - range basic information to restore - */ - inline.restoreSelection = function (containerEl, savedSel) { - - var range = document.createRange(), - charIndex = 0; - - range.setStart(containerEl, 0); - range.collapse(true); - - var nodeStack = [ containerEl ], - node, - foundStart = false, - stop = false, - nextCharIndex; - - while (!stop && (node = nodeStack.pop())) { - - if (node.nodeType == 3) { - - nextCharIndex = charIndex + node.length; - - if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) { - - range.setStart(node, savedSel.start - charIndex); - foundStart = true; - - } - if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) { - - range.setEnd(node, savedSel.end - charIndex); - stop = true; - - } - charIndex = nextCharIndex; - - } else { - - var i = node.childNodes.length; - - while (i--) { - - nodeStack.push(node.childNodes[i]); - - } - - } - - } - - var sel = window.getSelection(); - - sel.removeAllRanges(); - sel.addRange(range); - - }; - - /** - * @private - * - * Removes all ranges from window selection - */ - inline.clearRange = function () { - - var selection = window.getSelection(); - - selection.removeAllRanges(); - - }; - - /** - * @private - * - * sets or removes hightlight - */ - inline.hightlight = function (tool) { - - var dataType = tool.dataset.type; - - if (document.queryCommandState(dataType)) { - - editor.toolbar.inline.setButtonHighlighted(tool); - - } else { - - editor.toolbar.inline.removeButtonsHighLight(tool); - - } - - /** - * - * hightlight for anchors - */ - var selection = window.getSelection(), - tag = selection.anchorNode.parentNode; - - if (tag.tagName == 'A' && dataType == 'link') { - - editor.toolbar.inline.setButtonHighlighted(tool); - - } - - }; - - /** - * @private - * - * Mark button if text is already executed - */ - inline.setButtonHighlighted = function (button) { - - button.classList.add('hightlighted'); - - /** At link tool we also change icon */ - if (button.dataset.type == 'link') { - - var icon = button.childNodes[0]; - - icon.classList.remove('ce-icon-link'); - icon.classList.add('ce-icon-unlink'); - - } - - }; - - /** - * @private - * - * Removes hightlight - */ - inline.removeButtonsHighLight = function (button) { - - button.classList.remove('hightlighted'); - - /** At link tool we also change icon */ - if (button.dataset.type == 'link') { - - var icon = button.childNodes[0]; - - icon.classList.remove('ce-icon-unlink'); - icon.classList.add('ce-icon-link'); - - } - - }; - - - return inline; - -})({}); diff --git a/modules/toolbar/settings.js b/modules/toolbar/settings.js deleted file mode 100644 index 307c719d..00000000 --- a/modules/toolbar/settings.js +++ /dev/null @@ -1,172 +0,0 @@ -/** - * Toolbar settings - * - * @version 1.0.5 - */ - -module.exports = (function (settings) { - - let editor = codex.editor; - - settings.opened = false; - - settings.setting = null; - settings.actions = null; - - /** - * Append and open settings - */ - settings.open = function (toolType) { - - /** - * Append settings content - * It's stored in tool.settings - */ - if ( !editor.tools[toolType] || !editor.tools[toolType].makeSettings ) { - - return; - - } - - /** - * Draw settings block - */ - var settingsBlock = editor.tools[toolType].makeSettings(); - - editor.nodes.pluginSettings.appendChild(settingsBlock); - - - /** Open settings block */ - editor.nodes.blockSettings.classList.add('opened'); - this.opened = true; - - }; - - /** - * Close and clear settings - */ - settings.close = function () { - - editor.nodes.blockSettings.classList.remove('opened'); - editor.nodes.pluginSettings.innerHTML = ''; - - this.opened = false; - - }; - - /** - * @param {string} toolType - plugin type - */ - settings.toggle = function ( toolType ) { - - if ( !this.opened ) { - - this.open(toolType); - - } else { - - this.close(); - - } - - }; - - /** - * Here we will draw buttons and add listeners to components - */ - settings.makeRemoveBlockButton = function () { - - var removeBlockWrapper = editor.draw.node('SPAN', 'ce-toolbar__remove-btn', {}), - settingButton = editor.draw.node('SPAN', 'ce-toolbar__remove-setting', { innerHTML : '' }), - actionWrapper = editor.draw.node('DIV', 'ce-toolbar__remove-confirmation', {}), - confirmAction = editor.draw.node('DIV', 'ce-toolbar__remove-confirm', { textContent : 'Удалить блок' }), - cancelAction = editor.draw.node('DIV', 'ce-toolbar__remove-cancel', { textContent : 'Отмена' }); - - editor.listeners.add(settingButton, 'click', editor.toolbar.settings.removeButtonClicked, false); - - editor.listeners.add(confirmAction, 'click', editor.toolbar.settings.confirmRemovingRequest, false); - - editor.listeners.add(cancelAction, 'click', editor.toolbar.settings.cancelRemovingRequest, false); - - actionWrapper.appendChild(confirmAction); - actionWrapper.appendChild(cancelAction); - - removeBlockWrapper.appendChild(settingButton); - removeBlockWrapper.appendChild(actionWrapper); - - /** Save setting */ - editor.toolbar.settings.setting = settingButton; - editor.toolbar.settings.actions = actionWrapper; - - return removeBlockWrapper; - - }; - - settings.removeButtonClicked = function () { - - var action = editor.toolbar.settings.actions; - - if (action.classList.contains('opened')) { - - editor.toolbar.settings.hideRemoveActions(); - - } else { - - editor.toolbar.settings.showRemoveActions(); - - } - - editor.toolbar.toolbox.close(); - editor.toolbar.settings.close(); - - }; - - settings.cancelRemovingRequest = function () { - - editor.toolbar.settings.actions.classList.remove('opened'); - - }; - - settings.confirmRemovingRequest = function () { - - var currentBlock = editor.content.currentNode, - firstLevelBlocksCount; - - currentBlock.remove(); - - firstLevelBlocksCount = editor.nodes.redactor.childNodes.length; - - /** - * If all blocks are removed - */ - if (firstLevelBlocksCount === 0) { - - /** update currentNode variable */ - editor.content.currentNode = null; - - /** Inserting new empty initial block */ - editor.ui.addInitialBlock(); - - } - - editor.ui.saveInputs(); - - editor.toolbar.close(); - - }; - - settings.showRemoveActions = function () { - - editor.toolbar.settings.actions.classList.add('opened'); - - }; - - settings.hideRemoveActions = function () { - - editor.toolbar.settings.actions.classList.remove('opened'); - - }; - - return settings; - -})({}); diff --git a/modules/toolbar/toolbar.js b/modules/toolbar/toolbar.js deleted file mode 100644 index e59b7d68..00000000 --- a/modules/toolbar/toolbar.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Codex Editor toolbar module - * - * Contains: - * - Inline toolbox - * - Toolbox within plus button - * - Settings section - * - * @author Codex Team - * @version 1.0 - */ - -module.exports = (function (toolbar) { - - let editor = codex.editor; - - toolbar.settings = require('./settings'); - toolbar.inline = require('./inline'); - toolbar.toolbox = require('./toolbox'); - - /** - * Margin between focused node and toolbar - */ - toolbar.defaultToolbarHeight = 49; - - toolbar.defaultOffset = 34; - - toolbar.opened = false; - - toolbar.current = null; - - /** - * @protected - */ - toolbar.open = function () { - - if (editor.hideToolbar) { - - return; - - } - - let toolType = editor.content.currentNode.dataset.tool; - - if (!editor.tools[toolType] || !editor.tools[toolType].makeSettings ) { - - editor.nodes.showSettingsButton.classList.add('hide'); - - } else { - - editor.nodes.showSettingsButton.classList.remove('hide'); - - } - - editor.nodes.toolbar.classList.add('opened'); - this.opened = true; - - }; - - /** - * @protected - */ - toolbar.close = function () { - - editor.nodes.toolbar.classList.remove('opened'); - - toolbar.opened = false; - toolbar.current = null; - - for (var button in editor.nodes.toolbarButtons) { - - editor.nodes.toolbarButtons[button].classList.remove('selected'); - - } - - /** Close toolbox when toolbar is not displayed */ - editor.toolbar.toolbox.close(); - editor.toolbar.settings.close(); - - }; - - toolbar.toggle = function () { - - if ( !this.opened ) { - - this.open(); - - } else { - - this.close(); - - } - - }; - - toolbar.hidePlusButton = function () { - - editor.nodes.plusButton.classList.add('hide'); - - }; - - toolbar.showPlusButton = function () { - - editor.nodes.plusButton.classList.remove('hide'); - - }; - - /** - * Moving toolbar to the specified node - */ - toolbar.move = function () { - - /** Close Toolbox when we move toolbar */ - editor.toolbar.toolbox.close(); - - if (!editor.content.currentNode) { - - return; - - } - - var newYCoordinate = editor.content.currentNode.offsetTop - (editor.toolbar.defaultToolbarHeight / 2) + editor.toolbar.defaultOffset; - - editor.nodes.toolbar.style.transform = `translate3D(0, ${Math.floor(newYCoordinate)}px, 0)`; - - /** Close trash actions */ - editor.toolbar.settings.hideRemoveActions(); - - }; - - return toolbar; - -})({}); diff --git a/modules/toolbar/toolbox.js b/modules/toolbar/toolbox.js deleted file mode 100644 index cec3e980..00000000 --- a/modules/toolbar/toolbox.js +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Codex Editor toolbox - * - * All tools be able to appended here - * - * @author Codex Team - * @version 1.0 - */ - -module.exports = (function (toolbox) { - - let editor = codex.editor; - - toolbox.opened = false; - toolbox.openedOnBlock = null; - - /** Shows toolbox */ - toolbox.open = function () { - - /** Close setting if toolbox is opened */ - if (editor.toolbar.settings.opened) { - - editor.toolbar.settings.close(); - - } - - /** Add 'toolbar-opened' class for current block **/ - toolbox.openedOnBlock = editor.content.currentNode; - toolbox.openedOnBlock.classList.add('toolbar-opened'); - - /** display toolbox */ - editor.nodes.toolbox.classList.add('opened'); - - /** Animate plus button */ - editor.nodes.plusButton.classList.add('clicked'); - - /** toolbox state */ - editor.toolbar.toolbox.opened = true; - - }; - - /** Closes toolbox */ - toolbox.close = function () { - - /** Remove 'toolbar-opened' class from current block **/ - if (toolbox.openedOnBlock) toolbox.openedOnBlock.classList.remove('toolbar-opened'); - toolbox.openedOnBlock = null; - - /** Makes toolbox disappear */ - editor.nodes.toolbox.classList.remove('opened'); - - /** Rotate plus button */ - editor.nodes.plusButton.classList.remove('clicked'); - - /** toolbox state */ - editor.toolbar.toolbox.opened = false; - - editor.toolbar.current = null; - - }; - - toolbox.leaf = function () { - - let currentTool = editor.toolbar.current, - tools = Object.keys(editor.tools), - barButtons = editor.nodes.toolbarButtons, - nextToolIndex = 0, - toolToSelect, - visibleTool, - tool; - - if ( !currentTool ) { - - /** Get first tool from object*/ - for(tool in editor.tools) { - - if (editor.tools[tool].displayInToolbox) { - - break; - - } - - nextToolIndex ++; - - } - - } else { - - nextToolIndex = (tools.indexOf(currentTool) + 1) % tools.length; - visibleTool = tools[nextToolIndex]; - - while (!editor.tools[visibleTool].displayInToolbox) { - - nextToolIndex = (nextToolIndex + 1) % tools.length; - visibleTool = tools[nextToolIndex]; - - } - - } - - toolToSelect = tools[nextToolIndex]; - - for ( var button in barButtons ) { - - barButtons[button].classList.remove('selected'); - - } - - barButtons[toolToSelect].classList.add('selected'); - editor.toolbar.current = toolToSelect; - - }; - - /** - * Transforming selected node type into selected toolbar element type - * @param {event} event - */ - toolbox.toolClicked = function (event) { - - /** - * UNREPLACEBLE_TOOLS this types of tools are forbidden to replace even they are empty - */ - var UNREPLACEBLE_TOOLS = ['image', 'link', 'list', 'instagram', 'twitter', 'embed'], - tool = editor.tools[editor.toolbar.current], - workingNode = editor.content.currentNode, - currentInputIndex = editor.caret.inputIndex, - newBlockContent, - appendCallback, - blockData; - - /** Make block from plugin */ - newBlockContent = tool.render(); - - /** information about block */ - blockData = { - block : newBlockContent, - type : tool.type, - stretched : false - }; - - if ( - workingNode && - UNREPLACEBLE_TOOLS.indexOf(workingNode.dataset.tool) === -1 && - workingNode.textContent.trim() === '' - ) { - - /** Replace current block */ - editor.content.switchBlock(workingNode, newBlockContent, tool.type); - - } else { - - /** Insert new Block from plugin */ - editor.content.insertBlock(blockData); - - /** increase input index */ - currentInputIndex++; - - } - - /** Fire tool append callback */ - appendCallback = tool.appendCallback; - - if (appendCallback && typeof appendCallback == 'function') { - - appendCallback.call(event); - - } - - window.setTimeout(function () { - - /** Set caret to current block */ - editor.caret.setToBlock(currentInputIndex); - - }, 10); - - - /** - * Changing current Node - */ - editor.content.workingNodeChanged(); - - /** - * Move toolbar when node is changed - */ - editor.toolbar.move(); - - }; - - return toolbox; - -})({}); \ No newline at end of file diff --git a/modules/tools.js b/modules/tools.js deleted file mode 100644 index 044e0674..00000000 --- a/modules/tools.js +++ /dev/null @@ -1,153 +0,0 @@ -/** -* Module working with plugins -*/ -module.exports = (function () { - - let editor = codex.editor; - - /** - * Initialize plugins before using - * Ex. Load scripts or call some internal methods - * @return Promise - */ - function prepare() { - - return new Promise(function (resolve_, reject_) { - - Promise.resolve() - - /** - * Compose a sequence of plugins that requires preparation - */ - .then(function () { - - let pluginsRequiresPreparation = [], - allPlugins = editor.tools; - - for ( let pluginName in allPlugins ) { - - let plugin = allPlugins[pluginName]; - - if (plugin.prepare && typeof plugin.prepare != 'function' || !plugin.prepare) { - - continue; - - } - - pluginsRequiresPreparation.push(plugin); - - } - - /** - * If no one passed plugins requires preparation, finish prepare() and go ahead - */ - if (!pluginsRequiresPreparation.length) { - - resolve_(); - - } - - return pluginsRequiresPreparation; - - }) - - /** Wait plugins while they prepares */ - .then(waitAllPluginsPreparation_) - - .then(function () { - - editor.core.log('Plugins loaded', 'info'); - resolve_(); - - }).catch(function (error) { - - reject_(error); - - }); - - }); - - } - - /** - * @param {array} plugins - list of tools that requires preparation - * @return {Promise} resolved while all plugins will be ready or failed - */ - function waitAllPluginsPreparation_(plugins) { - - /** - * @calls allPluginsProcessed__ when all plugins prepared or failed - */ - return new Promise (function (allPluginsProcessed__) { - - /** - * pluck each element from queue - * First, send resolved Promise as previous value - * Each plugins "prepare" method returns a Promise, that's why - * reduce current element will not be able to continue while can't get - * a resolved Promise - * - * If last plugin is "prepared" then go to the next stage of initialization - */ - plugins.reduce(function (previousValue, plugin, iteration) { - - return previousValue.then(function () { - - /** - * Wait till plugins prepared - * @calls pluginIsReady__ when plugin is ready or failed - */ - return new Promise ( function (pluginIsReady__) { - - callPluginsPrepareMethod_( plugin ) - - .then( pluginIsReady__ ) - .then( function () { - - plugin.available = true; - - }) - - .catch(function (error) { - - editor.core.log(`Plugin «${plugin.type}» was not loaded. Preparation failed because %o`, 'warn', error); - plugin.available = false; - plugin.loadingMessage = error; - - /** Go ahead even some plugin has problems */ - pluginIsReady__(); - - }) - - .then(function () { - - /** If last plugin has problems then just ignore and continue */ - if (iteration == plugins.length - 1) { - - allPluginsProcessed__(); - - } - - }); - - }); - - }); - - }, Promise.resolve() ); - - }); - - } - - var callPluginsPrepareMethod_ = function (plugin) { - - return plugin.prepare( plugin.config || {} ); - - }; - - return { - prepare: prepare - }; - -}()); \ No newline at end of file diff --git a/modules/transport.js b/modules/transport.js deleted file mode 100644 index 7f659e37..00000000 --- a/modules/transport.js +++ /dev/null @@ -1,136 +0,0 @@ -/** - * - * Codex.Editor Transport Module - * - * @copyright 2017 Codex-Team - * @version 1.2.0 - */ - -module.exports = (function (transport) { - - let editor = codex.editor; - - - /** - * @private {Object} current XmlHttpRequest instance - */ - var currentRequest = null; - - - /** - * @type {null} | {DOMElement} input - keeps input element in memory - */ - transport.input = null; - - /** - * @property {Object} arguments - keep plugin settings and defined callbacks - */ - transport.arguments = null; - - /** - * Prepares input element where will be files - */ - transport.prepare = function () { - - let input = editor.draw.node( 'INPUT', '', { type : 'file' } ); - - editor.listeners.add(input, 'change', editor.transport.fileSelected); - editor.transport.input = input; - - }; - - /** Clear input when files is uploaded */ - transport.clearInput = function () { - - /** Remove old input */ - transport.input = null; - - /** Prepare new one */ - transport.prepare(); - - }; - - /** - * Callback for file selection - * @param {Event} event - */ - transport.fileSelected = function () { - - var input = this, - i, - files = input.files, - formData = new FormData(); - - if (editor.transport.arguments.multiple === true) { - - for ( i = 0; i < files.length; i++) { - - formData.append('files[]', files[i], files[i].name); - - } - - } else { - - formData.append('files', files[0], files[0].name); - - } - - currentRequest = editor.core.ajax({ - type : 'POST', - data : formData, - url : editor.transport.arguments.url, - beforeSend : editor.transport.arguments.beforeSend, - success : editor.transport.arguments.success, - error : editor.transport.arguments.error, - progress : editor.transport.arguments.progress - }); - - /** Clear input */ - transport.clearInput(); - - }; - - /** - * Use plugin callbacks - * @protected - * - * @param {Object} args - can have : - * @param {String} args.url - fetch URL - * @param {Function} args.beforeSend - function calls before sending ajax - * @param {Function} args.success - success callback - * @param {Function} args.error - on error handler - * @param {Function} args.progress - xhr onprogress handler - * @param {Boolean} args.multiple - allow select several files - * @param {String} args.accept - adds accept attribute - */ - transport.selectAndUpload = function (args) { - - transport.arguments = args; - - if ( args.multiple === true) { - - transport.input.setAttribute('multiple', 'multiple'); - - } - - if ( args.accept ) { - - transport.input.setAttribute('accept', args.accept); - - } - - transport.input.click(); - - }; - - transport.abort = function () { - - currentRequest.abort(); - - currentRequest = null; - - }; - - return transport; - -})({}); \ No newline at end of file diff --git a/modules/ui.js b/modules/ui.js deleted file mode 100644 index 736e8f1d..00000000 --- a/modules/ui.js +++ /dev/null @@ -1,431 +0,0 @@ -/** - * Codex Editor UI module - * - * @author Codex Team - * @version 1.2.0 - */ - -module.exports = (function (ui) { - - let editor = codex.editor; - - /** - * Basic editor classnames - */ - ui.className = { - - /** - * @const {string} BLOCK_CLASSNAME - redactor blocks name - */ - BLOCK_CLASSNAME : 'ce-block', - - /** - * @const {String} wrapper for plugins content - */ - BLOCK_CONTENT : 'ce-block__content', - - /** - * @const {String} BLOCK_STRETCHED - makes block stretched - */ - BLOCK_STRETCHED : 'ce-block--stretched', - - /** - * @const {String} BLOCK_HIGHLIGHTED - adds background - */ - BLOCK_HIGHLIGHTED : 'ce-block--focused', - - /** - * @const {String} - for all default settings - */ - SETTINGS_ITEM : 'ce-settings__item' - - }; - - /** - * @protected - * - * Making main interface - */ - ui.prepare = function () { - - return new Promise(function (resolve) { - - let wrapper = editor.draw.wrapper(), - redactor = editor.draw.redactor(), - toolbar = makeToolBar_(); - - wrapper.appendChild(toolbar); - wrapper.appendChild(redactor); - - /** Save created ui-elements to static nodes state */ - editor.nodes.wrapper = wrapper; - editor.nodes.redactor = redactor; - - /** Append editor wrapper with redactor zone into holder */ - editor.nodes.holder.appendChild(wrapper); - - resolve(); - - }) - - /** Add toolbox tools */ - .then(addTools_) - - /** Make container for inline toolbar */ - .then(makeInlineToolbar_) - - /** Add inline toolbar tools */ - .then(addInlineToolbarTools_) - - /** Draw wrapper for notifications */ - .then(makeNotificationHolder_) - - /** Add eventlisteners to redactor elements */ - .then(bindEvents_) - - .catch( function () { - - editor.core.log("Can't draw editor interface"); - - }); - - }; - - /** - * @private - * Draws inline toolbar zone - */ - var makeInlineToolbar_ = function () { - - var container = editor.draw.inlineToolbar(); - - /** Append to redactor new inline block */ - editor.nodes.inlineToolbar.wrapper = container; - - /** Draw toolbar buttons */ - editor.nodes.inlineToolbar.buttons = editor.draw.inlineToolbarButtons(); - - /** Buttons action or settings */ - editor.nodes.inlineToolbar.actions = editor.draw.inlineToolbarActions(); - - /** Append to inline toolbar buttons as part of it */ - editor.nodes.inlineToolbar.wrapper.appendChild(editor.nodes.inlineToolbar.buttons); - editor.nodes.inlineToolbar.wrapper.appendChild(editor.nodes.inlineToolbar.actions); - - editor.nodes.wrapper.appendChild(editor.nodes.inlineToolbar.wrapper); - - }; - - var makeToolBar_ = function () { - - let toolbar = editor.draw.toolbar(), - blockButtons = makeToolbarSettings_(), - toolbarContent = makeToolbarContent_(); - - /** Appending first-level block buttons */ - toolbar.appendChild(blockButtons); - - /** Append toolbarContent to toolbar */ - toolbar.appendChild(toolbarContent); - - /** Make toolbar global */ - editor.nodes.toolbar = toolbar; - - return toolbar; - - }; - - var makeToolbarContent_ = function () { - - let toolbarContent = editor.draw.toolbarContent(), - toolbox = editor.draw.toolbox(), - plusButton = editor.draw.plusButton(); - - /** Append plus button */ - toolbarContent.appendChild(plusButton); - - /** Appending toolbar tools */ - toolbarContent.appendChild(toolbox); - - /** Make Toolbox and plusButton global */ - editor.nodes.toolbox = toolbox; - editor.nodes.plusButton = plusButton; - - return toolbarContent; - - }; - - var makeToolbarSettings_ = function () { - - let blockSettings = editor.draw.blockSettings(), - blockButtons = editor.draw.blockButtons(), - defaultSettings = editor.draw.defaultSettings(), - showSettingsButton = editor.draw.settingsButton(), - showTrashButton = editor.toolbar.settings.makeRemoveBlockButton(), - pluginSettings = editor.draw.pluginsSettings(); - - /** Add default and plugins settings */ - blockSettings.appendChild(pluginSettings); - blockSettings.appendChild(defaultSettings); - - /** - * Make blocks buttons - * This block contains settings button and remove block button - */ - blockButtons.appendChild(showSettingsButton); - blockButtons.appendChild(showTrashButton); - blockButtons.appendChild(blockSettings); - - /** Make BlockSettings, PluginSettings, DefaultSettings global */ - editor.nodes.blockSettings = blockSettings; - editor.nodes.pluginSettings = pluginSettings; - editor.nodes.defaultSettings = defaultSettings; - editor.nodes.showSettingsButton = showSettingsButton; - editor.nodes.showTrashButton = showTrashButton; - - return blockButtons; - - }; - - /** Draw notifications holder */ - var makeNotificationHolder_ = function () { - - /** Append block with notifications to the document */ - editor.nodes.notifications = editor.notifications.createHolder(); - - }; - - /** - * @private - * Append tools passed in editor.tools - */ - var addTools_ = function () { - - var tool, - toolName, - toolButton; - - for ( toolName in editor.settings.tools ) { - - tool = editor.settings.tools[toolName]; - - editor.tools[toolName] = tool; - - if (!tool.iconClassname && tool.displayInToolbox) { - - editor.core.log('Toolbar icon classname missed. Tool %o skipped', 'warn', toolName); - continue; - - } - - if (typeof tool.render != 'function') { - - editor.core.log('render method missed. Tool %o skipped', 'warn', toolName); - continue; - - } - - if (!tool.displayInToolbox) { - - continue; - - } else { - - /** if tools is for toolbox */ - toolButton = editor.draw.toolbarButton(toolName, tool.iconClassname); - - editor.nodes.toolbox.appendChild(toolButton); - - editor.nodes.toolbarButtons[toolName] = toolButton; - - } - - } - - }; - - var addInlineToolbarTools_ = function () { - - var tools = { - - bold: { - icon : 'ce-icon-bold', - command : 'bold' - }, - - italic: { - icon : 'ce-icon-italic', - command : 'italic' - }, - - link: { - icon : 'ce-icon-link', - command : 'createLink' - } - }; - - var toolButton, - tool; - - for(var name in tools) { - - tool = tools[name]; - - toolButton = editor.draw.toolbarButtonInline(name, tool.icon); - - editor.nodes.inlineToolbar.buttons.appendChild(toolButton); - /** - * Add callbacks to this buttons - */ - editor.ui.setInlineToolbarButtonBehaviour(toolButton, tool.command); - - } - - }; - - /** - * @private - * Bind editor UI events - */ - var bindEvents_ = function () { - - editor.core.log('ui.bindEvents fired', 'info'); - - // window.addEventListener('error', function (errorMsg, url, lineNumber) { - // editor.notifications.errorThrown(errorMsg, event); - // }, false ); - - /** All keydowns on Document */ - editor.listeners.add(document, 'keydown', editor.callback.globalKeydown, false); - - /** All keydowns on Redactor zone */ - editor.listeners.add(editor.nodes.redactor, 'keydown', editor.callback.redactorKeyDown, false); - - /** All keydowns on Document */ - editor.listeners.add(document, 'keyup', editor.callback.globalKeyup, false ); - - /** - * Mouse click to radactor - */ - editor.listeners.add(editor.nodes.redactor, 'click', editor.callback.redactorClicked, false ); - - /** - * Clicks to the Plus button - */ - editor.listeners.add(editor.nodes.plusButton, 'click', editor.callback.plusButtonClicked, false); - - /** - * Clicks to SETTINGS button in toolbar - */ - editor.listeners.add(editor.nodes.showSettingsButton, 'click', editor.callback.showSettingsButtonClicked, false ); - - /** Bind click listeners on toolbar buttons */ - for (var button in editor.nodes.toolbarButtons) { - - editor.listeners.add(editor.nodes.toolbarButtons[button], 'click', editor.callback.toolbarButtonClicked, false); - - } - - }; - - ui.addBlockHandlers = function (block) { - - if (!block) return; - - /** - * Block keydowns - */ - editor.listeners.add(block, 'keydown', editor.callback.blockKeydown, false); - - /** - * Pasting content from another source - * We have two type of sanitization - * First - uses deep-first search algorithm to get sub nodes, - * sanitizes whole Block_content and replaces cleared nodes - * This method is deprecated - * Method is used in editor.callback.blockPaste(event) - * - * Secont - uses Mutation observer. - * Observer "observe" DOM changes and send changings to callback. - * Callback gets changed node, not whole Block_content. - * Inserted or changed node, which we've gotten have been cleared and replaced with diry node - * - * Method is used in editor.callback.blockPasteViaSanitize(event) - * - * @uses html-janitor - * @example editor.callback.blockPasteViaSanitize(event), the second method. - * - */ - editor.listeners.add(block, 'paste', editor.paste.blockPasteCallback, false); - - /** - * Show inline toolbar for selected text - */ - editor.listeners.add(block, 'mouseup', editor.toolbar.inline.show, false); - editor.listeners.add(block, 'keyup', editor.toolbar.inline.show, false); - - }; - - /** getting all contenteditable elements */ - ui.saveInputs = function () { - - var redactor = editor.nodes.redactor; - - editor.state.inputs = []; - - /** Save all inputs in global variable state */ - var inputs = redactor.querySelectorAll('[contenteditable], input, textarea'); - - Array.prototype.map.call(inputs, function (current) { - - if (!current.type || current.type == 'text' || current.type == 'textarea') { - - editor.state.inputs.push(current); - - } - - }); - - }; - - /** - * Adds first initial block on empty redactor - */ - ui.addInitialBlock = function () { - - var initialBlockType = editor.settings.initialBlockPlugin, - initialBlock; - - if ( !editor.tools[initialBlockType] ) { - - editor.core.log('Plugin %o was not implemented and can\'t be used as initial block', 'warn', initialBlockType); - return; - - } - - initialBlock = editor.tools[initialBlockType].render(); - - initialBlock.setAttribute('data-placeholder', editor.settings.placeholder); - - editor.content.insertBlock({ - type : initialBlockType, - block : initialBlock - }); - - editor.content.workingNodeChanged(initialBlock); - - }; - - ui.setInlineToolbarButtonBehaviour = function (button, type) { - - editor.listeners.add(button, 'mousedown', function (event) { - - editor.toolbar.inline.toolClicked(event, type); - - }, false); - - }; - - return ui; - -})({}); diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f5d6410d..00000000 --- a/package-lock.json +++ /dev/null @@ -1,5018 +0,0 @@ -{ - "name": "codex.editor", - "version": "1.7.8", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "acorn": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.1.2.tgz", - "integrity": "sha512-o96FZLJBPY1lvTuJylGA9Bk3t/GKPPJG8H0ydQQl01crzwJgspa4AEIq/pVTXigmK0PHVQhiAtn8WMBLL9D2WA==", - "dev": true - }, - "acorn-jsx": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-3.0.1.tgz", - "integrity": "sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s=", - "dev": true, - "requires": { - "acorn": "3.3.0" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - } - } - }, - "ajv": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-4.11.8.tgz", - "integrity": "sha1-gv+wKynmYq5TvcIK8VlHcGc5xTY=", - "dev": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ajv-keywords": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-1.5.1.tgz", - "integrity": "sha1-MU3QpLM2j609/NxU7eYXG4htrzw=", - "dev": true - }, - "align-text": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/align-text/-/align-text-0.1.4.tgz", - "integrity": "sha1-DNkKVhCT810KmSVsIrcGlDP60Rc=", - "dev": true, - "requires": { - "kind-of": "3.2.2", - "longest": "1.0.1", - "repeat-string": "1.6.1" - } - }, - "alphanum-sort": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/alphanum-sort/-/alphanum-sort-1.0.2.tgz", - "integrity": "sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM=", - "dev": true - }, - "amdefine": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", - "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", - "dev": true - }, - "ansi-escapes": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-1.4.0.tgz", - "integrity": "sha1-06ioOzGapneTZisT52HHkRQiMG4=", - "dev": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true - }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", - "dev": true - }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", - "dev": true, - "requires": { - "micromatch": "2.3.11", - "normalize-path": "2.1.1" - } - }, - "argparse": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.9.tgz", - "integrity": "sha1-c9g7wmP4bpf4zE9rrhsOkKfSLIY=", - "dev": true, - "requires": { - "sprintf-js": "1.0.3" - } - }, - "arr-diff": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", - "dev": true, - "requires": { - "arr-flatten": "1.1.0" - } - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, - "requires": { - "array-uniq": "1.0.3" - } - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", - "dev": true - }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", - "dev": true - }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, - "requires": { - "util": "0.10.3" - } - }, - "async": { - "version": "1.5.2", - "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", - "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=", - "dev": true - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "dev": true - }, - "autoprefixer": { - "version": "6.7.7", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-6.7.7.tgz", - "integrity": "sha1-Hb0cg1ZY41zj+ZhAmdsAWFx4IBQ=", - "dev": true, - "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000746", - "normalize-range": "0.1.2", - "num2fraction": "1.2.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "esutils": "2.0.2", - "js-tokens": "3.0.2" - } - }, - "babel-core": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.0.tgz", - "integrity": "sha1-rzL3izGm/O8RnIew/Y2XU/A6C7g=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-generator": "6.26.0", - "babel-helpers": "6.24.1", - "babel-messages": "6.23.0", - "babel-register": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "convert-source-map": "1.5.0", - "debug": "2.6.9", - "json5": "0.5.1", - "lodash": "4.17.4", - "minimatch": "3.0.4", - "path-is-absolute": "1.0.1", - "private": "0.1.7", - "slash": "1.0.0", - "source-map": "0.5.7" - } - }, - "babel-generator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.0.tgz", - "integrity": "sha1-rBriAHC3n248odMmlhMFN3TyDcU=", - "dev": true, - "requires": { - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "detect-indent": "4.0.0", - "jsesc": "1.3.0", - "lodash": "4.17.4", - "source-map": "0.5.7", - "trim-right": "1.0.1" - } - }, - "babel-helper-call-delegate": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", - "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-define-map": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", - "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.4" - } - }, - "babel-helper-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", - "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", - "dev": true, - "requires": { - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-get-function-arity": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", - "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-hoist-variables": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", - "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-optimise-call-expression": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", - "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helper-regex": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", - "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.4" - } - }, - "babel-helper-replace-supers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", - "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", - "dev": true, - "requires": { - "babel-helper-optimise-call-expression": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-helpers": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", - "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-loader": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-6.4.1.tgz", - "integrity": "sha1-CzQRLVsHSKjc2/Uaz2+b1C1QuMo=", - "dev": true, - "requires": { - "find-cache-dir": "0.1.1", - "loader-utils": "0.2.17", - "mkdirp": "0.5.1", - "object-assign": "4.1.1" - } - }, - "babel-messages": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", - "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-check-es2015-constants": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", - "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-arrow-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", - "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-block-scoped-functions": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", - "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-block-scoping": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", - "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "lodash": "4.17.4" - } - }, - "babel-plugin-transform-es2015-classes": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", - "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", - "dev": true, - "requires": { - "babel-helper-define-map": "6.26.0", - "babel-helper-function-name": "6.24.1", - "babel-helper-optimise-call-expression": "6.24.1", - "babel-helper-replace-supers": "6.24.1", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-computed-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", - "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-destructuring": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", - "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-duplicate-keys": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", - "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-for-of": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", - "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-function-name": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", - "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", - "dev": true, - "requires": { - "babel-helper-function-name": "6.24.1", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", - "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-amd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", - "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-commonjs": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz", - "integrity": "sha1-DYOUApt9xqvhqX7xgeAHWN0uXYo=", - "dev": true, - "requires": { - "babel-plugin-transform-strict-mode": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-systemjs": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", - "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", - "dev": true, - "requires": { - "babel-helper-hoist-variables": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-modules-umd": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", - "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", - "dev": true, - "requires": { - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0" - } - }, - "babel-plugin-transform-es2015-object-super": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", - "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", - "dev": true, - "requires": { - "babel-helper-replace-supers": "6.24.1", - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-parameters": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", - "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", - "dev": true, - "requires": { - "babel-helper-call-delegate": "6.24.1", - "babel-helper-get-function-arity": "6.24.1", - "babel-runtime": "6.26.0", - "babel-template": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-shorthand-properties": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", - "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-spread": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", - "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-sticky-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", - "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", - "dev": true, - "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-plugin-transform-es2015-template-literals": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", - "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-typeof-symbol": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", - "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0" - } - }, - "babel-plugin-transform-es2015-unicode-regex": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", - "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", - "dev": true, - "requires": { - "babel-helper-regex": "6.26.0", - "babel-runtime": "6.26.0", - "regexpu-core": "2.0.0" - } - }, - "babel-plugin-transform-regenerator": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", - "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", - "dev": true, - "requires": { - "regenerator-transform": "0.10.1" - } - }, - "babel-plugin-transform-strict-mode": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", - "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0" - } - }, - "babel-polyfill": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz", - "integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "core-js": "2.5.1", - "regenerator-runtime": "0.10.5" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz", - "integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=", - "dev": true - } - } - }, - "babel-preset-es2015": { - "version": "6.24.1", - "resolved": "https://registry.npmjs.org/babel-preset-es2015/-/babel-preset-es2015-6.24.1.tgz", - "integrity": "sha1-1EBQ1rwsn+6nAqrzjXJ6AhBTiTk=", - "dev": true, - "requires": { - "babel-plugin-check-es2015-constants": "6.22.0", - "babel-plugin-transform-es2015-arrow-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoped-functions": "6.22.0", - "babel-plugin-transform-es2015-block-scoping": "6.26.0", - "babel-plugin-transform-es2015-classes": "6.24.1", - "babel-plugin-transform-es2015-computed-properties": "6.24.1", - "babel-plugin-transform-es2015-destructuring": "6.23.0", - "babel-plugin-transform-es2015-duplicate-keys": "6.24.1", - "babel-plugin-transform-es2015-for-of": "6.23.0", - "babel-plugin-transform-es2015-function-name": "6.24.1", - "babel-plugin-transform-es2015-literals": "6.22.0", - "babel-plugin-transform-es2015-modules-amd": "6.24.1", - "babel-plugin-transform-es2015-modules-commonjs": "6.26.0", - "babel-plugin-transform-es2015-modules-systemjs": "6.24.1", - "babel-plugin-transform-es2015-modules-umd": "6.24.1", - "babel-plugin-transform-es2015-object-super": "6.24.1", - "babel-plugin-transform-es2015-parameters": "6.24.1", - "babel-plugin-transform-es2015-shorthand-properties": "6.24.1", - "babel-plugin-transform-es2015-spread": "6.22.0", - "babel-plugin-transform-es2015-sticky-regex": "6.24.1", - "babel-plugin-transform-es2015-template-literals": "6.22.0", - "babel-plugin-transform-es2015-typeof-symbol": "6.23.0", - "babel-plugin-transform-es2015-unicode-regex": "6.24.1", - "babel-plugin-transform-regenerator": "6.26.0" - } - }, - "babel-register": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", - "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", - "dev": true, - "requires": { - "babel-core": "6.26.0", - "babel-runtime": "6.26.0", - "core-js": "2.5.1", - "home-or-tmp": "2.0.0", - "lodash": "4.17.4", - "mkdirp": "0.5.1", - "source-map-support": "0.4.18" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "2.5.1", - "regenerator-runtime": "0.11.0" - } - }, - "babel-template": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", - "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-traverse": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "lodash": "4.17.4" - } - }, - "babel-traverse": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", - "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "babel-messages": "6.23.0", - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "babylon": "6.18.0", - "debug": "2.6.9", - "globals": "9.18.0", - "invariant": "2.2.2", - "lodash": "4.17.4" - } - }, - "babel-types": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", - "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "esutils": "2.0.2", - "lodash": "4.17.4", - "to-fast-properties": "1.0.3" - } - }, - "babylon": { - "version": "6.18.0", - "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", - "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base64-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.2.1.tgz", - "integrity": "sha512-dwVUVIXsBZXwTuwnXI9RK8sBmgq09NDHzyR9SAph9eqk76gKK2JSQmZARC2zRC81JC2QTtxD0ARU5qTS25gIGw==", - "dev": true - }, - "big.js": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", - "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", - "dev": true - }, - "binary-extensions": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz", - "integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz", - "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=", - "dev": true, - "requires": { - "balanced-match": "1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", - "dev": true, - "requires": { - "expand-range": "1.8.2", - "preserve": "0.2.0", - "repeat-element": "1.1.2" - } - }, - "browserify-aes": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-0.4.0.tgz", - "integrity": "sha1-BnFJtmjfMcS1hTPgLQHoBthgjiw=", - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "browserify-zlib": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.1.4.tgz", - "integrity": "sha1-uzX4pRn2AOD6a4SFJByXnQFB+y0=", - "dev": true, - "requires": { - "pako": "0.2.9" - } - }, - "browserslist": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-1.7.7.tgz", - "integrity": "sha1-C9dnBCWL6CmyOYu1Dkti0aFmsLk=", - "dev": true, - "requires": { - "caniuse-db": "1.0.30000746", - "electron-to-chromium": "1.3.26" - } - }, - "buffer": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", - "integrity": "sha1-bRu2AbB6TvztlwlBMgkwJ8lbwpg=", - "dev": true, - "requires": { - "base64-js": "1.2.1", - "ieee754": "1.1.8", - "isarray": "1.0.0" - } - }, - "builtin-status-codes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", - "dev": true - }, - "caller-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", - "integrity": "sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8=", - "dev": true, - "requires": { - "callsites": "0.2.0" - } - }, - "callsites": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-0.2.0.tgz", - "integrity": "sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo=", - "dev": true - }, - "camelcase": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", - "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", - "dev": true - }, - "caniuse-api": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-1.6.1.tgz", - "integrity": "sha1-tTTnxzTE+B7F++isoq0kNUuWLGw=", - "dev": true, - "requires": { - "browserslist": "1.7.7", - "caniuse-db": "1.0.30000746", - "lodash.memoize": "4.1.2", - "lodash.uniq": "4.5.0" - } - }, - "caniuse-db": { - "version": "1.0.30000746", - "resolved": "https://registry.npmjs.org/caniuse-db/-/caniuse-db-1.0.30000746.tgz", - "integrity": "sha1-UBCYxm9fu/Y0wC8lUIsF6ICZEPQ=", - "dev": true - }, - "center-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/center-align/-/center-align-0.1.3.tgz", - "integrity": "sha1-qg0yYptu6XIgBBHL1EYckHvCt60=", - "dev": true, - "requires": { - "align-text": "0.1.4", - "lazy-cache": "1.0.4" - } - }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", - "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" - } - }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", - "dev": true, - "requires": { - "anymatch": "1.3.2", - "async-each": "1.0.1", - "fsevents": "1.1.2", - "glob-parent": "2.0.0", - "inherits": "2.0.3", - "is-binary-path": "1.0.1", - "is-glob": "2.0.1", - "path-is-absolute": "1.0.1", - "readdirp": "2.1.0" - } - }, - "circular-json": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", - "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", - "dev": true - }, - "clap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/clap/-/clap-1.2.3.tgz", - "integrity": "sha512-4CoL/A3hf90V3VIEjeuhSvlGFEHKzOz+Wfc2IVZc+FaUgU0ZQafJTP49fvnULipOPcAfqhyI2duwQyns6xqjYA==", - "dev": true, - "requires": { - "chalk": "1.1.3" - } - }, - "cli-cursor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-1.0.2.tgz", - "integrity": "sha1-ZNo/fValRBLll5S9Ytw1KV6PKYc=", - "dev": true, - "requires": { - "restore-cursor": "1.0.1" - } - }, - "cli-width": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", - "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", - "dev": true - }, - "cliui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-2.1.0.tgz", - "integrity": "sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE=", - "dev": true, - "requires": { - "center-align": "0.1.3", - "right-align": "0.1.3", - "wordwrap": "0.0.2" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", - "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", - "dev": true - } - } - }, - "clone": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.2.tgz", - "integrity": "sha1-Jgt6meux7f4kdTgXX3gyQ8sZ0Uk=", - "dev": true - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "coa": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/coa/-/coa-1.0.4.tgz", - "integrity": "sha1-qe8VNmDWqGqL3sAomlxoTSF0Mv0=", - "dev": true, - "requires": { - "q": "1.5.0" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true - }, - "color": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/color/-/color-0.11.4.tgz", - "integrity": "sha1-bXtcdPtl6EHNSHkq0e1eB7kE12Q=", - "dev": true, - "requires": { - "clone": "1.0.2", - "color-convert": "1.9.0", - "color-string": "0.3.0" - } - }, - "color-convert": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz", - "integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "color-string": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.3.0.tgz", - "integrity": "sha1-J9RvtnAlxcL6JZk7+/V55HhBuZE=", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "colormin": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colormin/-/colormin-1.1.2.tgz", - "integrity": "sha1-6i90IKcrlogaOKrlnsEkpvcpgTM=", - "dev": true, - "requires": { - "color": "0.11.4", - "css-color-names": "0.0.4", - "has": "1.0.1" - } - }, - "colors": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz", - "integrity": "sha1-FopHAXVran9RoSzgyXv6KMCE7WM=", - "dev": true - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "concat-stream": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.0.tgz", - "integrity": "sha1-CqxmL9Ur54lk1VMvaUeE5wEQrPc=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "typedarray": "0.0.6" - } - }, - "console-browserify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", - "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", - "dev": true, - "requires": { - "date-now": "0.1.4" - } - }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true - }, - "convert-source-map": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.0.tgz", - "integrity": "sha1-ms1whRxtXf3ZPZKC5e35SgP/RrU=", - "dev": true - }, - "core-js": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz", - "integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "crypto-browserify": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.3.0.tgz", - "integrity": "sha1-ufx1u0oO1h3PHNXa6W6zDJw+UGw=", - "dev": true, - "requires": { - "browserify-aes": "0.4.0", - "pbkdf2-compat": "2.0.1", - "ripemd160": "0.2.0", - "sha.js": "2.2.6" - } - }, - "css-color-names": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", - "integrity": "sha1-gIrcLnnPhHOAabZGyyDsJ762KeA=", - "dev": true - }, - "css-loader": { - "version": "0.26.4", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-0.26.4.tgz", - "integrity": "sha1-th6eMNuUMD5v/IkvEOzQmtAlof0=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "css-selector-tokenizer": "0.7.0", - "cssnano": "3.10.0", - "loader-utils": "1.1.0", - "lodash.camelcase": "4.3.0", - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-modules-extract-imports": "1.1.0", - "postcss-modules-local-by-default": "1.2.0", - "postcss-modules-scope": "1.1.0", - "postcss-modules-values": "1.3.0", - "source-list-map": "0.1.8" - }, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - } - } - }, - "css-selector-tokenizer": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz", - "integrity": "sha1-5piEdK6MlTR3v15+/s/OzNnPTIY=", - "dev": true, - "requires": { - "cssesc": "0.1.0", - "fastparse": "1.1.1", - "regexpu-core": "1.0.0" - }, - "dependencies": { - "regexpu-core": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-1.0.0.tgz", - "integrity": "sha1-hqdj9Y7k18L2sQLkdkBQ3n7ZDGs=", - "dev": true, - "requires": { - "regenerate": "1.3.3", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" - } - } - } - }, - "cssesc": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-0.1.0.tgz", - "integrity": "sha1-yBSQPkViM3GgR3tAEJqq++6t27Q=", - "dev": true - }, - "cssnano": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/cssnano/-/cssnano-3.10.0.tgz", - "integrity": "sha1-Tzj2zqK5sX+gFJDyPx3GjqZcHDg=", - "dev": true, - "requires": { - "autoprefixer": "6.7.7", - "decamelize": "1.2.0", - "defined": "1.0.0", - "has": "1.0.1", - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-calc": "5.3.1", - "postcss-colormin": "2.2.2", - "postcss-convert-values": "2.6.1", - "postcss-discard-comments": "2.0.4", - "postcss-discard-duplicates": "2.1.0", - "postcss-discard-empty": "2.1.0", - "postcss-discard-overridden": "0.1.1", - "postcss-discard-unused": "2.2.3", - "postcss-filter-plugins": "2.0.2", - "postcss-merge-idents": "2.1.7", - "postcss-merge-longhand": "2.0.2", - "postcss-merge-rules": "2.1.2", - "postcss-minify-font-values": "1.0.5", - "postcss-minify-gradients": "1.0.5", - "postcss-minify-params": "1.2.2", - "postcss-minify-selectors": "2.1.1", - "postcss-normalize-charset": "1.1.1", - "postcss-normalize-url": "3.0.8", - "postcss-ordered-values": "2.2.3", - "postcss-reduce-idents": "2.4.0", - "postcss-reduce-initial": "1.0.1", - "postcss-reduce-transforms": "1.0.4", - "postcss-svgo": "2.1.6", - "postcss-unique-selectors": "2.0.2", - "postcss-value-parser": "3.3.0", - "postcss-zindex": "2.2.0" - } - }, - "csso": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/csso/-/csso-2.3.2.tgz", - "integrity": "sha1-3dUsWHAz9J6Utx/FVWnyUuj/X4U=", - "dev": true, - "requires": { - "clap": "1.2.3", - "source-map": "0.5.7" - } - }, - "d": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/d/-/d-1.0.0.tgz", - "integrity": "sha1-dUu1v+VUUdpppYuU1F9MWwRi1Y8=", - "dev": true, - "requires": { - "es5-ext": "0.10.31" - } - }, - "date-now": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", - "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", - "dev": true - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", - "dev": true - }, - "del": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", - "integrity": "sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag=", - "dev": true, - "requires": { - "globby": "5.0.0", - "is-path-cwd": "1.0.0", - "is-path-in-cwd": "1.0.0", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1", - "rimraf": "2.6.2" - } - }, - "detect-indent": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", - "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", - "dev": true, - "requires": { - "repeating": "2.0.1" - } - }, - "doctrine": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.0.0.tgz", - "integrity": "sha1-xz2NKQnSIpHhoAejlYBNqLZl/mM=", - "dev": true, - "requires": { - "esutils": "2.0.2", - "isarray": "1.0.0" - } - }, - "domain-browser": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.1.7.tgz", - "integrity": "sha1-hnqksJP6oF8d4IwG9NeyH9+GmLw=", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.26", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.26.tgz", - "integrity": "sha1-mWQnKUhhp02cfIK5Jg6jAejALWY=", - "dev": true - }, - "emojis-list": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-2.1.0.tgz", - "integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=", - "dev": true - }, - "enhanced-resolve": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-0.9.1.tgz", - "integrity": "sha1-TW5omzcl+GCQknzMhs2fFjW4ni4=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "memory-fs": "0.2.0", - "tapable": "0.1.10" - }, - "dependencies": { - "memory-fs": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.2.0.tgz", - "integrity": "sha1-8rslNovBIeORwlIN6Slpyu4KApA=", - "dev": true - } - } - }, - "errno": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.4.tgz", - "integrity": "sha1-uJbiOp5ei6M4cfyZar02NfyaHH0=", - "dev": true, - "requires": { - "prr": "0.0.0" - } - }, - "es5-ext": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.31.tgz", - "integrity": "sha1-e7k4yVp/G59ygJLcCcQe3MOY7v4=", - "dev": true, - "requires": { - "es6-iterator": "2.0.1", - "es6-symbol": "3.1.1" - } - }, - "es6-iterator": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.1.tgz", - "integrity": "sha1-jjGcnwRTv1ddN0lAplWSDlnKVRI=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.31", - "es6-symbol": "3.1.1" - } - }, - "es6-map": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-map/-/es6-map-0.1.5.tgz", - "integrity": "sha1-kTbgUD3MBqMBaQ8LsU/042TpSfA=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.31", - "es6-iterator": "2.0.1", - "es6-set": "0.1.5", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-set": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/es6-set/-/es6-set-0.1.5.tgz", - "integrity": "sha1-0rPsXU2ADO2BjbU40ol02wpzzLE=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.31", - "es6-iterator": "2.0.1", - "es6-symbol": "3.1.1", - "event-emitter": "0.3.5" - } - }, - "es6-symbol": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.1.tgz", - "integrity": "sha1-vwDvT9q2uhtG7Le2KbTH7VcVzHc=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.31" - } - }, - "es6-weak-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es6-weak-map/-/es6-weak-map-2.0.2.tgz", - "integrity": "sha1-XjqzIlH/0VOKH45f+hNXdy+S2W8=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.31", - "es6-iterator": "2.0.1", - "es6-symbol": "3.1.1" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escope": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/escope/-/escope-3.6.0.tgz", - "integrity": "sha1-4Bl16BJ4GhY6ba392AOY3GTIicM=", - "dev": true, - "requires": { - "es6-map": "0.1.5", - "es6-weak-map": "2.0.2", - "esrecurse": "4.2.0", - "estraverse": "4.2.0" - } - }, - "eslint": { - "version": "3.19.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-3.19.0.tgz", - "integrity": "sha1-yPxiAcf0DdCJQbh8CFdnOGpnmsw=", - "dev": true, - "requires": { - "babel-code-frame": "6.26.0", - "chalk": "1.1.3", - "concat-stream": "1.6.0", - "debug": "2.6.9", - "doctrine": "2.0.0", - "escope": "3.6.0", - "espree": "3.5.1", - "esquery": "1.0.0", - "estraverse": "4.2.0", - "esutils": "2.0.2", - "file-entry-cache": "2.0.0", - "glob": "7.1.2", - "globals": "9.18.0", - "ignore": "3.3.5", - "imurmurhash": "0.1.4", - "inquirer": "0.12.0", - "is-my-json-valid": "2.16.1", - "is-resolvable": "1.0.0", - "js-yaml": "3.7.0", - "json-stable-stringify": "1.0.1", - "levn": "0.3.0", - "lodash": "4.17.4", - "mkdirp": "0.5.1", - "natural-compare": "1.4.0", - "optionator": "0.8.2", - "path-is-inside": "1.0.2", - "pluralize": "1.2.1", - "progress": "1.1.8", - "require-uncached": "1.0.3", - "shelljs": "0.7.8", - "strip-bom": "3.0.0", - "strip-json-comments": "2.0.1", - "table": "3.8.3", - "text-table": "0.2.0", - "user-home": "2.0.0" - } - }, - "eslint-loader": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-1.9.0.tgz", - "integrity": "sha512-40aN976qSNPyb9ejTqjEthZITpls1SVKtwguahmH1dzGCwQU/vySE+xX33VZmD8csU0ahVNCtFlsPgKqRBiqgg==", - "dev": true, - "requires": { - "loader-fs-cache": "1.0.1", - "loader-utils": "1.1.0", - "object-assign": "4.1.1", - "object-hash": "1.2.0", - "rimraf": "2.6.2" - }, - "dependencies": { - "loader-utils": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.1.0.tgz", - "integrity": "sha1-yYrvSIvM7aL/teLeZG1qdUQp9c0=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1" - } - } - } - }, - "espree": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-3.5.1.tgz", - "integrity": "sha1-DJiLirRttTEAoZVK5LqZXd0n2H4=", - "dev": true, - "requires": { - "acorn": "5.1.2", - "acorn-jsx": "3.0.1" - } - }, - "esprima": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", - "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=", - "dev": true - }, - "esquery": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.0.tgz", - "integrity": "sha1-z7qLV9f7qT8XKYqKAGoEzaE9gPo=", - "dev": true, - "requires": { - "estraverse": "4.2.0" - } - }, - "esrecurse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.0.tgz", - "integrity": "sha1-+pVo2Y04I/mkHZHpAtyrnqblsWM=", - "dev": true, - "requires": { - "estraverse": "4.2.0", - "object-assign": "4.1.1" - } - }, - "estraverse": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "event-emitter": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz", - "integrity": "sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=", - "dev": true, - "requires": { - "d": "1.0.0", - "es5-ext": "0.10.31" - } - }, - "events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=", - "dev": true - }, - "exit-hook": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz", - "integrity": "sha1-8FyiM7SMBdVP/wd2XfhQfpXAL/g=", - "dev": true - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "0.1.1" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "2.2.3" - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "extract-text-webpack-plugin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/extract-text-webpack-plugin/-/extract-text-webpack-plugin-1.0.1.tgz", - "integrity": "sha1-yVvzy6rEnclvHcbgclSfu2VMzSw=", - "dev": true, - "requires": { - "async": "1.5.2", - "loader-utils": "0.2.17", - "webpack-sources": "0.1.5" - } - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastparse": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.1.tgz", - "integrity": "sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=", - "dev": true - }, - "figures": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz", - "integrity": "sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=", - "dev": true, - "requires": { - "escape-string-regexp": "1.0.5", - "object-assign": "4.1.1" - } - }, - "file-entry-cache": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz", - "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=", - "dev": true, - "requires": { - "flat-cache": "1.3.0", - "object-assign": "4.1.1" - } - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fill-range": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.3.tgz", - "integrity": "sha1-ULd9/X5Gm8dJJHCWNpn+eoSFpyM=", - "dev": true, - "requires": { - "is-number": "2.1.0", - "isobject": "2.1.0", - "randomatic": "1.1.7", - "repeat-element": "1.1.2", - "repeat-string": "1.6.1" - } - }, - "find-cache-dir": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-0.1.1.tgz", - "integrity": "sha1-yN765XyKUqinhPnjHFfHQumToLk=", - "dev": true, - "requires": { - "commondir": "1.0.1", - "mkdirp": "0.5.1", - "pkg-dir": "1.0.0" - } - }, - "find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8=", - "dev": true, - "requires": { - "path-exists": "2.1.0", - "pinkie-promise": "2.0.1" - } - }, - "flat-cache": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", - "integrity": "sha1-0wMLMrOBVPTjt+nHCfSQ9++XxIE=", - "dev": true, - "requires": { - "circular-json": "0.3.3", - "del": "2.2.2", - "graceful-fs": "4.1.11", - "write": "0.2.1" - } - }, - "flatten": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/flatten/-/flatten-1.0.2.tgz", - "integrity": "sha1-2uRqnXj74lKSJYzB54CkHZXAN4I=", - "dev": true - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "1.0.2" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz", - "integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==", - "dev": true, - "optional": true, - "requires": { - "nan": "2.7.0", - "node-pre-gyp": "0.6.36" - }, - "dependencies": { - "abbrev": { - "version": "1.1.0", - "bundled": true, - "dev": true, - "optional": true - }, - "ajv": { - "version": "4.11.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "co": "4.6.0", - "json-stable-stringify": "1.0.1" - } - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true, - "dev": true - }, - "aproba": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "delegates": "1.0.0", - "readable-stream": "2.2.9" - } - }, - "asn1": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "assert-plus": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "asynckit": { - "version": "0.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws-sign2": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "aws4": { - "version": "1.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "balanced-match": { - "version": "0.4.2", - "bundled": true, - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "tweetnacl": "0.14.5" - } - }, - "block-stream": { - "version": "0.0.9", - "bundled": true, - "dev": true, - "requires": { - "inherits": "2.0.3" - } - }, - "boom": { - "version": "2.10.1", - "bundled": true, - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "brace-expansion": { - "version": "1.1.7", - "bundled": true, - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "concat-map": "0.0.1" - } - }, - "buffer-shims": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "caseless": { - "version": "0.12.0", - "bundled": true, - "dev": true, - "optional": true - }, - "co": { - "version": "4.6.0", - "bundled": true, - "dev": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "combined-stream": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "requires": { - "delayed-stream": "1.0.0" - } - }, - "concat-map": { - "version": "0.0.1", - "bundled": true, - "dev": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true, - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "boom": "2.10.1" - } - }, - "dashdash": { - "version": "1.14.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "debug": { - "version": "2.6.8", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.4.2", - "bundled": true, - "dev": true, - "optional": true - }, - "delayed-stream": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "ecc-jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "extend": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "extsprintf": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "bundled": true, - "dev": true, - "optional": true - }, - "form-data": { - "version": "2.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.15" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "fstream": { - "version": "1.0.11", - "bundled": true, - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "inherits": "2.0.3", - "mkdirp": "0.5.1", - "rimraf": "2.6.1" - } - }, - "fstream-ignore": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "fstream": "1.0.11", - "inherits": "2.0.3", - "minimatch": "3.0.4" - } - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aproba": "1.1.1", - "console-control-strings": "1.1.0", - "has-unicode": "2.0.1", - "object-assign": "4.1.1", - "signal-exit": "3.0.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "wide-align": "1.1.2" - } - }, - "getpass": { - "version": "0.1.7", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "bundled": true, - "dev": true - }, - "har-schema": { - "version": "1.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "har-validator": { - "version": "4.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "ajv": "4.11.8", - "har-schema": "1.0.5" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "hawk": { - "version": "3.1.3", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "bundled": true, - "dev": true - }, - "http-signature": { - "version": "1.1.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.0", - "sshpk": "1.13.0" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true, - "dev": true - }, - "ini": { - "version": "1.3.4", - "bundled": true, - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "dev": true - }, - "isstream": { - "version": "0.1.2", - "bundled": true, - "dev": true, - "optional": true - }, - "jodid25519": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsbn": "0.1.1" - } - }, - "jsbn": { - "version": "0.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "json-schema": { - "version": "0.2.3", - "bundled": true, - "dev": true, - "optional": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json-stringify-safe": { - "version": "5.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "jsonify": { - "version": "0.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "jsprim": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.0.2", - "json-schema": "0.2.3", - "verror": "1.3.6" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "mime-db": { - "version": "1.27.0", - "bundled": true, - "dev": true - }, - "mime-types": { - "version": "2.1.15", - "bundled": true, - "dev": true, - "requires": { - "mime-db": "1.27.0" - } - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "dev": true, - "requires": { - "brace-expansion": "1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true, - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "node-pre-gyp": { - "version": "0.6.36", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "mkdirp": "0.5.1", - "nopt": "4.0.1", - "npmlog": "4.1.0", - "rc": "1.2.1", - "request": "2.81.0", - "rimraf": "2.6.1", - "semver": "5.3.0", - "tar": "2.2.1", - "tar-pack": "3.4.0" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "abbrev": "1.1.0", - "osenv": "0.1.4" - } - }, - "npmlog": { - "version": "4.1.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "1.1.4", - "console-control-strings": "1.1.0", - "gauge": "2.7.4", - "set-blocking": "2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "bundled": true, - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.4", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "dev": true - }, - "performance-now": { - "version": "0.2.0", - "bundled": true, - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "1.0.7", - "bundled": true, - "dev": true - }, - "punycode": { - "version": "1.4.1", - "bundled": true, - "dev": true, - "optional": true - }, - "qs": { - "version": "6.4.0", - "bundled": true, - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.1", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "deep-extend": "0.4.2", - "ini": "1.3.4", - "minimist": "1.2.0", - "strip-json-comments": "2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.2.9", - "bundled": true, - "dev": true, - "requires": { - "buffer-shims": "1.0.0", - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "1.0.1", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.81.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "aws-sign2": "0.6.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.1.4", - "har-validator": "4.2.1", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.15", - "oauth-sign": "0.8.2", - "performance-now": "0.2.0", - "qs": "6.4.0", - "safe-buffer": "5.0.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.2", - "tunnel-agent": "0.6.0", - "uuid": "3.0.1" - } - }, - "rimraf": { - "version": "2.6.1", - "bundled": true, - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "safe-buffer": { - "version": "5.0.1", - "bundled": true, - "dev": true - }, - "semver": { - "version": "5.3.0", - "bundled": true, - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "dev": true, - "optional": true - }, - "sntp": { - "version": "1.0.9", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "hoek": "2.16.3" - } - }, - "sshpk": { - "version": "1.13.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jodid25519": "1.0.2", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - }, - "dependencies": { - "assert-plus": { - "version": "1.0.0", - "bundled": true, - "dev": true, - "optional": true - } - } - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "stringstream": { - "version": "0.0.5", - "bundled": true, - "dev": true, - "optional": true - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "tar": { - "version": "2.2.1", - "bundled": true, - "dev": true, - "requires": { - "block-stream": "0.0.9", - "fstream": "1.0.11", - "inherits": "2.0.3" - } - }, - "tar-pack": { - "version": "3.4.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "debug": "2.6.8", - "fstream": "1.0.11", - "fstream-ignore": "1.0.5", - "once": "1.4.0", - "readable-stream": "2.2.9", - "rimraf": "2.6.1", - "tar": "2.2.1", - "uid-number": "0.0.6" - } - }, - "tough-cookie": { - "version": "2.3.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "bundled": true, - "dev": true, - "optional": true - }, - "uid-number": { - "version": "0.0.6", - "bundled": true, - "dev": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "dev": true - }, - "uuid": { - "version": "3.0.1", - "bundled": true, - "dev": true, - "optional": true - }, - "verror": { - "version": "1.3.6", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "extsprintf": "1.0.2" - } - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "dev": true, - "optional": true, - "requires": { - "string-width": "1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true, - "dev": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true - }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", - "dev": true, - "requires": { - "is-property": "1.0.2" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "1.0.0", - "inflight": "1.0.6", - "inherits": "2.0.3", - "minimatch": "3.0.4", - "once": "1.4.0", - "path-is-absolute": "1.0.1" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "2.0.0", - "is-glob": "2.0.1" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "2.0.1" - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==", - "dev": true - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "1.0.2", - "arrify": "1.0.1", - "glob": "7.1.2", - "object-assign": "4.1.1", - "pify": "2.3.0", - "pinkie-promise": "2.0.1" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "has": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", - "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", - "dev": true, - "requires": { - "function-bind": "1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "has-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", - "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", - "dev": true - }, - "home-or-tmp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", - "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", - "dev": true, - "requires": { - "os-homedir": "1.0.2", - "os-tmpdir": "1.0.2" - } - }, - "html-comment-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/html-comment-regex/-/html-comment-regex-1.1.1.tgz", - "integrity": "sha1-ZouTd26q5V696POtRkswekljYl4=", - "dev": true - }, - "html-janitor": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-janitor/-/html-janitor-2.0.2.tgz", - "integrity": "sha1-P0VR0j0b6FVOJz+eraK2F8Kzq3A=", - "dev": true - }, - "https-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-0.0.1.tgz", - "integrity": "sha1-P5E2XKvmC3ftDruiS0VOPgnZWoI=", - "dev": true - }, - "icss-replace-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz", - "integrity": "sha1-Bupvg2ead0njhs/h/oEq5dsiPe0=", - "dev": true - }, - "ieee754": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", - "integrity": "sha1-vjPUCsEO8ZJnAfbwii2G+/0a0+Q=", - "dev": true - }, - "ignore": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-3.3.5.tgz", - "integrity": "sha512-JLH93mL8amZQhh/p6mfQgVBH3M6epNq3DfsXsTSuSrInVjwyYlFE1nv2AgfRCC8PoOhM0jwQ5v8s9LgbK7yGDw==", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "indexes-of": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/indexes-of/-/indexes-of-1.0.1.tgz", - "integrity": "sha1-8w9xbI4r00bHtn0985FVZqfAVgc=", - "dev": true - }, - "indexof": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/indexof/-/indexof-0.0.1.tgz", - "integrity": "sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "1.4.0", - "wrappy": "1.0.2" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - }, - "inquirer": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-0.12.0.tgz", - "integrity": "sha1-HvK/1jUE3wvHV4X/+MLEHfEvB34=", - "dev": true, - "requires": { - "ansi-escapes": "1.4.0", - "ansi-regex": "2.1.1", - "chalk": "1.1.3", - "cli-cursor": "1.0.2", - "cli-width": "2.2.0", - "figures": "1.7.0", - "lodash": "4.17.4", - "readline2": "1.0.1", - "run-async": "0.1.0", - "rx-lite": "3.1.2", - "string-width": "1.0.2", - "strip-ansi": "3.0.1", - "through": "2.3.8" - } - }, - "interpret": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.0.4.tgz", - "integrity": "sha1-ggzdWIuGj/sZGoCVBtbJyPISsbA=", - "dev": true - }, - "invariant": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz", - "integrity": "sha1-nh9WrArNtr8wMwbzOL47IErmA2A=", - "dev": true, - "requires": { - "loose-envify": "1.3.1" - } - }, - "is-absolute-url": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-absolute-url/-/is-absolute-url-2.1.0.tgz", - "integrity": "sha1-UFMN+4T8yap9vnhS6Do3uTufKqY=", - "dev": true - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "1.10.0" - } - }, - "is-buffer": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.5.tgz", - "integrity": "sha1-Hzsm72E7IUuIy8ojzGwB2Hlh7sw=", - "dev": true - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-finite": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", - "integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "requires": { - "number-is-nan": "1.0.1" - } - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "1.0.0" - } - }, - "is-my-json-valid": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", - "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", - "dev": true, - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - } - }, - "is-path-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", - "integrity": "sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0=", - "dev": true - }, - "is-path-in-cwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz", - "integrity": "sha1-ZHdYK4IU1gI0YJRWcAO+ip6sBNw=", - "dev": true, - "requires": { - "is-path-inside": "1.0.0" - } - }, - "is-path-inside": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-1.0.0.tgz", - "integrity": "sha1-/AbloWg/vaE95mev9xe7wQpI838=", - "dev": true, - "requires": { - "path-is-inside": "1.0.2" - } - }, - "is-plain-obj": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", - "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", - "dev": true - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true - }, - "is-resolvable": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-resolvable/-/is-resolvable-1.0.0.tgz", - "integrity": "sha1-jfV8YeouPFAUCNEA+wE8+NbgzGI=", - "dev": true, - "requires": { - "tryit": "1.0.3" - } - }, - "is-svg": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-svg/-/is-svg-2.1.0.tgz", - "integrity": "sha1-z2EJDaDZ77yrhyLeum8DIgjbsOk=", - "dev": true, - "requires": { - "html-comment-regex": "1.1.1" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "js-base64": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.3.2.tgz", - "integrity": "sha512-Y2/+DnfJJXT1/FCwUebUhLWb3QihxiSC42+ctHLGogmW2jPY6LCapMdFZXRvVP2z6qyKW7s6qncE/9gSqZiArw==", - "dev": true - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.7.0.tgz", - "integrity": "sha1-XJZ93YN6m/3KXy3oQlOr6KHAO4A=", - "dev": true, - "requires": { - "argparse": "1.0.9", - "esprima": "2.7.3" - } - }, - "jsesc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", - "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=", - "dev": true - }, - "json-stable-stringify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", - "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", - "dev": true, - "requires": { - "jsonify": "0.0.0" - } - }, - "json5": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", - "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=", - "dev": true - }, - "jsonify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", - "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=", - "dev": true - }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - }, - "lazy-cache": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", - "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4=", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "1.1.2", - "type-check": "0.3.2" - } - }, - "loader-fs-cache": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz", - "integrity": "sha1-VuC/CL2XCLJqdltoUJhAyN7J/bw=", - "dev": true, - "requires": { - "find-cache-dir": "0.1.1", - "mkdirp": "0.5.1" - } - }, - "loader-utils": { - "version": "0.2.17", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", - "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", - "dev": true, - "requires": { - "big.js": "3.2.0", - "emojis-list": "2.1.0", - "json5": "0.5.1", - "object-assign": "4.1.1" - } - }, - "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", - "dev": true - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "longest": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz", - "integrity": "sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc=", - "dev": true - }, - "loose-envify": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz", - "integrity": "sha1-0aitM/qc4OcT1l/dCsi3SNR4yEg=", - "dev": true, - "requires": { - "js-tokens": "3.0.2" - } - }, - "macaddress": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/macaddress/-/macaddress-0.2.8.tgz", - "integrity": "sha1-WQTcU3w57G2+/q6QIycTX6hRHxI=", - "dev": true - }, - "math-expression-evaluator": { - "version": "1.2.17", - "resolved": "https://registry.npmjs.org/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz", - "integrity": "sha1-3oGf282E3M2PrlnGrreWFbnSZqw=", - "dev": true - }, - "memory-fs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.3.0.tgz", - "integrity": "sha1-e8xrYp46Q+hx1+Kaymrop/FcuyA=", - "dev": true, - "requires": { - "errno": "0.1.4", - "readable-stream": "2.3.3" - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "2.0.0", - "array-unique": "0.2.1", - "braces": "1.8.5", - "expand-brackets": "0.1.5", - "extglob": "0.3.2", - "filename-regex": "2.0.1", - "is-extglob": "1.0.0", - "is-glob": "2.0.1", - "kind-of": "3.2.2", - "normalize-path": "2.1.1", - "object.omit": "2.0.1", - "parse-glob": "3.0.4", - "regex-cache": "0.4.4" - } - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "1.1.8" - } - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "mute-stream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.5.tgz", - "integrity": "sha1-j7+rsKmKJT0xhDMfno3rc3L6xsA=", - "dev": true - }, - "nan": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz", - "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=", - "dev": true, - "optional": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node-libs-browser": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-0.7.0.tgz", - "integrity": "sha1-PicsCBnjCJNeJmdECNevDhSRuDs=", - "dev": true, - "requires": { - "assert": "1.4.1", - "browserify-zlib": "0.1.4", - "buffer": "4.9.1", - "console-browserify": "1.1.0", - "constants-browserify": "1.0.0", - "crypto-browserify": "3.3.0", - "domain-browser": "1.1.7", - "events": "1.1.1", - "https-browserify": "0.0.1", - "os-browserify": "0.2.1", - "path-browserify": "0.0.0", - "process": "0.11.10", - "punycode": "1.4.1", - "querystring-es3": "0.2.1", - "readable-stream": "2.3.3", - "stream-browserify": "2.0.1", - "stream-http": "2.7.2", - "string_decoder": "0.10.31", - "timers-browserify": "2.0.4", - "tty-browserify": "0.0.0", - "url": "0.11.0", - "util": "0.10.3", - "vm-browserify": "0.0.4" - }, - "dependencies": { - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - } - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "1.1.0" - } - }, - "normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=", - "dev": true - }, - "normalize-url": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-1.9.1.tgz", - "integrity": "sha1-LMDWazHqIwNkWENuNiDYWVTGbDw=", - "dev": true, - "requires": { - "object-assign": "4.1.1", - "prepend-http": "1.0.4", - "query-string": "4.3.4", - "sort-keys": "1.1.2" - } - }, - "num2fraction": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/num2fraction/-/num2fraction-1.2.2.tgz", - "integrity": "sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=", - "dev": true - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-1.2.0.tgz", - "integrity": "sha512-smRWXzkvxw72VquyZ0wggySl7PFUtoDhvhpdwgESXxUrH7vVhhp9asfup1+rVLrhsl7L45Ee1Q/l5R2Ul4MwUg==", - "dev": true - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "0.1.5", - "is-extendable": "0.1.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1.0.2" - } - }, - "onetime": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz", - "integrity": "sha1-ofeDj4MUxRbwXs78vEzP4EtO14k=", - "dev": true - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "dev": true, - "requires": { - "minimist": "0.0.8", - "wordwrap": "0.0.3" - }, - "dependencies": { - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", - "dev": true - } - } - }, - "optionator": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", - "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, - "requires": { - "deep-is": "0.1.3", - "fast-levenshtein": "2.0.6", - "levn": "0.3.0", - "prelude-ls": "1.1.2", - "type-check": "0.3.2", - "wordwrap": "1.0.0" - } - }, - "os-browserify": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.2.1.tgz", - "integrity": "sha1-Y/xMzuXS13Y9Jrv4YBB45sLgBE8=", - "dev": true - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true - }, - "pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU=", - "dev": true - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "0.3.0", - "is-dotfile": "1.0.3", - "is-extglob": "1.0.0", - "is-glob": "2.0.1" - } - }, - "path": { - "version": "0.12.7", - "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", - "integrity": "sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8=", - "dev": true, - "requires": { - "process": "0.11.10", - "util": "0.10.3" - } - }, - "path-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.0.tgz", - "integrity": "sha1-oLhwcpquIUAFt9UDLsLLuw+0RRo=", - "dev": true - }, - "path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s=", - "dev": true, - "requires": { - "pinkie-promise": "2.0.1" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", - "dev": true - }, - "pbkdf2-compat": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pbkdf2-compat/-/pbkdf2-compat-2.0.1.tgz", - "integrity": "sha1-tuDI+plJTZTgURV1gCpZpcFC8og=", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "2.0.4" - } - }, - "pkg-dir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-1.0.0.tgz", - "integrity": "sha1-ektQio1bstYp1EcFb/TpyTFM89Q=", - "dev": true, - "requires": { - "find-up": "1.1.2" - } - }, - "pluralize": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-1.2.1.tgz", - "integrity": "sha1-0aIUg/0iu0HlihL6NCGCMUCJfEU=", - "dev": true - }, - "postcss": { - "version": "5.2.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-5.2.18.tgz", - "integrity": "sha512-zrUjRRe1bpXKsX1qAJNJjqZViErVuyEkMTRrwu4ud4sbTtIBRmtaYDrHmcGgmrbsW3MHfmtIf+vJumgQn+PrXg==", - "dev": true, - "requires": { - "chalk": "1.1.3", - "js-base64": "2.3.2", - "source-map": "0.5.7", - "supports-color": "3.2.3" - }, - "dependencies": { - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "postcss-calc": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/postcss-calc/-/postcss-calc-5.3.1.tgz", - "integrity": "sha1-d7rnypKK2FcW4v2kLyYb98HWW14=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-message-helpers": "2.0.0", - "reduce-css-calc": "1.3.0" - } - }, - "postcss-colormin": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/postcss-colormin/-/postcss-colormin-2.2.2.tgz", - "integrity": "sha1-ZjFBfV8OkJo9fsJrJMio0eT5bks=", - "dev": true, - "requires": { - "colormin": "1.1.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-convert-values": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz", - "integrity": "sha1-u9hZPFwf0uPRwyK7kl3K6Nrk1i0=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-discard-comments": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz", - "integrity": "sha1-vv6J+v1bPazlzM5Rt2uBUUvgDj0=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-discard-duplicates": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz", - "integrity": "sha1-uavye4isGIFYpesSq8riAmO5GTI=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-discard-empty": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz", - "integrity": "sha1-0rS9nVztXr2Nyt52QMfXzX9PkrU=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-discard-overridden": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz", - "integrity": "sha1-ix6vVU9ob7KIzYdMVWZ7CqNmjVg=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-discard-unused": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz", - "integrity": "sha1-vOMLLMWR/8Y0Mitfs0ZLbZNPRDM=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "uniqs": "2.0.0" - } - }, - "postcss-filter-plugins": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz", - "integrity": "sha1-bYWGJTTXNaxCDkqFgG4fXUKG2Ew=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "uniqid": "4.1.1" - } - }, - "postcss-merge-idents": { - "version": "2.1.7", - "resolved": "https://registry.npmjs.org/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz", - "integrity": "sha1-TFUwMTwI4dWzu/PSu8dH4njuonA=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-merge-longhand": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz", - "integrity": "sha1-I9kM0Sewp3mUkVMyc5A0oaTz1lg=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-merge-rules": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz", - "integrity": "sha1-0d9d+qexrMO+VT8OnhDofGG19yE=", - "dev": true, - "requires": { - "browserslist": "1.7.7", - "caniuse-api": "1.6.1", - "postcss": "5.2.18", - "postcss-selector-parser": "2.2.3", - "vendors": "1.0.1" - } - }, - "postcss-message-helpers": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz", - "integrity": "sha1-pPL0+rbk/gAvCu0ABHjN9S+bpg4=", - "dev": true - }, - "postcss-minify-font-values": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz", - "integrity": "sha1-S1jttWZB66fIR0qzUmyv17vey2k=", - "dev": true, - "requires": { - "object-assign": "4.1.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-minify-gradients": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz", - "integrity": "sha1-Xb2hE3NwP4PPtKPqOIHY11/15uE=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-minify-params": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz", - "integrity": "sha1-rSzgcTc7lDs9kwo/pZo1jCjW8fM=", - "dev": true, - "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0", - "uniqs": "2.0.0" - } - }, - "postcss-minify-selectors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz", - "integrity": "sha1-ssapjAByz5G5MtGkllCBFDEXNb8=", - "dev": true, - "requires": { - "alphanum-sort": "1.0.2", - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-selector-parser": "2.2.3" - } - }, - "postcss-modules-extract-imports": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz", - "integrity": "sha1-thTJcgvmgW6u41+zpfqh26agXds=", - "dev": true, - "requires": { - "postcss": "6.0.13" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "postcss": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.13.tgz", - "integrity": "sha512-nHsrD1PPTMSJDfU+osVsLtPkSP9YGeoOz4FDLN4r1DW4N5vqL1J+gACzTQHsfwIiWG/0/nV4yCzjTMo1zD8U1g==", - "dev": true, - "requires": { - "chalk": "2.1.0", - "source-map": "0.6.1", - "supports-color": "4.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "postcss-modules-local-by-default": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz", - "integrity": "sha1-99gMOYxaOT+nlkRmvRlQCn1hwGk=", - "dev": true, - "requires": { - "css-selector-tokenizer": "0.7.0", - "postcss": "6.0.13" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "postcss": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.13.tgz", - "integrity": "sha512-nHsrD1PPTMSJDfU+osVsLtPkSP9YGeoOz4FDLN4r1DW4N5vqL1J+gACzTQHsfwIiWG/0/nV4yCzjTMo1zD8U1g==", - "dev": true, - "requires": { - "chalk": "2.1.0", - "source-map": "0.6.1", - "supports-color": "4.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "postcss-modules-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz", - "integrity": "sha1-1upkmUx5+XtipytCb75gVqGUu5A=", - "dev": true, - "requires": { - "css-selector-tokenizer": "0.7.0", - "postcss": "6.0.13" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "postcss": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.13.tgz", - "integrity": "sha512-nHsrD1PPTMSJDfU+osVsLtPkSP9YGeoOz4FDLN4r1DW4N5vqL1J+gACzTQHsfwIiWG/0/nV4yCzjTMo1zD8U1g==", - "dev": true, - "requires": { - "chalk": "2.1.0", - "source-map": "0.6.1", - "supports-color": "4.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "postcss-modules-values": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz", - "integrity": "sha1-7P+p1+GSUYOJ9CrQ6D9yrsRW6iA=", - "dev": true, - "requires": { - "icss-replace-symbols": "1.1.0", - "postcss": "6.0.13" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz", - "integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==", - "dev": true, - "requires": { - "color-convert": "1.9.0" - } - }, - "chalk": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.1.0.tgz", - "integrity": "sha512-LUHGS/dge4ujbXMJrnihYMcL4AoOweGnw9Tp3kQuqy1Kx5c1qKjqvMJZ6nVJPMWJtKCTN72ZogH3oeSO9g9rXQ==", - "dev": true, - "requires": { - "ansi-styles": "3.2.0", - "escape-string-regexp": "1.0.5", - "supports-color": "4.4.0" - } - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "postcss": { - "version": "6.0.13", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-6.0.13.tgz", - "integrity": "sha512-nHsrD1PPTMSJDfU+osVsLtPkSP9YGeoOz4FDLN4r1DW4N5vqL1J+gACzTQHsfwIiWG/0/nV4yCzjTMo1zD8U1g==", - "dev": true, - "requires": { - "chalk": "2.1.0", - "source-map": "0.6.1", - "supports-color": "4.4.0" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "requires": { - "has-flag": "2.0.0" - } - } - } - }, - "postcss-normalize-charset": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz", - "integrity": "sha1-757nEhLX/nWceO0WL2HtYrXLk/E=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-normalize-url": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz", - "integrity": "sha1-EI90s/L82viRov+j6kWSJ5/HgiI=", - "dev": true, - "requires": { - "is-absolute-url": "2.1.0", - "normalize-url": "1.9.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-ordered-values": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz", - "integrity": "sha1-7sbCpntsQSqNsgQud/6NpD+VwR0=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-reduce-idents": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz", - "integrity": "sha1-wsbSDMlYKE9qv75j92Cb9AkFmtM=", - "dev": true, - "requires": { - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-reduce-initial": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz", - "integrity": "sha1-aPgGlfBF0IJjqHmtJA343WT2ROo=", - "dev": true, - "requires": { - "postcss": "5.2.18" - } - }, - "postcss-reduce-transforms": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz", - "integrity": "sha1-/3b02CEkN7McKYpC0uFEQCV3GuE=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0" - } - }, - "postcss-selector-parser": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz", - "integrity": "sha1-+UN3iGBsPJrO4W/+jYsWKX8nu5A=", - "dev": true, - "requires": { - "flatten": "1.0.2", - "indexes-of": "1.0.1", - "uniq": "1.0.1" - } - }, - "postcss-svgo": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/postcss-svgo/-/postcss-svgo-2.1.6.tgz", - "integrity": "sha1-tt8YqmE7Zm4TPwittSGcJoSsEI0=", - "dev": true, - "requires": { - "is-svg": "2.1.0", - "postcss": "5.2.18", - "postcss-value-parser": "3.3.0", - "svgo": "0.7.2" - } - }, - "postcss-unique-selectors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz", - "integrity": "sha1-mB1X0p3csz57Hf4f1DuGSfkzyh0=", - "dev": true, - "requires": { - "alphanum-sort": "1.0.2", - "postcss": "5.2.18", - "uniqs": "2.0.0" - } - }, - "postcss-value-parser": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz", - "integrity": "sha1-h/OPnxj3dKSrTIojL1xc6IcqnRU=", - "dev": true - }, - "postcss-zindex": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/postcss-zindex/-/postcss-zindex-2.2.0.tgz", - "integrity": "sha1-0hCd3AVbka9n/EyzsCWUZjnSryI=", - "dev": true, - "requires": { - "has": "1.0.1", - "postcss": "5.2.18", - "uniqs": "2.0.0" - } - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prepend-http": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", - "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=", - "dev": true - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "private": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/private/-/private-0.1.7.tgz", - "integrity": "sha1-aM5eih7woju1cMwoU3tTMqumPvE=", - "dev": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", - "dev": true - }, - "prr": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/prr/-/prr-0.0.0.tgz", - "integrity": "sha1-GoS4WQgyVQFBGFPQCB7j+obikmo=", - "dev": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "q": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.0.tgz", - "integrity": "sha1-3QG6ydBtMObyGa7LglPunr3DCPE=", - "dev": true - }, - "query-string": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz", - "integrity": "sha1-u7aTucqRXCMlFbIosaArYJBD2+s=", - "dev": true, - "requires": { - "object-assign": "4.1.1", - "strict-uri-encode": "1.1.0" - } - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "randomatic": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-1.1.7.tgz", - "integrity": "sha512-D5JUjPyJbaJDkuAazpVnSfVkLlpeO3wDlPROTMLGKG1zMFNFRgrciKo1ltz/AzNTkqE0HzDx655QOL51N06how==", - "dev": true, - "requires": { - "is-number": "3.0.0", - "kind-of": "4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "3.2.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "1.1.5" - } - } - } - }, - "readable-stream": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.3.tgz", - "integrity": "sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ==", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "safe-buffer": "5.1.1", - "string_decoder": "1.0.3", - "util-deprecate": "1.0.2" - } - }, - "readdirp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.1.0.tgz", - "integrity": "sha1-TtCtBg3zBzMAxIRANz9y0cxkLXg=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "minimatch": "3.0.4", - "readable-stream": "2.3.3", - "set-immediate-shim": "1.0.1" - } - }, - "readline2": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/readline2/-/readline2-1.0.1.tgz", - "integrity": "sha1-QQWWCP/BVHV7cV2ZidGZ/783LjU=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "mute-stream": "0.0.5" - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "1.4.0" - } - }, - "reduce-css-calc": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz", - "integrity": "sha1-dHyRTgSWFKTJz7umKYca0dKSdxY=", - "dev": true, - "requires": { - "balanced-match": "0.4.2", - "math-expression-evaluator": "1.2.17", - "reduce-function-call": "1.0.2" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "reduce-function-call": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/reduce-function-call/-/reduce-function-call-1.0.2.tgz", - "integrity": "sha1-WiAL+S4ON3UXUv5FsKszD9S2vpk=", - "dev": true, - "requires": { - "balanced-match": "0.4.2" - }, - "dependencies": { - "balanced-match": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-0.4.2.tgz", - "integrity": "sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg=", - "dev": true - } - } - }, - "regenerate": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", - "integrity": "sha512-jVpo1GadrDAK59t/0jRx5VxYWQEDkkEKi6+HjE3joFVLfDOh9Xrdh0dF1eSq+BI/SwvTQ44gSscJ8N5zYL61sg==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz", - "integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A==", - "dev": true - }, - "regenerator-transform": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", - "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", - "dev": true, - "requires": { - "babel-runtime": "6.26.0", - "babel-types": "6.26.0", - "private": "0.1.7" - } - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "0.1.3" - } - }, - "regexpu-core": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", - "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", - "dev": true, - "requires": { - "regenerate": "1.3.3", - "regjsgen": "0.2.0", - "regjsparser": "0.1.5" - } - }, - "regjsgen": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", - "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=", - "dev": true - }, - "regjsparser": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", - "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", - "dev": true, - "requires": { - "jsesc": "0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.2.tgz", - "integrity": "sha1-7wiaF40Ug7quTZPrmLT55OEdmQo=", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "repeating": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", - "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", - "dev": true, - "requires": { - "is-finite": "1.0.2" - } - }, - "require-uncached": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/require-uncached/-/require-uncached-1.0.3.tgz", - "integrity": "sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM=", - "dev": true, - "requires": { - "caller-path": "0.1.0", - "resolve-from": "1.0.1" - } - }, - "resolve": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.4.0.tgz", - "integrity": "sha512-aW7sVKPufyHqOmyyLzg/J+8606v5nevBgaliIlV7nUpVMsDnoBGV/cbSLNjZAg9q0Cfd/+easKVKQ8vOu8fn1Q==", - "dev": true, - "requires": { - "path-parse": "1.0.5" - } - }, - "resolve-from": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz", - "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", - "dev": true - }, - "restore-cursor": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz", - "integrity": "sha1-NGYfRohjJ/7SmRR5FSJS35LapUE=", - "dev": true, - "requires": { - "exit-hook": "1.1.1", - "onetime": "1.1.0" - } - }, - "right-align": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/right-align/-/right-align-0.1.3.tgz", - "integrity": "sha1-YTObci/mo1FWiSENJOFMlhSGE+8=", - "dev": true, - "requires": { - "align-text": "0.1.4" - } - }, - "rimraf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.2.tgz", - "integrity": "sha512-lreewLK/BlghmxtfH36YYVg1i8IAce4TI7oao75I1g245+6BctqTVQiBP3YUJ9C6DQOXJmkYR9X9fCLtCOJc5w==", - "dev": true, - "requires": { - "glob": "7.1.2" - } - }, - "ripemd160": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-0.2.0.tgz", - "integrity": "sha1-K/GYveFnys+lHAqSjoS2i74XH84=", - "dev": true - }, - "run-async": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/run-async/-/run-async-0.1.0.tgz", - "integrity": "sha1-yK1KXhEGYeQCp9IbUw4AnyX444k=", - "dev": true, - "requires": { - "once": "1.4.0" - } - }, - "rx-lite": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/rx-lite/-/rx-lite-3.1.2.tgz", - "integrity": "sha1-Gc5QLKVyZl87ZHsQk5+X/RYV8QI=", - "dev": true - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", - "dev": true - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true - }, - "set-immediate-shim": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", - "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=", - "dev": true - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "sha.js": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.2.6.tgz", - "integrity": "sha1-F93t3F9yL7ZlAWWIlUYZd4ZzFbo=", - "dev": true - }, - "shelljs": { - "version": "0.7.8", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.7.8.tgz", - "integrity": "sha1-3svPh0sNHl+3LhSxZKloMEjprLM=", - "dev": true, - "requires": { - "glob": "7.1.2", - "interpret": "1.0.4", - "rechoir": "0.6.2" - } - }, - "slash": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", - "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=", - "dev": true - }, - "slice-ansi": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-0.0.4.tgz", - "integrity": "sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=", - "dev": true - }, - "sort-keys": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz", - "integrity": "sha1-RBttTTRnmPG05J6JIK37oOVD+a0=", - "dev": true, - "requires": { - "is-plain-obj": "1.1.0" - } - }, - "source-list-map": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-0.1.8.tgz", - "integrity": "sha1-xVCyq1Qn9rPyH1r+rYjE9Vh7IQY=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-support": { - "version": "0.4.18", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", - "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", - "dev": true, - "requires": { - "source-map": "0.5.7" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "stream-browserify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.1.tgz", - "integrity": "sha1-ZiZu5fm9uZQKTkUUyvtDu3Hlyds=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.3.3" - } - }, - "stream-http": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.7.2.tgz", - "integrity": "sha512-c0yTD2rbQzXtSsFSVhtpvY/vS6u066PcXOX9kBB3mSO76RiUQzL340uJkGBWnlBg4/HZzqiUXtaVA7wcRcJgEw==", - "dev": true, - "requires": { - "builtin-status-codes": "3.0.0", - "inherits": "2.0.3", - "readable-stream": "2.3.3", - "to-arraybuffer": "1.0.1", - "xtend": "4.0.1" - } - }, - "strict-uri-encode": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", - "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=", - "dev": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "requires": { - "code-point-at": "1.1.0", - "is-fullwidth-code-point": "1.0.0", - "strip-ansi": "3.0.1" - } - }, - "string_decoder": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.0.3.tgz", - "integrity": "sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ==", - "dev": true, - "requires": { - "safe-buffer": "5.1.1" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "2.1.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "svgo": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/svgo/-/svgo-0.7.2.tgz", - "integrity": "sha1-n1dyQTlSE1xv779Ar+ak+qiLS7U=", - "dev": true, - "requires": { - "coa": "1.0.4", - "colors": "1.1.2", - "csso": "2.3.2", - "js-yaml": "3.7.0", - "mkdirp": "0.5.1", - "sax": "1.2.4", - "whet.extend": "0.9.9" - } - }, - "table": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/table/-/table-3.8.3.tgz", - "integrity": "sha1-K7xULw/amGGnVdOUf+/Ys/UThV8=", - "dev": true, - "requires": { - "ajv": "4.11.8", - "ajv-keywords": "1.5.1", - "chalk": "1.1.3", - "lodash": "4.17.4", - "slice-ansi": "0.0.4", - "string-width": "2.1.1" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "2.0.0", - "strip-ansi": "4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "3.0.0" - } - } - } - }, - "tapable": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-0.1.10.tgz", - "integrity": "sha1-KcNXB8K3DlDQdIK10gLo7URtr9Q=", - "dev": true - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", - "dev": true - }, - "timers-browserify": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.4.tgz", - "integrity": "sha512-uZYhyU3EX8O7HQP+J9fTVYwsq90Vr68xPEFo7yrVImIxYvHgukBEgOB/SgGoorWVTzGM/3Z+wUNnboA4M8jWrg==", - "dev": true, - "requires": { - "setimmediate": "1.0.5" - } - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-fast-properties": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", - "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=", - "dev": true - }, - "trim-right": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", - "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", - "dev": true - }, - "tryit": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tryit/-/tryit-1.0.3.tgz", - "integrity": "sha1-OTvnMKlEb9Hq1tpZoBQwjzbCics=", - "dev": true - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "1.1.2" - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "uglify-js": { - "version": "2.7.5", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.7.5.tgz", - "integrity": "sha1-RhLAx7qu4rp8SH3kkErhIgefLKg=", - "dev": true, - "requires": { - "async": "0.2.10", - "source-map": "0.5.7", - "uglify-to-browserify": "1.0.2", - "yargs": "3.10.0" - }, - "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", - "dev": true - } - } - }, - "uglify-to-browserify": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", - "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", - "dev": true - }, - "uniq": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz", - "integrity": "sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8=", - "dev": true - }, - "uniqid": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/uniqid/-/uniqid-4.1.1.tgz", - "integrity": "sha1-iSIN32t1GuUrX3JISGNShZa7hME=", - "dev": true, - "requires": { - "macaddress": "0.2.8" - } - }, - "uniqs": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uniqs/-/uniqs-2.0.0.tgz", - "integrity": "sha1-/+3ks2slKQaW5uFl1KWe25mOawI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "user-home": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/user-home/-/user-home-2.0.0.tgz", - "integrity": "sha1-nHC/2Babwdy/SGBODwS4tJzenp8=", - "dev": true, - "requires": { - "os-homedir": "1.0.2" - } - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "vendors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vendors/-/vendors-1.0.1.tgz", - "integrity": "sha1-N61zyO5Bf7PVgOeFMSMH0nSEfyI=", - "dev": true - }, - "vm-browserify": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-0.0.4.tgz", - "integrity": "sha1-XX6kW7755Kb/ZflUOOCofDV9WnM=", - "dev": true, - "requires": { - "indexof": "0.0.1" - } - }, - "watchpack": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-0.2.9.tgz", - "integrity": "sha1-Yuqkq15bo1/fwBgnVibjwPXj+ws=", - "dev": true, - "requires": { - "async": "0.9.2", - "chokidar": "1.7.0", - "graceful-fs": "4.1.11" - }, - "dependencies": { - "async": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", - "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", - "dev": true - } - } - }, - "webpack": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-1.15.0.tgz", - "integrity": "sha1-T/MfU9sDM55VFkqdRo7gMklo/pg=", - "dev": true, - "requires": { - "acorn": "3.3.0", - "async": "1.5.2", - "clone": "1.0.2", - "enhanced-resolve": "0.9.1", - "interpret": "0.6.6", - "loader-utils": "0.2.17", - "memory-fs": "0.3.0", - "mkdirp": "0.5.1", - "node-libs-browser": "0.7.0", - "optimist": "0.6.1", - "supports-color": "3.2.3", - "tapable": "0.1.10", - "uglify-js": "2.7.5", - "watchpack": "0.2.9", - "webpack-core": "0.6.9" - }, - "dependencies": { - "acorn": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-3.3.0.tgz", - "integrity": "sha1-ReN/s56No/JbruP/U2niu18iAXo=", - "dev": true - }, - "interpret": { - "version": "0.6.6", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-0.6.6.tgz", - "integrity": "sha1-/s16GOfOXKar+5U+H4YhOknxYls=", - "dev": true - }, - "supports-color": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", - "integrity": "sha1-ZawFBLOVQXHYpklGsq48u4pfVPY=", - "dev": true, - "requires": { - "has-flag": "1.0.0" - } - } - } - }, - "webpack-core": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/webpack-core/-/webpack-core-0.6.9.tgz", - "integrity": "sha1-/FcViMhVjad76e+23r3Fo7FyvcI=", - "dev": true, - "requires": { - "source-list-map": "0.1.8", - "source-map": "0.4.4" - }, - "dependencies": { - "source-map": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.4.4.tgz", - "integrity": "sha1-66T12pwNyZneaAMti092FzZSA2s=", - "dev": true, - "requires": { - "amdefine": "1.0.1" - } - } - } - }, - "webpack-sources": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-0.1.5.tgz", - "integrity": "sha1-qh86vw8NdNtxEcQOUAuE+WZkB1A=", - "dev": true, - "requires": { - "source-list-map": "0.1.8", - "source-map": "0.5.7" - } - }, - "whet.extend": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/whet.extend/-/whet.extend-0.9.9.tgz", - "integrity": "sha1-+HfVv2SMl+WqVC+twW1qJZucEaE=", - "dev": true - }, - "window-size": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", - "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", - "dev": true - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz", - "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=", - "dev": true, - "requires": { - "mkdirp": "0.5.1" - } - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true - }, - "yargs": { - "version": "3.10.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.10.0.tgz", - "integrity": "sha1-9+572FfdfB0tOMDnTvvWgdFDH9E=", - "dev": true, - "requires": { - "camelcase": "1.2.1", - "cliui": "2.1.0", - "decamelize": "1.2.0", - "window-size": "0.1.0" - } - } - } -} diff --git a/package.json b/package.json index 4ea7ebf4..829207af 100644 --- a/package.json +++ b/package.json @@ -1,25 +1,59 @@ { "name": "codex.editor", - "version": "1.7.9", + "version": "2.0.0", "description": "Codex Editor. Native JS, based on API and Open Source", - "main": "index.js", + "main": "build/codex-editor.js", "scripts": { - "build": "webpack" + "build": "rimraf dist && yarn svg && yarn build:dev", + "svg": "svg-sprite-generate -d src/assets/ -o build/sprite.svg", + "build:dev": "webpack --mode development --progress --display-error-details --display-entrypoints" }, - "author": "Codex Team", + "author": "CodeX", "license": "ISC", + "repository": { + "type": "git", + "url": "git+https://github.com/codex-team/codex.editor.git" + }, "devDependencies": { - "babel-core": "^6.21.0", - "babel-loader": "^6.2.10", - "babel-polyfill": "^6.20.0", - "babel-preset-es2015": "^6.22.0", - "babel-runtime": "^6.20.0", - "css-loader": "^0.26.1", - "eslint": "^3.12.2", - "eslint-loader": "^1.6.1", - "extract-text-webpack-plugin": "^1.0.1", + "@codexteam/shortcuts": "^1.0.0", + "babel-core": "^6.26.3", + "babel-loader": "^7.1.5", + "babel-plugin-add-module-exports": "^0.2.1", + "babel-plugin-class-display-name": "^2.1.0", + "babel-polyfill": "^6.26.0", + "babel-preset-env": "^1.7.0", + "babel-preset-stage-0": "^6.24.1", + "babel-runtime": "^6.26.0", + "css-loader": "^0.28.11", + "eslint": "^4.19.1", + "eslint-loader": "^2.1.0", + "extract-text-webpack-plugin": "^3.0.2", "html-janitor": "^2.0.2", "path": "^0.12.7", - "webpack": "^1.14.0" + "postcss-apply": "^0.10.0", + "postcss-color-hex-alpha": "^3.0.0", + "postcss-color-mod-function": "^2.4.3", + "postcss-cssnext": "^3.1.0", + "postcss-custom-media": "^6.0.0", + "postcss-custom-properties": "^7.0.0", + "postcss-custom-selectors": "^4.0.1", + "postcss-font-family-system-ui": "^3.0.0", + "postcss-font-variant": "^3.0.0", + "postcss-loader": "^2.1.6", + "postcss-media-minmax": "^3.0.0", + "postcss-nested": "^3.0.0", + "postcss-nested-ancestors": "^2.0.0", + "postcss-nesting": "^6.0.0", + "postcss-smart-import": "^0.7.6", + "raw-loader": "^0.5.1", + "rimraf": "^2.6.2", + "stylelint": "^9.3.0", + "svg-sprite-generator": "0.0.7", + "ts-loader": "^4.4.2", + "tslint": "^5.11.0", + "tslint-loader": "^3.6.0", + "typescript": "^2.9.2", + "webpack": "^4.16.2", + "webpack-cli": "^3.1.0" } } diff --git a/plugins/attaches/attaches.css b/plugins/attaches/attaches.css deleted file mode 100644 index 8082a356..00000000 --- a/plugins/attaches/attaches.css +++ /dev/null @@ -1,130 +0,0 @@ -.cdx-attaches__default-wrapper { - margin: 15px 0; - padding: 15px; - background: #fff; - border: 1px solid #ebecec; - box-shadow: 0 1px 2px 0 rgba(34, 36, 44, 0.03); - border-radius: 3px; - text-align: center; -} - -.cdx-attaches__default-button { - color: #8990aa; - cursor: pointer; -} - -.cdx-attaches__default-button:hover { - color: #393f52; -} - -.cdx-attaches__wrapper { - display: -ms-flexbox; - display: flex; - -ms-flex-flow: row nowrap; - flex-flow: row nowrap; - -ms-flex-pack: start; - justify-content: flex-start; - -ms-flex-align: center; - align-items: center; - margin: 10px 0; - padding: 15px 20px; - background: #fff; - border: 1px solid #ebecec; - box-shadow: 0 1px 2px 0 rgba(34, 36, 44, 0.03); - border-radius: 3px; - font-size: 15px; -} - -.cdx-attaches__file-name { - -ms-flex-positive: 8; - flex-grow: 8; - width: 100%; - outline: none; - border: 0; - font-size: inherit; -} - -.cdx-attaches__file-name--collapsed { - width: 30%; - overflow: hidden; - text-overflow: ellipsis; -} - -.cdx-attaches__extension, -.cdx-attaches__size { - color: #8f9298; - white-space: nowrap; -} - -.cdx-attaches__extension::after { - content: ','; - margin-right: 0.2em; -} - -.cdx-attaches__size::after { - content: 'KB'; - margin-left: 0.2em; -} - -.cdx-attaches__icon { - display: inline-block; - width: 16px; - height: 32px; - background: url(file-icon-black.svg) no-repeat center center; - background-size: contain; -} - -li:hover .cdx-attaches__icon, -.selected .cdx-attaches__icon { - background: url(file-icon-white.svg) no-repeat center center; - background-size: contain; -} - -.cdx-attaches__icon--inline { - height: 16px; - vertical-align: text-bottom; -} - -.cdx-attaches__loader { - background-color: transparent; - background-image: repeating-linear-gradient(-45deg, transparent, transparent 4px, #f5f9ff 4px, #eaedef 8px) !important; - background-size: 56px 56px; - animation: loading-bar 5s infinite linear; -} - -@keyframes loading-bar { - 100% { background-position: -56% 0; } -} - -.cdx-attaches__progress-bar { - width: 100%; - height: 3px; - margin: 0 15px; - background: #e1e3eb; - border: 0; - border-radius: 5px; -} - -.cdx-attaches__progress-bar::-webkit-progress-bar { - background: #e0e1e3; - border-radius: 5px; -} - -.cdx-attaches__progress-bar::-webkit-progress-value { - background: #414957; - border-radius: 5px; - transition: all 100ms ease-in; -} - -progress::-moz-progress-bar { - background: #414957; - border-radius: 5px; -} - -.cdx-attaches__cross-button { - width: 18px; - height: 18px; - background: url(cross.svg) no-repeat center center; - background-size: contain; - cursor: pointer; -} diff --git a/plugins/attaches/attaches.js b/plugins/attaches/attaches.js deleted file mode 100644 index 52268411..00000000 --- a/plugins/attaches/attaches.js +++ /dev/null @@ -1,430 +0,0 @@ -/** - * Attache-file Plugin for CodeX Editor - * - * @param {String} config.fetchUrl - Route for file uploding - * @param {Nubmer} config.maxSize - Maximum allowed file size in KB - * @param {String} config.accept - Accepted MIME-types. By default, accepts all - * - * Backend should return response with - * 'url' - Full path to the uploaded file - * 'title' - File title, - * 'name' - File name without extension, - * 'extension' - File extension, - * 'size' - File size - * - * @author @gohabereg - * @version 1.0.0 - */ -var cdxAttaches = function () { - - /** - * Private methods and props - */ - - var KBYTE = 1024, - fileWrapper = null; - - /** - * Default config - * Can be redefined with prepare method - * - * @var sting config.fetchUrl -- url to your fetch script - * @var int config.maxSize -- max size of file in kilobytes - * @var accept config.accept -- valid MIME-types. By default, accepts all - * - */ - var config = { - - fetchUrl: '', - maxSize: 2, - accept: '' - - }; - - var elementsClasses = { - - defaultFormWrapper : 'cdx-attaches__default-wrapper', - defaultFormButton : 'cdx-attaches__default-button', - - progressBar : 'cdx-attaches__progress-bar', - wrapper : 'cdx-attaches__wrapper', - loader : 'cdx-attaches__loader', - crossButton : 'cdx-attaches__cross-button', - - file: { - title : 'cdx-attaches__file-name', - collapsedName : 'cdx-attaches__file-name--collapsed', - extension : 'cdx-attaches__extension', - size : 'cdx-attaches__size' - } - - }; - - var ui = { - - defaultForm: function () { - - var wrapper = codex.editor.draw.node('div', elementsClasses.defaultFormWrapper), - button = codex.editor.draw.node('div', elementsClasses.defaultFormButton); - - button.addEventListener('click', upload.fire); - button.innerHTML = ' Загрузить файл'; - - wrapper.appendChild(button); - - return wrapper; - - }, - - uploadedFile: function (data) { - - var wrapper = codex.editor.draw.node('div', elementsClasses.wrapper), - name = codex.editor.draw.node('input', elementsClasses.file.title), - extension = codex.editor.draw.node('span', elementsClasses.file.extension), - size = codex.editor.draw.node('span', elementsClasses.file.size); - - wrapper.dataset.url = data.url; - wrapper.dataset.name = data.name; - name.value = data.title || ''; - extension.textContent = data.extension.toUpperCase(); - size.textContent = data.size; - - wrapper.appendChild(name); - wrapper.appendChild(extension); - wrapper.appendChild(size); - - return wrapper; - - }, - - progressBar: { - - bar: null, - - draw: function () { - - var wrapper = codex.editor.draw.node('div', elementsClasses.wrapper), - progress = codex.editor.draw.node('progress', elementsClasses.progressBar), - name = codex.editor.draw.node('span', elementsClasses.file.title), - crossButton = codex.editor.draw.node('span', elementsClasses.crossButton); - - progress.max = 100; - progress.value = 0; - - name.textContent = codex.editor.transport.input.files[0].name; - name.classList.add(elementsClasses.file.collapsedName); - - crossButton.addEventListener('click', upload.abort); - - ui.progressBar.bar = progress; - - wrapper.appendChild(name); - wrapper.appendChild(progress); - wrapper.appendChild(crossButton); - - return wrapper; - - }, - - change: function (value) { - - console.assert( !isNaN(value), 'CodeX Editor Attaches: passed value is not a Number'); - - ui.progressBar.bar.value = value; - - } - - } - - }; - - /** - * Notify about upload errors via codex.editor.notifications - * - * @param Object error can have `message` property with error message - */ - var notifyError = function (error) { - - error = error || {}; - - codex.editor.notifications.notification({ - type: 'error', - message: 'Ошибка во время загрузки файла' + ( error.message ? ': ' + error.message : '' ) - }); - - }; - - /** - * Contains validation methods - * - * TODO: MIME-type validation - * - */ - var validation = { - - size: function () { - - var file = codex.editor.transport.input.files[0]; - - return Math.ceil(file.size / KBYTE) <= config.maxSize; - - }, - - }; - - var upload = { - - current: null, - - aborted: false, - - /** - * Fired codex.editor.transport selectAndUpload methods - */ - fire: function () { - - codex.editor.transport.selectAndUpload({ - url: config.fetchUrl, - success: upload.success, - beforeSend: upload.start, - progress: upload.progress, - error: upload.error, - accept: config.accept - }); - - }, - - /** - * Will be called before upload - * Draws load animation and progress bar - */ - start: function () { - - if (!validation.size()) { - - notifyError({message: 'Файл слишком большой'}); - return false; - - } - - if (upload.current) { - - notifyError({message: 'Дождитесь окончания предыдущей загрузки'}); - return; - - } - - var progress = ui.progressBar.draw(); - - upload.current = progress; - - codex.editor.content.switchBlock(fileWrapper, progress, 'attaches'); - - }, - - /** - * Handler for XmlHttpRequest.upload.onprogress event - * Changes progress bar status - * - * @param event - */ - progress: function (event) { - - /** Prevents isNaN value assignment */ - if (!event.total) { - return; - } - - var value = parseInt(event.loaded / event.total * 100); - - ui.progressBar.change(value); - - }, - - /** - * Will be called after success upload - * Try to decode JSON response and draws ui or fires error handler - * - * @param response - */ - success: function (response) { - - var data, - uploadedFile; - - try { - - response = JSON.parse(response); - - if (response.success) { - - data = response.data; - data.size = Math.ceil(data.size / KBYTE) || 1; - - uploadedFile = ui.uploadedFile(data); - codex.editor.content.switchBlock(upload.current, uploadedFile, 'attaches'); - - uploadedFile.querySelector('input').focus(); - - } else { - - upload.error(response); - - } - - } catch (e) { - - upload.error(); - - } - - upload.current = null; - - }, - - /** - * Upload errors handler - * - * @param error - */ - error: function (error) { - - var defaultFrom = ui.defaultForm(); - - codex.editor.content.switchBlock(upload.current, defaultFrom, 'attaches'); - - if (!upload.aborted) { - - notifyError(error); - - } - - upload.aborted = false; - - upload.current = null; - - }, - - abort: function () { - - codex.editor.transport.abort(); - - upload.aborted = true; - - upload.current = null; - - } - - }; - - - - /* - * Public methods - * @param {String} _config.fetchUrl Required - */ - var prepare = function (_config) { - - return new Promise(function(resolve, reject){ - - if ( !_config.fetchUrl ){ - - reject(Error('fetchUrl is missed')); - return; - - } - - config.fetchUrl = _config.fetchUrl; - config.accept = _config.accept || config.accept; - - if ( !isNaN(_config.maxSize)){ - config.maxSize = _config.maxSize; - } - - resolve(); - - }); - - }; - - var render = function (data) { - - if (!data) { - - fileWrapper = ui.defaultForm(); - return fileWrapper; - - } - - return ui.uploadedFile(data); - - }; - - var save = function (block) { - - var data = { - - url: block.dataset.url, - name: block.dataset.name, - title: block.querySelector('.' + elementsClasses.file.title).value, - extension: block.querySelector('.' + elementsClasses.file.extension).textContent, - size: block.querySelector('.' + elementsClasses.file.size).textContent, - - }; - - return data; - - }; - - var validate = function (data) { - - if (!data.url || !data.url.trim()) { - - return false; - - } - - if (!data.title || !data.title.trim()) { - - return false; - - } - - if (!data.extension || !data.extension.trim()) { - - return false; - - } - - if (!data.size || !data.size.trim()) { - - return false; - - } - - return true; - - }; - - var destroy = function () { - - cdxAttaches = null; - - }; - - var appendCallback = function () { - - upload.fire(); - - }; - - return { - prepare: prepare, - render: render, - save: save, - validate: validate, - destroy: destroy, - appendCallback: appendCallback - }; - -}(); \ No newline at end of file diff --git a/plugins/attaches/cross.svg b/plugins/attaches/cross.svg deleted file mode 100644 index 1cbdeac8..00000000 --- a/plugins/attaches/cross.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/plugins/attaches/file-icon-black.svg b/plugins/attaches/file-icon-black.svg deleted file mode 100644 index 0a559b7e..00000000 --- a/plugins/attaches/file-icon-black.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/plugins/attaches/file-icon-white.svg b/plugins/attaches/file-icon-white.svg deleted file mode 100644 index 87a6039e..00000000 --- a/plugins/attaches/file-icon-white.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/plugins/code/code.css b/plugins/code/code.css deleted file mode 100644 index 8221b615..00000000 --- a/plugins/code/code.css +++ /dev/null @@ -1,30 +0,0 @@ -.ce-code { - display: block; - width: 100%; - min-height: 100px; - border: 1px solid #ebeef3; - border-radius: 3px; - background: #fdfdff !important; - - margin: 1em 0 !important; - padding: .5em .8em; - box-sizing: border-box; - white-space: pre-wrap; - - font-family: 'monospace', 'monaco', 'consolas', 'courier'; - line-height: 1.5em; - color: #325179; - font-size: .8em; - - resize: vertical; - outline: none; -} - - -/** -* CodeX Editor styles overlay -* @todo change for ce-tool-wrapper__code -*/ -.ce_block[data-type="code"]{ - padding: 1em 0 !important; -} diff --git a/plugins/code/code.js b/plugins/code/code.js deleted file mode 100644 index aaeb72b3..00000000 --- a/plugins/code/code.js +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Code Plugin\ - * Creates code tag and adds content to this tag - */ - -var code = (function(code_plugin) { - - var baseClass = "ce-code"; - - /** - * Make initial header block - * @param {object} JSON with block data - * @return {Element} element to append - */ - var make_ = function (data) { - - var tag = codex.editor.draw.node('TEXTAREA', [baseClass], {}); - - if (data && data.text) { - tag.value = data.text; - } - - return tag; - }; - - /** - * Escapes HTML chars - * - * @param {string} input - * @return {string} — escaped string - */ - var escapeHTML_ = function (input) { - - var div = document.createElement('DIV'), - text = document.createTextNode(input); - - div.appendChild(text); - - return div.innerHTML; - - }; - - /** - * Method to render HTML block from JSON - */ - code_plugin.render = function (data) { - - return make_(data); - }; - - /** - * Method to extract JSON data from HTML block - */ - code_plugin.save = function (blockContent) { - - var escaped = escapeHTML_(blockContent.value), - data = { - text : escaped - }; - - - return data; - - }; - - code_plugin.validate = function (data) { - - if (data.text.trim() == '') - return; - - return true; - }; - - code_plugin.destroy = function () { - - code = null; - - }; - - return code_plugin; - -})({}); diff --git a/plugins/embed/embed.css b/plugins/embed/embed.css deleted file mode 100644 index c11a8b38..00000000 --- a/plugins/embed/embed.css +++ /dev/null @@ -1,17 +0,0 @@ -.ce-redactor .embed { - max-width: 600px; - margin: 15px 0; - background: #fff; -} - -.ce-redactor .embed iframe { - width: 100% !important; - border: 0 !important; -} - -.embed__loader { - background-image: url("loading.gif") !important; - background-repeat: no-repeat; - background-position: center center; - opacity: 0.5; -} diff --git a/plugins/embed/embed.js b/plugins/embed/embed.js deleted file mode 100644 index 744ec85c..00000000 --- a/plugins/embed/embed.js +++ /dev/null @@ -1,211 +0,0 @@ -/** - * Embed plugin by gohabereg - * @version 1.0.0 - */ -var embed = function(embed_plugin){ - - var methods = { - - addInternal: function (content) { - codex.editor.content.switchBlock(codex.editor.content.currentNode, content); - - var blockContent = codex.editor.content.currentNode.childNodes[0]; - blockContent.classList.add('embed__loader'); - - setTimeout(function(){ - blockContent.classList.remove('embed__loader'); - }, 1000); - - }, - - getHtmlWithEmbedId: function (type, id) { - return services[type].html.replace(/<\%\= remote\_id \%\>/g, id); - }, - - makeElementFromHtml: function(html) { - var wrapper = document.createElement('DIV'); - - wrapper.innerHTML = html; - - return wrapper; - }, - - getRemoteId: function(source, execArray) { - - switch(source) { - case 'yandex-music-track': - id = execArray[2]+'/'+execArray[1]; - break; - case 'yandex-music-playlist': - id = execArray[1]+'/'+execArray[2]; - break; - default: - id = execArray[1]; - } - - return id; - } - }; - - var services = { - youtube: { - regex: /^.*(?:(?:youtu\.be\/)|(?:youtube\.com)\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*)(?:[\?\&]t\=(\d*)|)/, - html: "", - height: 320, - width: 580 - } - }; - - - embed_plugin.make = function(data, isInternal) { - - if (!data.remote_id) - return; - - var html = methods.getHtmlWithEmbedId(data.source, data.remote_id), - block = methods.makeElementFromHtml(html); - - block.dataset.remoteId = data.remote_id; - block.dataset.source = data.source; - block.dataset.thumbnailUrl = data.thumbnailUrl; - - block.classList.add('embed'); - - // var sidePadding = (600 - services[data.source].width) / 2 + 'px'; - - // block.style.padding = '30px ' + sidePadding; - - if (isInternal) { - methods.addInternal(block); - } - - return block; - - }; - - /** - * Saving JSON output. - * Upload data via ajax - */ - embed_plugin.save = function(blockContent) { - - if (!blockContent) - return; - - var data, - source = blockContent.dataset.source; - - data = { - source: source, - remote_id: blockContent.dataset.remoteId, - thumbnailUrl: blockContent.dataset.thumbnailUrl, - height: services[source].height, - width: services[source].width - }; - - return data; - - }; - - /** - * Render data - */ - embed_plugin.render = function(data) { - return embed_plugin.make(data); - }; - - embed_plugin.urlPastedCallback = function(url, pattern) { - - var execArray = pattern.regex.exec(url), - id = methods.getRemoteId(pattern.type, execArray); - - var data = { - source: pattern.type, - remote_id: id, - thumbnailUrl: url - }; - - embed_plugin.make(data, true); - }; - - embed_plugin.validate = function(savedData) { - - var source = savedData.source, - execArray = services[source].regex.exec(savedData.thumbnailUrl), - remoteId = methods.getRemoteId(source, execArray); - - return remoteId == savedData.remote_id; - - }; - - embed_plugin.pastePatterns = [ - { - type: 'vk', - regex: /https?:\/\/vk\.com\/.*(?:video)([-0-9]+_[0-9]+)/, ///https?.+vk?.com\/feed\?w=wall\d+_\d+/, - callback: embed_plugin.urlPastedCallback - }, - { - type: 'youtube', - regex: /(?:https?:\/{2})?(?:w{3}\.)?youtu(?:be)?\.(?:com|be)(?:\/watch\?v=|\/)([^\s&]+)/, - callback: embed_plugin.urlPastedCallback - }, - { - type: 'vimeo', - regex: /(?:http[s]?:\/\/)?(?:www.)?vimeo\.co(?:.+\/([^\/]\d+)(?:#t=[\d]+)?s?$)/, - callback: embed_plugin.urlPastedCallback - }, - { - type: 'coub', - regex: /https?:\/\/coub\.com\/view\/([^\/\?\&]+)/, - callback: embed_plugin.urlPastedCallback - }, - { - type: 'vine', - regex: /https?:\/\/vine\.co\/v\/([^\/\?\&]+)/, - callback: embed_plugin.urlPastedCallback - }, - { - type: 'imgur', - regex: /https?:\/\/(?:i\.)?imgur\.com.*\/([a-zA-Z0-9]+)(?:\.gifv)?/, - callback: embed_plugin.urlPastedCallback - }, - { - type: 'gfycat', - regex: /https?:\/\/gfycat\.com(?:\/detail)?\/([a-zA-Z]+)/, - callback: embed_plugin.urlPastedCallback - }, - { - type: 'twitch-channel', - regex: /https?:\/\/www.twitch.tv\/([^\/\?\&]*)/, - callback: embed_plugin.urlPastedCallback - }, - { - type: 'twitch-video', - regex: /https?:\/\/www.twitch.tv\/(?:[^\/\?\&]*\/v|videos)\/([0-9]*)/, - callback: embed_plugin.urlPastedCallback - }, - { - type: 'yandex-music-album', - regex: /https?:\/\/music.yandex.ru\/album\/([0-9]*)/, - callback: embed_plugin.urlPastedCallback - }, - { - type: 'yandex-music-track', - regex: /https?:\/\/music.yandex.ru\/album\/([0-9]*)\/track\/([0-9]*)/, - callback: embed_plugin.urlPastedCallback - }, - { - type: 'yandex-music-playlist', - regex: /https?:\/\/music.yandex.ru\/users\/([^\/\?\&]*)\/playlists\/([0-9]*)/, - callback: embed_plugin.urlPastedCallback - } ]; - - embed_plugin.destroy = function () { - - embed = null; - - }; - - return embed_plugin; - -}({}); \ No newline at end of file diff --git a/plugins/embed/loading.gif b/plugins/embed/loading.gif deleted file mode 100644 index 4e3904d5..00000000 Binary files a/plugins/embed/loading.gif and /dev/null differ diff --git a/plugins/header/header.css b/plugins/header/header.css deleted file mode 100644 index 42a1c0ce..00000000 --- a/plugins/header/header.css +++ /dev/null @@ -1,41 +0,0 @@ -/** -* Plugin styles -*/ -.ce-header { - padding: .7em 0; - margin: 0; - line-height: 1.4em; -} -.ce-header p, -.ce-header div{ - padding: 0 !important; - margin: 0 !important; -} - -/** H e a d e r - settings */ -.ce_plugin_header--select_button{ - display: block; - color: #306ac7; - cursor: pointer; - line-height: 1.3em; -} - .ce_plugin_header--select_button:not(:last-of-type){ - margin-bottom: 1.5em; - } - .ce_plugin_header--select_button:hover{ - color: #a1b4ec; - } - - -/** -* Empty header placeholder -*/ -.ce-header:empty::before{ - content : attr(data-placeholder); - color: #818BA1; - opacity: .7; - transition: opacity 200ms ease; -} -.ce-header:focus::before{ - opacity: .1; -} diff --git a/plugins/header/header.js b/plugins/header/header.js deleted file mode 100644 index 6e73ee88..00000000 --- a/plugins/header/header.js +++ /dev/null @@ -1,169 +0,0 @@ -/** -* Example of making plugin -* H e a d e r -*/ - -var header = (function(header_plugin) { - - /** - * @private - */ - var methods_ = { - - /** - * Binds click event to passed button - */ - addSelectTypeClickListener : function (el, type) { - - el.addEventListener('click', function () { - - methods_.selectTypeClicked(type); - - }, false); - }, - - /** - * Replaces old header with new type - * @params {string} type - new header tagName: H1—H6 - */ - selectTypeClicked : function (type) { - - var old_header, new_header; - - /** Now current header stored as a currentNode */ - old_header = codex.editor.content.currentNode.querySelector('[contentEditable]'); - - /** Making new header */ - new_header = codex.editor.draw.node(type, ['ce-header'], { innerHTML : old_header.innerHTML }); - new_header.contentEditable = true; - new_header.setAttribute('data-placeholder', 'Заголовок'); - new_header.dataset.headerData = type; - - codex.editor.content.switchBlock(old_header, new_header, 'header'); - - /** Close settings after replacing */ - codex.editor.toolbar.settings.close(); - } - - }; - - /** - * @private - * - * Make initial header block - * @param {object} JSON with block data - * @return {Element} element to append - */ - var make_ = function (data) { - - var availableTypes = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'], - tag, - headerType = 'h2'; - - - if ( data && data['heading-styles'] && availableTypes.includes(data['heading-styles']) ) { - - headerType = data['heading-styles']; - - } - - tag = document.createElement(headerType); - - /** - * Save header type in data-attr. - * We need it in save method to extract type from HTML to JSON - */ - tag.dataset.headerData = headerType; - - - if (data && data.text) { - tag.textContent = data.text; - } - - if (!tag.dataset.headerData) { - tag.dataset.headerData = 'h2'; - } - - tag.classList.add('ce-header'); - tag.setAttribute('data-placeholder', 'Заголовок'); - tag.contentEditable = true; - - return tag; - - }; - - header_plugin.prepareDataForSave = function(data) { - - }; - - /** - * Method to render HTML block from JSON - */ - header_plugin.render = function (data) { - - return make_(data); - - }; - - /** - * Method to extract JSON data from HTML block - */ - header_plugin.save = function (blockContent) { - - var data = { - "heading-styles": blockContent.dataset.headerData, - "format": "html", - "text": blockContent.textContent || '' - }; - - return data; - }; - - /** - * Settings panel content - * - - - - - - - - - - - - - - * | настройки H1 H2 H3 | - * - - - - - - - - - - - - - - * @return {Element} element contains all settings - */ - header_plugin.makeSettings = function () { - - var holder = codex.editor.draw.node('DIV', ['cdx-plugin-settings--horisontal'], {} ), - types = { - h2: 'H2', - h3: 'H3', - h4: 'H4' - }, - selectTypeButton; - - /** Now add type selectors */ - for (var type in types){ - - selectTypeButton = codex.editor.draw.node('SPAN', ['cdx-plugin-settings__item'], { textContent : types[type] }); - methods_.addSelectTypeClickListener(selectTypeButton, type); - holder.appendChild(selectTypeButton); - - } - - return holder; - }; - - header_plugin.validate = function(data) { - - if (data.text.trim() === '' || data['heading-styles'].trim() === ''){ - return false; - } - - return true; - }; - - header_plugin.destroy = function () { - - header = null; - - } - - return header_plugin; - -})({}); - diff --git a/plugins/image/image.css b/plugins/image/image.css deleted file mode 100644 index 8534065e..00000000 --- a/plugins/image/image.css +++ /dev/null @@ -1,180 +0,0 @@ -/** -* Image plugin for codex-editor -* @author CodeX Team -* -* @version 0.0.1 -*/ - -.ce-image__wrapper img { - transition: all 500ms ease-in; - will-change: opacity, filter; -} - -.ce-image__preview img { - opacity: .5; - filter: blur(1.7px) grayscale(1); -} - -/** upload image form */ -.ce-plugin-image__holder{ - position: relative; - background: #FEFEFE; - border: 2px solid #edeff5; - text-align: center; - margin: 30px 0; - padding: 15px; -} - .ce-plugin-image__holder input{ - border: 0; - background: transparent; - outline: none; - -webkit-appearance: none; - font-size: 1.2em; - color: #A5ABBC; - } - /* Placeholder color for Chrome */ - .ce-plugin-image__holder input::-webkit-input-placeholder { - color: #A5ABBC; - } - /* Placeholder color for IE 10+ */ - .ce-plugin-image__holder input:-ms-input-placeholder { - color: #A5ABBC; - } - /* Placeholder color for Firefox 19+ */ - .ce-plugin-image__holder input::-moz-placeholder { - color: #A5ABBC; - opacity: 1; - } - -.ce-plugin-image__loader { - background-color: transparent; - background-image: repeating-linear-gradient(-45deg, transparent, transparent 4px, #f5f9ff 4px, #eaedef 8px) !important; - background-size: 56px 56px; - animation: loading-bar 5s infinite linear; -} -@keyframes loading-bar { - 100% { background-position: -56% 0 } -} - - - -.ce-plugin-image__button{ - cursor: pointer; - font-size: 1; - color: #8990AA; -} - .ce-plugin-image__button:hover{ - color: #393F52; - } - - - -/** Uploaded image */ -.ce-plugin-image__wrapper { - padding: 1em 0; -} - -.ce-image__wrapper--bordered { - border: 1px solid #eee; - box-sizing: border-box; -} - -.ce-plugin-image__uploaded--centered { - display: block; - max-width: 600px; - margin: 0 auto; -} - -.ce-plugin-image__uploaded--stretched { - width: 100%; -} - .ce-plugin-image__firstlevel--stretch { - margin: 0 !important; - max-width: none !important; - padding: 0 !important; - } - -.ce-plugin-image__caption { - max-width: 600px; - margin: .5em auto 0; - padding: .5em; - text-align: center; - color: #898a8c; - background: #fff; - border: 1px solid #ebeef3; - border-radius: 3px; - box-sizing: border-box; -} - - .ce-plugin-image__caption:empty::before { - content: 'Введите подпись'; - text-align: center; - font-weight: normal; - color: #a1a5b3;; - opacity: .9; - transition: opacity 200ms ease; - } - - .ce-plugin-image__caption:focus::before { - opacity: .1; - text-align: center; - } - -/** Settings */ -.ce_plugin_image--select_button{ - display: block; - color: #306ac7; - cursor: pointer; - line-height: 1.3em; -} - .ce_plugin_image--select_button:not(:last-of-type){ - margin-bottom: 1.5em; - } - .ce_plugin_image--select_button:hover{ - color: #a1b4ec; - } - -.ce-image-settings { - padding: 7px 0 -} -.ce-image-settings__item { - padding: 7px 15px; - cursor: pointer; -} - -.ce-settings-checkbox { - display: inline-block; - width: 17px; - background: #494361; - line-height: 0px; - border-radius: 14px; - padding: 2px; - vertical-align: text-top; - margin-right: 16px; - transition: background-color 200ms ease-out; - will-change: background-color; -} - -.ce-settings-checkbox__toggler { - display: inline-block; - width: 12px; - height: 12px; - border-radius: 50%; - background: #fff; - transition: margin 200ms ease-out; -} - -/** - * Setting is active - */ -.ce-image-settings__item--toggled { - color: #1FAA7E; -} - -.ce-image-settings__item--toggled .ce-settings-checkbox { - background-color: #14DC9E; -} - -.ce-image-settings__item--toggled .ce-settings-checkbox__toggler { - margin-left: 6px; -} \ No newline at end of file diff --git a/plugins/image/image.js b/plugins/image/image.js deleted file mode 100644 index b0b83746..00000000 --- a/plugins/image/image.js +++ /dev/null @@ -1,701 +0,0 @@ -/** - * Image plugin for codex-editor - * @author CodeX Team - * - * @version 1.3.0 - */ -var image = (function(image_plugin) { - - /** - * @private - * - * CSS classNames - */ - var elementClasses_ = { - - ce_image : 'ce-image', - loading : 'ce-plugin-image__loader', - blockStretched: 'ce-block--stretched', - uploadedImage : { - centered : 'ce-plugin-image__uploaded--centered', - stretched : 'ce-plugin-image__uploaded--stretched' - }, - imageCaption : 'ce-plugin-image__caption', - imageWrapper : 'ce-plugin-image__wrapper', - formHolder : 'ce-plugin-image__holder', - uploadButton : 'ce-plugin-image__button', - imagePreview : 'ce-image__preview', - selectorHolder: 'ce-settings-checkbox', - selectorButton: 'ce-settings-checkbox__toggler', - settingsItem: 'ce-image-settings__item', - imageWrapperBordered : 'ce-image__wrapper--bordered', - toggled : 'ce-image-settings__item--toggled' - - }; - - /** - * - * @private - * - * UI methods - */ - var ui_ = { - - holder : function(){ - - var element = document.createElement('DIV'); - - element.classList.add(elementClasses_.formHolder); - element.classList.add(elementClasses_.ce_image); - - return element; - }, - - uploadButton : function(){ - - var button = document.createElement('SPAN'); - - button.classList.add(elementClasses_.uploadButton); - - button.innerHTML = ' '; - button.innerHTML += 'Загрузить фотографию'; - - return button; - - }, - - /** - * @param {object} file - file path - * @param {string} style - css class - * @return {object} image - document IMG tag - */ - image : function(file, styles) { - - var image = document.createElement('IMG'); - - styles.map(function(item) { - image.classList.add(item); - }); - - image.src = file.url; - image.dataset.bigUrl = file.bigUrl; - - return image; - }, - - wrapper : function() { - - var div = document.createElement('div'); - - div.classList.add(elementClasses_.imageWrapper); - - return div; - }, - - caption : function() { - - var div = document.createElement('div'); - - div.classList.add(elementClasses_.imageCaption); - div.contentEditable = true; - - return div; - }, - /** - * Draws form for image upload - */ - makeForm : function() { - - var holder = ui_.holder(), - uploadButton = ui_.uploadButton(); - - holder.appendChild(uploadButton); - - uploadButton.addEventListener('click', uploadButtonClicked_, false ); - - image.holder = holder; - - return holder; - }, - - - /** - * wraps image and caption - * @param {object} data - image information - * @param {string} imageTypeClass - plugin's style - * @param {boolean} stretched - stretched or not - * @return wrapped block with image and caption - */ - makeImage : function(data, imageTypeClasses, stretched, bordered) { - - var file = data, - text = data.caption, - type = data.type, - image = ui_.image(file, imageTypeClasses), - caption = ui_.caption(), - wrapper = ui_.wrapper(); - - caption.innerHTML = text || ''; - - wrapper.dataset.stretched = stretched; - wrapper.dataset.bordered = bordered; - - /** Appeding to the wrapper */ - wrapper.appendChild(image); - wrapper.appendChild(caption); - - return wrapper; - }, - - /** - * @param {HTML} data - Rendered block with image - */ - getImage : function(data) { - - var image = data.querySelector('.' + elementClasses_.uploadedImage.centered) || - data.querySelector('.' + elementClasses_.uploadedImage.stretched); - - return image; - }, - - /** - * wraps image and caption - * @deprecated - * @param {object} data - image information - * @return wrapped block with image and caption - */ - centeredImage : function(data) { - - var file = data.file, - text = data.caption, - type = data.type, - image = ui_.image(file, elementClasses_.uploadedImage.centered), - caption = ui_.caption(), - wrapper = ui_.wrapper(); - - caption.textContent = text; - - wrapper.dataset.stretched = 'false'; - - /** Appeding to the wrapper */ - wrapper.appendChild(image); - wrapper.appendChild(caption); - - return wrapper; - }, - - /** - * wraps image and caption - * @deprecated - * @param {object} data - image information - * @return stretched image - */ - stretchedImage : function(data) { - - var file = data.file, - text = data.caption, - type = data.type, - image = ui_.image(file, elementClasses_.uploadedImage.stretched), - caption = ui_.caption(), - wrapper = ui_.wrapper(); - - caption.textContent = text; - - wrapper.dataset.stretched = 'true'; - - /** Appeding to the wrapper */ - wrapper.appendChild(image); - wrapper.appendChild(caption); - - return wrapper; - - } - - }; - - /** - * @private - * - * After render callback - */ - var uploadButtonClicked_ = function(event) { - - var url = image_plugin.config.uploadImage, - beforeSend = uploadingCallbacks_.ByClick.beforeSend, - success = uploadingCallbacks_.ByClick.success, - error = uploadingCallbacks_.ByClick.error; - - /** Define callbacks */ - codex.editor.transport.selectAndUpload({ - url : url, - multiple : false, - accept : 'image/*', - beforeSend : beforeSend, - success : success, - error : error - }); - }; - - var methods_ = { - - addSelectTypeClickListener : function(el, type) { - - el.addEventListener('click', function() { - - // el - settings element - - switch (type) { - case 'bordered': - methods_.toggleBordered(type, this); break; - case 'stretched': - methods_.toggleStretched(type, this); break; - } - - - }, false); - - }, - - toggleBordered : function(type, clickedSettingsItem ) { - - var current = codex.editor.content.currentNode, - blockContent = current.childNodes[0], - img = ui_.getImage(current), - wrapper = current.querySelector('.' + elementClasses_.imageWrapper); - - if (!img) { - return; - } - - /** - * Add classes to the IMG tag and to the Settings element - */ - img.classList.toggle(elementClasses_.imageWrapperBordered); - clickedSettingsItem.classList.toggle(elementClasses_.toggled); - - /** - * Save settings in dataset - */ - wrapper.dataset.bordered = img.classList.contains(elementClasses_.imageWrapperBordered); - - setTimeout(function() { - codex.editor.toolbar.settings.close(); - }, 200); - - }, - - toggleStretched : function( type, clickedSettingsItem ) { - - var current = codex.editor.content.currentNode, - blockContent = current.childNodes[0], - img = ui_.getImage(current), - wrapper = current.querySelector('.' + elementClasses_.imageWrapper); - - if (!img) { - return; - } - - /** Clear classList */ - blockContent.classList.add(elementClasses_.blockStretched); - img.classList.toggle(elementClasses_.uploadedImage.stretched); - img.classList.toggle(elementClasses_.uploadedImage.centered); - - clickedSettingsItem.classList.toggle(elementClasses_.toggled); - - wrapper.dataset.stretched = img.classList.contains(elementClasses_.uploadedImage.stretched); - - setTimeout(function() { - codex.editor.toolbar.settings.close(); - }, 1000); - - } - }; - - /** - * @private - * Callbacks - */ - var uploadingCallbacks_ = { - - ByClick : { - - /** - * Before sending ajax request - */ - beforeSend : function() { - - var input = codex.editor.transport.input, - files = input.files; - - var validFileExtensions = ["jpg", "jpeg", "bmp", "gif", "png"]; - - var type = files[0].type.split('/'); - - var result = validFileExtensions.some(function(ext) { - return ext == type[1]; - }); - - if (!result) { - return; - } - - var reader = new FileReader(); - reader.readAsDataURL(files[0]); - - reader.onload = function(e) { - - var data = { - background : false, - border : false, - isstretch : false, - url : e.target.result, - bigUrl : null, - width : null, - height : null, - additionalData : null, - caption : '', - cover : null - }; - - var newImage = make_(data); - - codex.editor.content.switchBlock(image.holder, newImage, 'image'); - newImage.classList.add(elementClasses_.imagePreview); - - /** - * Change holder to image - */ - image.holder = newImage; - }; - - }, - - /** Photo was uploaded successfully */ - success : function(result) { - - var parsed = JSON.parse(result), - data, - currentBlock = codex.editor.content.currentNode; - - /** - * Preparing {Object} data to draw an image - * @uses ceImage.make method - */ - data = parsed.data; - - image.holder.classList.remove(elementClasses_.imagePreview); - - /** - * Change src of image - */ - var newImage = image.holder.getElementsByTagName('IMG')[0]; - - newImage.src = parsed.data.url; - newImage.dataset.bigUrl = parsed.data.bigUrl; - newImage.dataset.width = parsed.data.width; - newImage.dataset.height = parsed.data.height; - newImage.dataset.additionalData = parsed.data.additionalData; - - }, - - /** Error callback. Sends notification to user that something happend or plugin doesn't supports method */ - error : function(result) { - - var oldHolder = image.holder; - var form = ui_.makeForm(); - - codex.editor.content.switchBlock(oldHolder, form, 'image'); - - } - }, - - ByPaste : { - - /** - * Direct upload - * Any URL that contains image extension - * @param url - */ - uploadImageFromUrl : function(path) { - - var image, - current = codex.editor.content.currentNode, - beforeSend, - success_callback; - - /** When image is uploaded to redactors folder */ - success_callback = function(data) { - - var imageInfo = JSON.parse(data); - - var newImage = image.getElementsByTagName('IMG')[0]; - - newImage.dataset.stretched = false; - newImage.dataset.src = imageInfo.url; - newImage.dataset.bigUrl = imageInfo.bigUrl; - newImage.dataset.width = imageInfo.width; - newImage.dataset.height = imageInfo.height; - newImage.dataset.additionalData = imageInfo.additionalData; - - image.classList.remove(elementClasses_.imagePreview); - - }; - - /** Before sending XMLHTTP request */ - beforeSend = function() { - - var content = current.querySelector('.ce-block__content'); - - var data = { - background: false, - border: false, - isStretch: false, - file: { - url: path, - bigUrl: null, - width: null, - height: null, - additionalData: null - }, - caption: '', - cover: null - }; - - image = codex.editor.tools.image_extended.render(data); - - image.classList.add(elementClasses_.imagePreview); - - var img = image.querySelector('img'); - - codex.editor.content.switchBlock(codex.editor.content.currentNode, image, 'image'); - - }; - - /** Preparing data for XMLHTTP */ - var data = { - url: image_plugin.config.uploadFromUrl, - type: "POST", - data : { - url: path - }, - beforeSend : beforeSend, - success : success_callback - }; - - codex.editor.core.ajax(data); - } - - } - }; - - /** - * Image path - * @type {null} - */ - image_plugin.path = null; - - /** - * Plugin configuration - */ - image_plugin.config = null; - - /** - * - * @private - * - * @param data - * @return {*} - * - */ - var make_ = function ( data ) { - - var holder, - classes = []; - - if (data) { - - if (data.border) { - classes.push(elementClasses_.imageWrapperBordered); - } - - if ( data.isstretch || data.isstretch === 'true') { - - classes.push(elementClasses_.uploadedImage.stretched); - holder = ui_.makeImage(data, classes, 'true', data.border); - - } else { - - classes.push(elementClasses_.uploadedImage.centered); - holder = ui_.makeImage(data, classes, 'false', data.border); - - } - - return holder; - - } else { - - holder = ui_.makeForm(); - - return holder; - } - }; - - /** - * @private - * - * Prepare clear data before save - * - * @param data - */ - var prepareDataForSave_ = function(data) { - - }; - - /** - * @public - * @param config - */ - image_plugin.prepare = function(config) { - - image_plugin.config = config; - - return Promise.resolve(); - }; - - /** - * @public - * - * this tool works when tool is clicked in toolbox - */ - image_plugin.appendCallback = function(event) { - - /** Upload image and call success callback*/ - uploadButtonClicked_(event); - - }; - - /** - * @public - * - * @param data - * @return {*} - */ - image_plugin.render = function( data ) { - - return make_(data); - }; - - /** - * @public - * - * @param block - * @return {{background: boolean, border: boolean, isstretch: boolean, file: {url: (*|string|Object), bigUrl: (null|*), width: *, height: *, additionalData: null}, caption: (string|*|string), cover: null}} - */ - image_plugin.save = function ( block ) { - - var content = block, - image = ui_.getImage(content), - caption = content.querySelector('.' + elementClasses_.imageCaption); - - var data = { - background : false, - border : content.dataset.bordered === 'true' ? true : false, - isstretch : content.dataset.stretched === 'true' ? true : false, - // file : { - url : image.dataset.src || image.src, - bigUrl : image.dataset.bigUrl, - width : image.width, - height : image.height, - additionalData :null, - // }, - caption : caption.innerHTML || '', - cover : null - }; - - return data; - }; - - /** - * @public - * - * Settings panel content - * @return {Element} element contains all settings - */ - image_plugin.makeSettings = function () { - - var currentNode = codex.editor.content.currentNode, - wrapper = currentNode.querySelector('.' + elementClasses_.imageWrapper), - holder = document.createElement('DIV'), - types = { - stretched : "На всю ширину", - bordered : "Добавить рамку" - }, - currentImageWrapper = currentNode.querySelector('.' + elementClasses_.imageWrapper ), - currentImageSettings = currentImageWrapper.dataset; - - /** Add holder classname */ - holder.className = 'ce-image-settings'; - - /** Now add type selectors */ - for (var type in types){ - - /** - * Settings template - */ - var settingsItem = document.createElement('DIV'), - selectorsHolder = document.createElement('SPAN'), - selectorsButton = document.createElement('SPAN'); - - settingsItem.classList.add(elementClasses_.settingsItem); - selectorsHolder.classList.add(elementClasses_.selectorHolder); - selectorsButton.classList.add(elementClasses_.selectorButton); - - selectorsHolder.appendChild(selectorsButton); - settingsItem.appendChild(selectorsHolder); - - selectTypeButton = document.createTextNode(types[type]); - settingsItem.appendChild(selectTypeButton); - - /** - * Activate previously selected settings - */ - if ( currentImageSettings[type] == 'true' ){ - settingsItem.classList.add(elementClasses_.toggled); - } - - methods_.addSelectTypeClickListener(settingsItem, type); - - holder.appendChild(settingsItem); - - } - - return holder; - - }; - - /** - * Share as API - */ - image_plugin.uploadImageFromUri = uploadingCallbacks_.ByPaste.uploadImageFromUrl; - - image_plugin.pastePatterns = [ - { - type: 'image', - regex: /(?:([^:\/?#]+):)?(?:\/\/([^\/?#]*))?([^?#]*\.(?:jpe?g|gif|png))(?:\?([^#]*))?(?:#(.*))?/i, - callback: image_plugin.uploadImageFromUri - }, - { - type: 'uploadCare', - regex: /^https:\/\/(uploadcare\.cmtt\.ru|ucarecdn\.com|static[0-9]+\.siliconrus\.cmtt\.ru|static[0-9]+\.cmtt\.ru)/i, - callback: image_plugin.uploadImageFromUri - } ]; - - image_plugin.destroy = function () { - - image = null; - - }; - - return image_plugin; - -})({}); \ No newline at end of file diff --git a/plugins/instagram/instagram.css b/plugins/instagram/instagram.css deleted file mode 100644 index 5d4f265f..00000000 --- a/plugins/instagram/instagram.css +++ /dev/null @@ -1,10 +0,0 @@ -.ce-redactor .instagram { - width: 100%; - max-width: 650px; - margin: 10px auto; -} - -.instagram__loader { - background: url("loading.gif") !important; - opacity: 0.1; -} \ No newline at end of file diff --git a/plugins/instagram/instagram.js b/plugins/instagram/instagram.js deleted file mode 100644 index 9072ef2d..00000000 --- a/plugins/instagram/instagram.js +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Instagram plugin - * @version 1.0.0 - */ -var instagram = (function(instagram_plugin) { - - var methods = { - - render : function(content) { - - codex.editor.content.switchBlock(codex.editor.content.currentNode, content); - - setTimeout(function() { - window.instgrm.Embeds.process(); - }, 200); - - }, - - /** - * Drawing html content. - * - * @param url - * @returns {Element} blockquote - HTML template for Instagram Embed JS - */ - instagramBlock : function(url) { - - var blockquote = codex.editor.draw.node('BLOCKQUOTE', 'instagram-media instagram', {}), - div = codex.editor.draw.node('DIV', '', {}), - paragraph = codex.editor.draw.node('P', 'ce-paste__instagram--p', {}), - anchor = codex.editor.draw.node('A', '', { href : url }); - - blockquote.dataset.instgrmVersion = 4; - - paragraph.appendChild(anchor); - div.appendChild(paragraph); - blockquote.appendChild(div); - - return blockquote; - - } - }; - - /** - * Prepare before usage - * Load important scripts to render embed - */ - instagram_plugin.prepare = function() { - - return new Promise(function(resolve, reject){ - - codex.editor.core.importScript("https://platform.instagram.com/en_US/embeds.js", 'instagram-api').then(function(){ - resolve(); - }).catch(function(){ - reject(Error('Instagram API was not loaded')); - }); - - }); - }; - - /** - * @private - * - * Make instagram embed via Widgets method - */ - var make_ = function(data, isInternal) { - - if (!data.instagram_url) - return; - - var block = methods.instagramBlock(data.instagram_url); - - if (isInternal) { - - setTimeout(function() { - - /** Render block */ - methods.render(block); - - }, 200); - } - - if (!isInternal) { - methods.render(block); - } - - return block; - }; - - instagram_plugin.validate = function(data) { - return true; - }; - - /** - * Saving JSON output. - * Upload data via ajax - */ - instagram_plugin.save = function(blockContent) { - - var data; - - if (!blockContent) - return; - - /** Example */ - data = { - instagram_url: blockContent.src - }; - - return data; - - }; - - instagram_plugin.validate = function(data) { - - var checkUrl = new RegExp("http?.+instagram.com\/p?."); - - if (!data.instagram_url || checkUrl.exec(data.instagram_url).length == 0) - return; - - return true; - }; - - /** - * Render data - */ - instagram_plugin.render = function(data) { - return make_(data); - }; - - /** - * callback for instagram url's coming from pasteTool - * Using instagram Embed Widgete to render - * @param url - */ - instagram_plugin.urlPastedCallback = function(url) { - var data = { - instagram_url: url - }; - - make_(data, true); - - }; - - instagram_plugin.pastePatterns = [ - { - type: 'instagram', - regex: /http?.+instagram.com\/p\/([a-zA-Z0-9]*)\S*/, - callback: instagram_plugin.urlPastedCallback - } - ]; - - instagram_plugin.destroy = function () { - - instagram = null; - delete window.instgrm - - }; - - return instagram_plugin; - -})({}); - diff --git a/plugins/instagram/loading.gif b/plugins/instagram/loading.gif deleted file mode 100644 index 72ea7ccb..00000000 Binary files a/plugins/instagram/loading.gif and /dev/null differ diff --git a/plugins/link/link.css b/plugins/link/link.css deleted file mode 100644 index 24f1c3b2..00000000 --- a/plugins/link/link.css +++ /dev/null @@ -1,86 +0,0 @@ -.ce-link { - padding: 0.7em 0 !important; - border-radius: 3px; -} - -.clearfix:after { - visibility: hidden; - display: block; - font-size: 0; - content: " "; - clear: both; - height: 0; -} - -.ceditor-tool-link-input { - outline: none; - border: 0; - width: 100%; - background: transparent; - font-size: 1em; - padding: 8px 25px; - transition: background 200ms; - border-left: 3px solid #65d8b3; -} - -.tool-link-panel { - position: relative; - margin: 5px 0; - background: #f8f7ef; - border: 1px solid transparent; - padding: 25px 30px; -} - -.tool-link-image { - float:right; - width: 75px; - border-radius: 3px; -} - -.tool-link-title { - display: block; - width: 340px; - margin-bottom: 4px; - line-height: 1.2em; - font-size: 20px; - font-weight: 700; - color: #000; -} - -.tool-link-description { - display: block; - margin-top: 10px; - font-size: 14px; - color: #000; -} - -.tool-link-link { - width: 360px; - font-size: 10px; - margin-bottom: 4px; - letter-spacing: 1px; - overflow: hidden; - text-transform: uppercase; - text-decoration: none; - color: rgba(165,156,86,.8); -} - -.tool-link-loader { - background-color: transparent; - background-image: repeating-linear-gradient(-45deg, transparent, transparent 4px, #f5f9ff 4px, #eaedef 8px) !important; - background-size: 56px 56px; - animation: loading-bar 5s infinite linear; -} -@keyframes loading-bar { - 100% { background-position: -56% 0 } -} - - - -.tool-link-error { - background: rgb(255, 241, 241); - color: #bf4747; -} -.tool-link-error .ceditor-tool-link-input { - border-left-color: #d86b6b -} diff --git a/plugins/link/link.js b/plugins/link/link.js deleted file mode 100644 index 2763015f..00000000 --- a/plugins/link/link.js +++ /dev/null @@ -1,350 +0,0 @@ -/** - * Created by nostr on 29.06.16. - */ - -/** - * Link tool plugin - */ - -var link = (function(link_plugin) { - - var settings = { - defaultText : 'Вставьте ссылку ...', - ENTER_KEY : 13, - currentBlock : null, - currentInput : null, - elementClasses : { - link: "tool-link-link", - image: "tool-link-image", - title: "tool-link-title", - description: "tool-link-description", - loader: "tool-link-loader", - error: "tool-link-error" - } - }; - - var ui = { - - make : function (json) { - - var wrapper = ui.wrapper(), - siteImage = ui.image(json.image, settings.elementClasses.image), - siteTitle = ui.title(json.title), - siteDescription = ui.description(json.description), - siteLink = ui.link(json.url, json.url); - - wrapper.appendChild(siteImage); - wrapper.appendChild(siteTitle); - wrapper.appendChild(siteLink); - wrapper.appendChild(siteDescription); - - siteTitle.contentEditable = true; - siteDescription.contentEditable = true; - - return wrapper; - - }, - - mainBlock : function () { - - var wrapper = document.createElement('div'); - - wrapper.classList.add("ceditor-tool-link"); - - return wrapper; - - }, - - input : function () { - - var inputTag = document.createElement('input'); - - inputTag.classList.add("ceditor-tool-link-input"); - - inputTag.placeholder = settings.defaultText; - - inputTag.contentEditable = false; - - return inputTag; - - }, - - wrapper : function () { - - var wrapper = document.createElement('div'); - - wrapper.classList.add('tool-link-panel', 'clearfix'); - - return wrapper; - - }, - - image : function (imageSrc, imageClass) { - - var imageTag = document.createElement('img'); - - imageTag.classList.add(imageClass); - - imageTag.setAttribute('src', imageSrc); - - return imageTag; - - }, - - link : function (linkUrl, linkText) { - - var linkTag = document.createElement('a'); - - linkTag.classList.add(settings.elementClasses.link); - - linkTag.href = linkUrl; - - linkTag.target = "_blank"; - - linkTag.innerText = linkText; - - return linkTag; - - }, - - title : function (titleText) { - - var titleTag = document.createElement('div'); - - titleTag.classList.add("tool-link-content", settings.elementClasses.title); - - titleTag.innerHTML = titleText; - - return titleTag; - }, - - description : function (descriptionText) { - - var descriptionTag = document.createElement('div'); - - descriptionTag.classList.add("tool-link-content", settings.elementClasses.description); - - descriptionTag.innerHTML = descriptionText; - - return descriptionTag; - } - - }; - - var methods = { - - blockPasteCallback : function (event) { - - var clipboardData = event.clipboardData || window.clipboardData, - pastedData = clipboardData.getData('Text'), - block = event.target.parentNode; - - methods.renderLink(pastedData, block); - - event.stopPropagation(); - - }, - - blockKeyDownCallback : function (event) { - - var inputTag = event.target, - block = inputTag.parentNode, - url; - - if ( block.classList.contains(settings.elementClasses.error) ) { - block.classList.remove(settings.elementClasses.error); - } - - if (event.keyCode == settings.ENTER_KEY) { - - url = inputTag.value; - - methods.renderLink(url, block); - - event.preventDefault(); - } - }, - - renderLink : function (url, block) { - - Promise.resolve() - - .then(function () { - return methods.urlify(url); - }) - - .then(function (url) { - - /* Show loader gif **/ - block.classList.add(settings.elementClasses.loader); - - return fetch( link_plugin.config.fetchUrl + '?url=' + encodeURI(url) ); - }) - - .then(function (response) { - - if (response.status == "200"){ - - return response.json(); - - } else { - - return Error("Invalid response status: %o", response); - - } - - }) - - .then(function (json) { - methods.composeLinkPreview(json, block); - }) - - .catch(function(error) { - - /* Hide loader gif **/ - block.classList.remove(settings.elementClasses.loader); - - block.classList.add(settings.elementClasses.error); - - codex.editor.core.log('Error while doing things with link paste: %o', 'error', error); - }); - - }, - - urlify : function (text) { - - var urlRegex = /(https?:\/\/\S+)/g; - - var links = text.match(urlRegex); - - if (links) { - return links[0]; - } - - return Promise.reject(Error("Url is not matched")); - - }, - - composeLinkPreview : function (json, currentBlock) { - - if (json == {}) { - return; - } - - var previewBlock = ui.make(json); - - settings.currentInput.remove(); - - currentBlock.appendChild(previewBlock); - - currentBlock.classList.remove(settings.elementClasses.loader); - - } - }; - - link_plugin.prepare = function (config) { - - link_plugin.config = config; - - return Promise.resolve(); - - }; - - /** - * Make initial header block - * @param {object} JSON with block data - * @return {Element} element to append - */ - link_plugin.makeNewBlock = function (data) { - - var wrapper = ui.mainBlock(), - tag = ui.input(); - - settings.currentInput = tag; - - wrapper.appendChild(tag); - - wrapper.classList.add('ce-link'); - /** - * Bind callbacks - **/ - tag.addEventListener('paste', methods.blockPasteCallback, false); - tag.addEventListener('keydown', methods.blockKeyDownCallback, false); - - return wrapper; - - }; - - /** - * Method to render HTML block from JSON - */ - link_plugin.render = function (json) { - - if ( json ) { - - var block = ui.mainBlock(), - tag = ui.make(json); - - block.classList.add('ce-link'); - block.appendChild(tag); - - return block; - - } else { - - var wrapper = ui.mainBlock(), - tag = ui.input(); - - settings.currentInput = tag; - - wrapper.appendChild(tag); - - wrapper.classList.add('ce-link'); - /** - * Bind callbacks - **/ - tag.addEventListener('paste', methods.blockPasteCallback, false); - tag.addEventListener('keydown', methods.blockKeyDownCallback, false); - - return wrapper; - } - - - }; - - link_plugin.validate = function (data) { - - if (data.url.trim() == '' || data.title.trim() == '' || data.description.trim() == '') - return; - - return true; - }; - - /** - * Method to extract JSON data from HTML block - */ - link_plugin.save = function (blockContent){ - - var linkElement = settings.elementClasses.link; - - var data = { - url : blockContent.querySelector("." + linkElement).href, - shortLink : blockContent.querySelector("." + linkElement).textContent || '', - image : blockContent.querySelector("." + settings.elementClasses.image).src || '', - title : blockContent.querySelector("." + settings.elementClasses.title).textContent || '', - description : blockContent.querySelector("." + settings.elementClasses.description).textContent || '' - }; - - return data; - - }; - - link_plugin.destroy = function () { - - link = null; - - }; - - return link_plugin; - -})({}); diff --git a/plugins/list/list.css b/plugins/list/list.css deleted file mode 100644 index ce714260..00000000 --- a/plugins/list/list.css +++ /dev/null @@ -1,45 +0,0 @@ -.cdx-plugin-list { - margin: 0; - padding: .5em 0; - line-height: 1.7em; -} - -ul.cdx-plugin-list { - list-style-type: disc; -} - -ol.cdx-plugin-list { - list-style-type: decimal; -} - -.cdx-plugin-list__li { - display: list-item; - background: #fff; - border: 1px solid #ebeef3; - border-radius: 3px; - margin: .5em; - padding: .5em; - list-style-position: outside; - list-style-type: inherit; - margin-left: 1.1em; -} - -.cdx-plugin-list p { - margin: 0.6em 0; -} - -.cdx-plugin-list p:first-of-type { - margin-top: 0; -} - -.cdx-plugin-list p:last-of-type { - margin-bottom: 0; -} - -/** -* List style settigns icons -*/ -.ce_plugin_list--select_button i{ - margin-right: 1.3em; -} - diff --git a/plugins/list/list.js b/plugins/list/list.js deleted file mode 100644 index 5cdef435..00000000 --- a/plugins/list/list.js +++ /dev/null @@ -1,241 +0,0 @@ -/** - * Code Plugin\ - * Creates code tag and adds content to this tag - */ -var list = (function(list_plugin) { - - /** - * CSS class names - */ - var elementClasses_ = { - pluginWrapper: 'cdx-plugin-list', - li: 'cdx-plugin-list__li', - settings: 'cdx-plugin-list__settings', - settingsItem: 'cdx-plugin-settings__item' - }; - - var LIST_ITEM_TAG = 'LI'; - - var ui = { - - make: function (blockType) { - - var wrapper = this.block(blockType || 'UL', elementClasses_.pluginWrapper); - - wrapper.dataset.type = blockType; - wrapper.contentEditable = true; - - wrapper.addEventListener('keydown', methods_.keyDown); - - return wrapper; - - }, - - block: function (blockType, blockClass) { - - var block = document.createElement(blockType); - - if (blockClass) block.classList.add(blockClass); - - return block; - - }, - - button: function (buttonType) { - - var types = { - unordered: 'Обычный', - ordered: 'Нумерованный' - }, - button = document.createElement('DIV'); - - button.innerHTML = types[buttonType]; - - button.classList.add(elementClasses_.settingsItem); - - return button; - } - }; - - var methods_ = { - - /** - * Changes block type => OL or UL - * @param event - * @param blockType - */ - changeBlockStyle : function (event, blockType) { - - var currentBlock = codex.editor.content.currentNode, - newEditable = ui.make(blockType), - oldEditable = currentBlock.querySelector("[contenteditable]"); - - newEditable.dataset.type = blockType; - newEditable.innerHTML = oldEditable.innerHTML; - newEditable.classList.add(elementClasses_.pluginWrapper); - - codex.editor.content.switchBlock(currentBlock, newEditable, 'list'); - }, - keyDown: function (e) { - - var controlKeyPressed = e.ctrlKey || e.metaKey, - keyCodeForA = 65; - - /** - * If CTRL+A (CMD+A) was pressed, we should select only one list item, - * not all
    or - */ - if (controlKeyPressed && e.keyCode == keyCodeForA) { - - e.preventDefault(); - - /** - * Select
  1. content - */ - methods_.selectListItem(); - - } - - }, - - /** - * Select all content of
  2. with caret - */ - selectListItem : function () { - - var selection = window.getSelection(), - currentSelectedNode = selection.anchorNode.parentNode, - range = new Range(); - - /** - * Search for
  3. element - */ - while ( currentSelectedNode && currentSelectedNode.tagName != LIST_ITEM_TAG ) { - - currentSelectedNode = currentSelectedNode.parentNode; - - } - - range.selectNodeContents(currentSelectedNode); - - selection.removeAllRanges(); - selection.addRange(range); - - } - }; - - /** - * Method to render HTML block from JSON - */ - list_plugin.render = function (data) { - - var type = data && (data.type == 'ordered' || data.type == 'OL') ? 'OL' : 'UL', - tag = ui.make(type), - newLi; - - if (data && data.items) { - - data.items.forEach(function (element, index, array) { - - newLi = ui.block('li', elementClasses_.li); - - newLi.innerHTML = element || ''; - - tag.appendChild(newLi); - - }); - - } else { - - newLi = ui.block('li', elementClasses_.li); - - tag.appendChild(newLi); - - } - - return tag; - - }; - - list_plugin.validate = function(data) { - - var isEmpty = data.items.every(function(item){ - return item.trim() === ''; - }); - - if (isEmpty){ - return; - } - - if (data.type != 'UL' && data.type != 'OL'){ - console.warn('CodeX Editor List-tool: wrong list type passed %o', data.type); - return; - } - - return true; - }; - - /** - * Method to extract JSON data from HTML block - */ - list_plugin.save = function (blockContent){ - - var data = { - type : null, - items : [] - }, - litsItemContent = '', - isEmptyItem = false; - - for (var index = 0; index < blockContent.childNodes.length; index++){ - - litsItemContent = blockContent.childNodes[index].innerHTML; - isEmptyItem = !blockContent.childNodes[index].textContent.trim(); - - if (!isEmptyItem) { - data.items.push(litsItemContent); - } - } - - data.type = blockContent.dataset.type; - - return data; - - }; - - list_plugin.makeSettings = function () { - - var holder = document.createElement('DIV'); - - /** Add holder classname */ - holder.className = elementClasses_.settings; - - var orderedButton = ui.button("ordered"), - unorderedButton = ui.button("unordered"); - - orderedButton.addEventListener('click', function (event) { - methods_.changeBlockStyle(event, 'OL'); - codex.editor.toolbar.settings.close(); - }); - - unorderedButton.addEventListener('click', function (event) { - methods_.changeBlockStyle(event, 'UL'); - codex.editor.toolbar.settings.close(); - }); - - holder.appendChild(orderedButton); - holder.appendChild(unorderedButton); - - return holder; - - }; - - list_plugin.destroy = function () { - - list = null; - - }; - - return list_plugin; - -})({}); diff --git a/plugins/paragraph/paragraph.css b/plugins/paragraph/paragraph.css deleted file mode 100644 index 7651db45..00000000 --- a/plugins/paragraph/paragraph.css +++ /dev/null @@ -1,34 +0,0 @@ -/** -* Empty paragraph placeholder -*/ - -.ce-paragraph { - padding: 0.7em 0 !important; - line-height: 1.7em; -} - -.ce-paragraph:empty::before, -.ce-paragraph p:empty::before{ - content : attr(data-placeholder); - color: #818BA1; - opacity: .7; - transition: opacity 200ms ease; -} -.ce-paragraph:focus::before{ - opacity: .1; -} - -.toolbar-opened .ce-paragraph::before { - display: none; -} - - -.ce-paragraph p { - margin: 1.2em 0; -} -.ce-paragraph p:first-of-type{ - margin-top: 0; -} -.ce-paragraph p:last-of-type{ - margin-bottom: 0; -} \ No newline at end of file diff --git a/plugins/paragraph/paragraph.js b/plugins/paragraph/paragraph.js deleted file mode 100644 index bbd5cd47..00000000 --- a/plugins/paragraph/paragraph.js +++ /dev/null @@ -1,132 +0,0 @@ -/** -* Paragraph Plugin -* Creates DIV tag and adds content to this tag -*/ - -var paragraph = (function(paragraph_plugin) { - - /** - * @private - * - * Make initial paragraph block - * @param {object} JSON with block data - * @return {Element} element to append - */ - - var make_ = function (data) { - - /** Create Empty DIV */ - var tag = codex.editor.draw.node('DIV', ['ce-paragraph'], {}); - - if (data && data.text) { - tag.innerHTML = data.text; - } - - tag.contentEditable = true; - - return tag; - - }; - - /** - * @private - * - * Handles input data for save - * @param data - */ - var prepareDataForSave_ = function(data) { - - }; - - /** - * @public - * - * Plugins should have prepare method - * @param config - */ - paragraph_plugin.prepare = function(config) { - - }; - - /* - * @public - * - * Method to render HTML block from JSON - */ - paragraph_plugin.render = function (data) { - - return make_(data); - - }; - - /** - * @public - * - * Check output data for validity. - * Should be defined by developer - */ - paragraph_plugin.validate = function(output) { - - let text = output.text; - - text = text.replace(' ', ' '); - text = text.replace(/\s/g, ' '); - text = text.trim(); - - /** - * Check for empty

    : - *

    - */ - let div = document.createElement('div'); - div.innerHTML = text; - - text = div.textContent.trim(); - - if (!text) { - return false; - } - - - return output; - }; - - /** - * @public - * - * Method to extract JSON data from HTML block - */ - paragraph_plugin.save = function (blockContent){ - - var wrappedText = codex.editor.content.wrapTextWithParagraphs(blockContent.innerHTML), - sanitizerConfig = { - tags : { - p : {}, - a: { - href: true, - target: '_blank', - rel: 'nofollow' - }, - i: {}, - b: {}, - } - }; - - var data = { - "text": codex.editor.sanitizer.clean(wrappedText, sanitizerConfig), - "format": "html", - "introText": '<>' - }; - - return data; - - }; - - paragraph_plugin.destroy = function () { - - paragraph = null; - - }; - - return paragraph_plugin; - -})({}); diff --git a/plugins/quote/img/codex.png b/plugins/quote/img/codex.png deleted file mode 100644 index 68f53217..00000000 Binary files a/plugins/quote/img/codex.png and /dev/null differ diff --git a/plugins/quote/img/upload.png b/plugins/quote/img/upload.png deleted file mode 100644 index 7d28e986..00000000 Binary files a/plugins/quote/img/upload.png and /dev/null differ diff --git a/plugins/quote/quote.css b/plugins/quote/quote.css deleted file mode 100644 index 3439e797..00000000 --- a/plugins/quote/quote.css +++ /dev/null @@ -1,210 +0,0 @@ -.ce_plugin_quote--select_button{ - display: block; - color: #306ac7; - cursor: pointer; - line-height: 1.3em; -} -.ce_plugin_quote--select_button:not(:last-of-type){ - margin-bottom: 1.5em; -} -.ce_plugin_quote--select_button:hover{ - color: #a1b4ec; -} - - -/** Quote Styles */ - -.quoteStyle-withCaption--author:empty { - direction: rtl; -} -.quoteStyle-withCaption--author { - margin: 5px 0; - background: #fff; - border: 1px solid #ebeef3; - padding: 7px; - text-align: right; - font-size: 1em; - font-weight: bold; - line-height: 1.5em; - -} -.quoteStyle-simple--text, -.quoteStyle-withCaption--blockquote, -.quoteStyle-withPhoto--quote { - /*font-size: 1.04em; - line-height: 1.9em;*/ -} - -.quoteStyle-simple--text { - padding: 1.2em 2.1em; - margin: 2.5em 2em; - background: #FBFBFB; -} - -.quoteStyle-withCaption--blockquote { - margin: 0; - padding: 1.5em 2.0em !important; - border: 1px solid #ebeef3; - background: #fff; -} - -.quoteStyle-withCaption--blockquote:focus, -.quoteStyle-withCaption--author:focus { - outline: none; -} - -.quoteStyle-simple--text:empty::before, -.quoteStyle-withCaption--blockquote:empty::before, -.quoteStyle-withPhoto--quote:empty::before -{ - content : 'Введите цитату'; - color: #818BA1; - opacity: 0.7; - transition: opacity 200ms ease; -} - -.quoteStyle-withCaption--author:empty::before -{ - content : 'Введите имя автора'; - font-weight: normal; - color: #a1a5b3;; - opacity: 1; - transition: opacity 200ms ease; -} -.quoteStyle-withPhoto--author:empty::before { - content : 'Введите имя автора'; - color: #000; - opacity: .8; - transition: opacity 200ms ease; -} - -.quoteStyle-simple--text:focus::before, -.quoteStyle-withCaption--blockquote:focus::before, -.quoteStyle-withPhoto--quote:focus::before -{ - opacity: .1; -} - -.quoteStyle-withCaption--author:focus::before, -.quoteStyle-withPhoto--author:focus::before -{ - opacity: .1; -} - -/** Quote with photo */ -.ce_redactor .quoteStyle-withPhoto--wrapper { - margin: 0; - padding: 4.5em 3.6em !important; -} - -.quoteStyle-withPhoto--photo { - width: 94px; - height: 94px; - overflow: hidden; - text-align: center; - line-height: 94px; - border-radius: 3px; - float: left; - margin-right: 10px; - border: 1px solid #ebeef3; - box-sizing: border-box; - background: #fff; -} -.quoteStyle-withPhoto--photo:hover { - cursor: pointer; - background: #F0F3F6; -} - -.quoteStyle-withPhoto--photo:hover::after { - display: block; -} - -.authorsPhoto { - height: 110%; - vertical-align: top; -} - -.authorsPhoto-wrapper { - border: 0 !important; - transition: all 20ms ease-in; - will-change: opacity, filter; -} - -.authorsPhotoWrapper_preview { - opacity: .5; - filter: blur(1.7px) grayscale(1); -} - -.quoteStyle-withPhoto--photo .ce-icon-picture { - font-family: "codex_editor"; - font-size: 1.5em; - color: #d5dbea; -} -.quoteStyle-withPhoto--photo:hover{ - background: #fdfeff; - border-color: #c1c4cc; -} -.quoteStyle-withPhoto--photo:hover .ce-icon-picture { - color: #2b3142; -} - -.quoteStyle-withPhoto--author { - outline: none; - overflow: hidden; - margin-bottom: 10px; - font-weight: bold; - color: #000; -} -.quoteStyle-withPhoto--job { - outline: none; - overflow: hidden; -} - -.quoteStyle-withPhoto--job:empty::before { - content : 'Введите должность автора'; - color: #A5ABBC; - opacity: 1; - transition: opacity 200ms ease; -} - -.quoteStyle-withPhoto--job:focus::before { - opacity: .1; -} - -.quoteStyle-withPhoto--quote { - margin-bottom: 10px; - line-height: 1.7em; - outline: none; - overflow: hidden; -} - -/** -* New styles -*/ -.ce-quote{ - margin: 0 !important; - padding: 15px 0; -} -.quoteStyle-withPhoto--author, -.quoteStyle-withPhoto--job, -.quoteStyle-withPhoto--quote{ - background: #fff; - border: 1px solid #ebeef3; - border-radius: 3px; - padding: 10px 15px; -} - -.quoteStyle-withPhoto--author, -.quoteStyle-withPhoto--job { - line-height: 20px; -} -.quoteStyle-withPhoto--quote{ - min-height: 200px; -} - -/** - * Current select-type button - */ -.ce-quote-settings--selected{ - font-weight: bold; -} \ No newline at end of file diff --git a/plugins/quote/quote.js b/plugins/quote/quote.js deleted file mode 100644 index 45c7a8b6..00000000 --- a/plugins/quote/quote.js +++ /dev/null @@ -1,585 +0,0 @@ -/** - * - * Quote plugin - */ - -var quote = (function(quote_plugin) { - - /** - * @private - * - * CSS styles - */ - var elementClasses_ = { - - ce_quote : 'ce-quote', - quoteText : 'ce_quote--text', - quoteAuthor : 'ce_quote--author', - authorsJob : 'ce_quote--job', - authorsPhoto : 'authorsPhoto', - authorsPhotoWrapper : 'authorsPhoto-wrapper', - authorsPhotoWrapper_preview : 'authorsPhotoWrapper_preview', - - simple : { - text : 'quoteStyle-simple--text' - }, - - withCaption : { - blockquote : 'quoteStyle-withCaption--blockquote', - author : 'quoteStyle-withCaption--author' - }, - - withPhoto : { - photo : 'quoteStyle-withPhoto--photo', - author : 'quoteStyle-withPhoto--author', - job : 'quoteStyle-withPhoto--job', - quote : 'quoteStyle-withPhoto--quote', - wrapper : 'quoteStyle-withPhoto--wrapper', - authorHolder : 'quoteStyle-withPhoto--authorWrapper' - }, - - settings : { - holder : 'cdx-plugin-settings--horisontal', - caption : 'ce_plugin_quote--caption', - buttons : 'cdx-plugin-settings__item', - selectedType : 'ce-quote-settings--selected' - } - }; - - /** - * @private - * - * - */ - var methods_ = { - - changeStyleClicked : function() { - - var changeStyleButton = this, - quote = codex.editor.content.currentNode.querySelector('.' + elementClasses_.ce_quote), - newStyle = changeStyleButton.dataset.style, - styleSelectors = this.parentNode.childNodes; - - quote.dataset.quoteStyle = newStyle; - - /** - * Mark selected style button - */ - for (var i = styleSelectors.length - 1; i >= 0; i--) { - styleSelectors[i].classList.remove(elementClasses_.settings.selectedType); - } - - this.classList.add(elementClasses_.settings.selectedType); - - }, - - /** - * @deprecated - */ - selectTypeQuoteStyle : function(type) { - - var quoteStyleFunction; - - /** - * Choose Quote style to replace - */ - switch (type) { - case 'simple': - quoteStyleFunction = methods_.makeSimpleQuote; - break; - case 'withCaption': - quoteStyleFunction = methods_.makeQuoteWithCaption; - break; - case 'withPhoto': - quoteStyleFunction = methods_.makeQuoteWithPhoto; - break; - } - - return quoteStyleFunction; - - }, - - /** - * @deprecated - */ - addSelectTypeClickListener : function(el, quoteStyle) { - - el.addEventListener('click', function () { - - /** - * Parsing currentNode to JSON. - */ - var parsedOldQuote = methods_.parseBlockQuote(), - newStyledQuote = quoteStyle(parsedOldQuote); - - var wrapper = codex.editor.content.composeNewBlock(newStyledQuote, 'quote'); - wrapper.appendChild(newStyledQuote); - - codex.editor.content.switchBlock(codex.editor.content.currentNode, newStyledQuote, 'quote'); - - /** Close settings after replacing */ - codex.editor.toolbar.settings.close(); - - }, false); - - }, - - /** - * @deprecated - */ - makeSimpleQuote : function(data) { - - var wrapper = ui_.makeBlock('BLOCKQUOTE', [elementClasses_.simple.text, elementClasses_.quoteText]); - - wrapper.innerHTML = data.text || ''; - - wrapper.dataset.quoteStyle = 'simple'; - wrapper.classList.add(elementClasses_.ce_quote); - wrapper.contentEditable = 'true'; - - return wrapper; - }, - - /** - * @deprecated - */ - makeQuoteWithCaption : function(data) { - - var wrapper = ui_.blockquote(), - text = ui_.makeBlock('DIV', [elementClasses_.withCaption.blockquote, elementClasses_.quoteText]), - author = ui_.makeBlock('DIV', [elementClasses_.withCaption.author, elementClasses_.quoteAuthor]); - - /* make text block ontentEditable */ - text.contentEditable = 'true'; - - text.innerHTML = data.text || ''; - - /* make Author contentEditable */ - author.contentEditable = 'true'; - - author.innerHTML = data.cite || ''; - - /* Appending created components */ - wrapper.dataset.quoteStyle = 'withCaption'; - wrapper.classList.add(elementClasses_.ce_quote); - - wrapper.appendChild(text); - wrapper.appendChild(author); - - return wrapper; - - }, - - makeQuoteWithPhoto : function(data) { - - var wrapper = ui_.blockquote(), - photo = ui_.makeBlock('DIV', [elementClasses_.withPhoto.photo]), - author = ui_.makeBlock('DIV', [elementClasses_.withPhoto.author, elementClasses_.quoteAuthor]), - job = ui_.makeBlock('DIV', [elementClasses_.withPhoto.job, elementClasses_.authorsJob]), - quote = ui_.makeBlock('DIV', [elementClasses_.withPhoto.quote, elementClasses_.quoteText]); - - /* Default Image src */ - if (!data.image) { - - var icon = ui_.makeBlock('SPAN', ['ce-icon-picture']); - photo.appendChild(icon); - - } else { - - var authorsPhoto = ui_.img(elementClasses_.authorsPhoto); - - authorsPhoto.src = data.image; - authorsPhoto.dataset.bigUrl = data.image; - - photo.classList.add(elementClasses_.authorsPhotoWrapper); - photo.appendChild(authorsPhoto); - } - - - photo.addEventListener('click', fileUploadClicked_, false); - - /* make author block contentEditable */ - author.contentEditable = 'true'; - author.innerHTML = data.cite || ''; - - /* Author's position and job */ - job.contentEditable = 'true'; - job.innerHTML = data.caption || ''; - - var authorsWrapper = ui_.makeBlock('DIV', [elementClasses_.withPhoto.authorHolder]); - authorsWrapper.appendChild(author); - authorsWrapper.appendChild(job); - - /* make quote text contentEditable */ - quote.contentEditable = 'true'; - quote.innerHTML = data.text || ''; - - wrapper.classList.add(elementClasses_.ce_quote); - wrapper.classList.add(elementClasses_.withPhoto.wrapper); - wrapper.dataset.quoteStyle = 'withPhoto'; - - wrapper.appendChild(quote); - wrapper.appendChild(photo); - wrapper.appendChild(authorsWrapper); - - return wrapper; - }, - - parseBlockQuote : function(block) { - - var currentNode = block || codex.editor.content.currentNode, - photo = currentNode.getElementsByTagName('img')[0], - author = currentNode.querySelector('.' + elementClasses_.quoteAuthor), - job = currentNode.querySelector('.' + elementClasses_.authorsJob), - quote ; - - /** Simple quote text placed in Blockquote tag*/ - if ( currentNode.dataset.quoteStyle == 'simple' ){ - - quote = currentNode.innerHTML || ''; - - } else { - - quote = currentNode.querySelector('.' + elementClasses_.quoteText).innerHTML; - - } - - if (job){ - - job = job.innerHTML || ''; - - } - - if (author){ - - author = author.innerHTML || ''; - - } - - if (photo){ - - photo = photo.dataset.bigUrl; - - } - - var data = { - style : currentNode.dataset.quoteStyle, - text : quote, - author : author, - job : job, - photo : photo - }; - - return data; - } - }; - - /** - * @private - * - * Author image Uploader - */ - var fileUploadClicked_ = function() { - - var beforeSend = photoUploadingCallbacks_.beforeSend, - success = photoUploadingCallbacks_.success, - error = photoUploadingCallbacks_.error; - - codex.editor.transport.selectAndUpload({ - beforeSend: beforeSend, - success: success, - error: error - }); - - }; - - /** - * @private - * - */ - var ui_ = { - - wrapper : function($classList) { - - var el = document.createElement('DIV'); - - el.classList.add($classList); - - return el; - - }, - - blockquote : function() { - - var el = document.createElement('BLOCKQUOTE'); - - return el; - - }, - - img : function(attribute) { - - var imageTag = document.createElement('IMG'); - imageTag.classList.add(attribute); - - return imageTag; - }, - - makeBlock : function(tag, classList) { - - var el = document.createElement(tag); - - if ( classList ) { - - for( var i = 0; i < classList.length; i++) - el.className += ' ' + classList[i]; - - } - - return el; - - } - - }; - - - - /** - * @private - * - * Callbacks - */ - var photoUploadingCallbacks_ = { - - preview_ : function(e) { - - var uploadImageWrapper = codex.editor.content.currentNode.querySelector('.' + elementClasses_.withPhoto.photo), - authorsPhoto = ui_.img(elementClasses_.authorsPhoto); - - /** Appending uploaded image */ - uploadImageWrapper.classList.add(elementClasses_.authorsPhotoWrapper, elementClasses_.authorsPhotoWrapper_preview); - - authorsPhoto.src = e.target.result; - - /** Remove icon from image wrapper */ - uploadImageWrapper.innerHTML = ''; - - uploadImageWrapper.appendChild(authorsPhoto); - }, - - beforeSend : function() { - - var input = codex.editor.transport.input, - files = input.files, - file = files[0], - fileReader = new FileReader(); - - fileReader.readAsDataURL(file); - - fileReader.onload = photoUploadingCallbacks_.preview_; - - }, - - /** - * Success callbacks for uploaded photo. - * Replace upload icon with uploaded photo - */ - success : function(result) { - - var parsed = JSON.parse(result), - filename = parsed.filename, - uploadImageWrapper = codex.editor.content.currentNode.querySelector('.' + elementClasses_.withPhoto.photo); - - var img = uploadImageWrapper.querySelector('IMG'); - img.src = parsed.data.file.bigUrl; - img.dataset.bigUrl = parsed.data.file.bigUrl; - - uploadImageWrapper.classList.remove(elementClasses_.authorsPhotoWrapper_preview); - }, - - /** Error callback. Sends notification to user that something happend or plugin doesn't supports method */ - error : function(result) { - console.log('Can\'t upload an image'); - } - - }; - - /** - * @private - * - * Make Quote from JSON datasets - */ - var make_ = function(data) { - - var tag; - - if (data && data.size) { - - data.style = quote_plugin.config.defaultStyle; - - /** - * Supported types - */ - switch (data.style) { - - case 'simple': - tag = methods_.makeSimpleQuote(data); - break; - case 'withCaption': - tag = methods_.makeQuoteWithCaption(data); - break; - case 'withPhoto': - tag = methods_.makeQuoteWithPhoto(data); - break; - } - - tag.dataset.quoteStyle = data.size; - - } else { - - var settings = { - "text" : null, - "format" : "html", - "cite" : null, - "caption": null, - "size" : null, - "image" : null - }; - - tag = methods_.makeQuoteWithPhoto(settings); - } - - return tag; - }; - - var prepareDataForSave_ = function(data) { - - if (data.size == 'withPhoto') { - data.size = 'small'; - } - - /** Make paragraphs */ - data.text = codex.editor.content.wrapTextWithParagraphs(data.text); - - return data; - }; - - /** - * @public - * - * Renderer - * - * @param data - */ - quote_plugin.render = function(data) { - return make_(data); - }; - - quote_plugin.validate = function(output) { - - if (typeof output.text != "string") { - return; - } - - return output; - }; - - quote_plugin.save = function(blockContent) { - - /** - * Extracts JSON quote data from HTML block - * @param {Text} text, {Text} author, {Object} photo - */ - var parsedblock = methods_.parseBlockQuote(blockContent); - - var outputData = { - "text" : parsedblock.text, - "format" : "html", - "cite" : parsedblock.author || '', - "caption": parsedblock.job || '', - "size" : parsedblock.style, - "image" : parsedblock.photo - }; - - return prepareDataForSave_(outputData); - }; - - /** - * @public - * - * Draws settings - */ - quote_plugin.makeSettings = function(data) { - - var holder = document.createElement('DIV'), - types = { - big : 'По центру', - small : 'Врезка' - }, - selectTypeButton; - - /** Add holder classname */ - holder.className = elementClasses_.settings.holder; - - /** Now add type selectors */ - for (var type in types){ - - selectTypeButton = document.createElement('SPAN'); - - selectTypeButton.textContent = types[type]; - selectTypeButton.className = elementClasses_.settings.buttons; - - selectTypeButton.dataset.style = type; - - if ( type == quote_plugin.config.defaultStyle ){ - selectTypeButton.classList.add(quoteTools.styles.settings.selectedType); - } - - // var quoteStyle = quoteTools.selectTypeQuoteStyle(type); - - selectTypeButton.addEventListener('click', methods_.changeStyleClicked, false); - // quoteTools.addSelectTypeClickListener(selectTypeButton, quoteStyle); - - holder.appendChild(selectTypeButton); - - } - - return holder; - - }; - - /** - * @public - * Default path to redactors images - * @type {null} - */ - quote_plugin.path = null; - - /** - * @public - * - * @type {null} - */ - quote_plugin.config = null; - - /** - * @public - * - * @param config - */ - quote_plugin.prepare = function(config) { - - quote_plugin.config = config; - - return Promise.resolve(); - }; - - quote_plugin.destroy = function () { - - quote = null; - - }; - - return quote_plugin; - -})({}); \ No newline at end of file diff --git a/plugins/raw/raw-icon-black.svg b/plugins/raw/raw-icon-black.svg deleted file mode 100644 index b9388423..00000000 --- a/plugins/raw/raw-icon-black.svg +++ /dev/null @@ -1,7 +0,0 @@ - - RAW - - - - - \ No newline at end of file diff --git a/plugins/raw/raw-icon-white.svg b/plugins/raw/raw-icon-white.svg deleted file mode 100644 index afdf1d24..00000000 --- a/plugins/raw/raw-icon-white.svg +++ /dev/null @@ -1,7 +0,0 @@ - - RAW - - - - - \ No newline at end of file diff --git a/plugins/raw/raw.css b/plugins/raw/raw.css deleted file mode 100644 index 1af87658..00000000 --- a/plugins/raw/raw.css +++ /dev/null @@ -1,36 +0,0 @@ -.raw-plugin__input { - display: block; - width: 100%; - min-height: 200px; - border: 0; - background: #fdfdfd; - box-shadow: inset 0 2px 8px rgba(23, 32, 74, 0.06); - border-radius: 3px; - margin: 1em auto; - padding: 1em; - box-sizing: border-box; - white-space: pre; - outline: none; - resize: vertical; - - font-family: 'monospace', 'monaco', 'consolas', 'courier'; - line-height: 1.7em; - font-size: 12px; - color: #152b48; - overflow: auto; - letter-spacing: 0.015em; -} - -.raw-plugin-icon { - display: inline-block; - width: 16px; - height: 32px; - background: url(raw-icon-black.svg) no-repeat center center; - background-size: contain; -} - -li:hover .raw-plugin-icon, -.selected .raw-plugin-icon{ - background: url(raw-icon-white.svg) no-repeat center center; - background-size: contain; -} diff --git a/plugins/raw/raw.js b/plugins/raw/raw.js deleted file mode 100644 index d63c29f1..00000000 --- a/plugins/raw/raw.js +++ /dev/null @@ -1,51 +0,0 @@ -/** -* Plugin for CodeX.Editor -* Implements RAW-data block -*/ -var rawPlugin = function (plugin) { - - var editor = codex.editor; - - plugin.render = function (data) { - - var input = editor.draw.node('TEXTAREA', 'raw-plugin__input', {}); - - input.placeholder = 'Вставьте HTML код'; - - if (data && data.raw) { - input.value = data.raw; - } - - return input; - - }; - - plugin.save = function (block) { - - return { - raw: block.value - }; - - }; - - plugin.validate = function (data) { - - if (data.raw.trim() === '') { - - return; - - } - - return true; - - }; - - plugin.destroy = function () { - - rawPlugin = null; - - }; - - return plugin; - -}({}); \ No newline at end of file diff --git a/plugins/twitter/twitter.css b/plugins/twitter/twitter.css deleted file mode 100644 index 34d4a89f..00000000 --- a/plugins/twitter/twitter.css +++ /dev/null @@ -1,20 +0,0 @@ -.ce-redactor .twitter-tweet { - margin: 15px auto !important; - width: 100% !important; -} - -.ce-twitter__caption { - max-width: 520px; - margin-bottom: 15px; - background: #fff; - border: 1px solid #ebeef3; - border-radius: 3px; - padding: .6em .8em; - box-sizing: border-box; - -} - -.ce-twitter__caption:empty::before { - content : 'Подпись'; - opacity: .5; -} \ No newline at end of file diff --git a/plugins/twitter/twitter.js b/plugins/twitter/twitter.js deleted file mode 100644 index 762fe6e9..00000000 --- a/plugins/twitter/twitter.js +++ /dev/null @@ -1,281 +0,0 @@ -/** - * Twitter plugin - * @version 1.0.0 - */ - -var twitter = (function(twitter_plugin) { - - /** - * User's configuration object - */ - var config_ = {}; - - /** - * CSS classes - */ - var css_ = { - pluginWrapper : 'cdx-tweet' - }; - - var methods = { - - /** - * Twitter render method appends content after block - * @param tweetId - */ - twitter : function(data, twitterBlock) { - - var tweet = methods.drawTwitterHolder(), - twittersCaption = methods.drawTwittersCaptionBlock(); - - if (data.caption) { - twittersCaption.innerHTML = data.caption; - } - - /** - * add created tweet to holder - */ - tweet.appendChild(twitterBlock); - - // setTimeout(function() { - window.twttr.widgets.createTweet(data.id_str, twitterBlock).then(tweetInsertedCallback_); - // }, 1000); - - tweet.classList.add('ce-redactor__loader'); - - if (codex.editor.content.currentNode) { - tweet.dataset.statusUrl = data.status_url; - codex.editor.content.switchBlock(codex.editor.content.currentNode, tweet, 'tweet'); - } - - /** - * in case if we need extra data - */ - if ( !data.user ) { - - codex.editor.core.ajax({ - url : config_.fetchUrl + '?tweetId=' + data.id_str, - type: "GET", - success: function(result) { - methods.saveTwitterData(result, tweet); - } - }); - - } else { - - tweet.dataset.profileImageUrl = data.user.profile_image_url; - tweet.dataset.profileImageUrlHttps = data.user.profile_image_url_https; - tweet.dataset.screenName = data.user.screen_name; - tweet.dataset.name = data.user.name; - tweet.dataset.id = +data.id; - tweet.dataset.idStr = data.id_str; - tweet.dataset.text = data.text; - tweet.dataset.createdAt = data.created_at; - tweet.dataset.statusUrl = data.status_url; - tweet.dataset.media = data.media; - - tweet.classList.remove('ce-redactor__loader'); - } - - /** - * add caption to tweet - */ - setTimeout(function() { - tweet.appendChild(twittersCaption); - }, 1000); - - return tweet; - - }, - - drawTwitterHolder : function() { - - var block = document.createElement('DIV'); - - block.classList.add(css_.pluginWrapper); - - return block; - - }, - - drawTwitterBlock : function() { - var block = codex.editor.draw.node('DIV', '', { height: "20px" }); - return block; - }, - - drawTwittersCaptionBlock : function() { - var block = codex.editor.draw.node('DIV', ['ce-twitter__caption'], { contentEditable : true }); - return block; - }, - - saveTwitterData : function(result, tweet) { - - var data = JSON.parse(result), - twitterContent = tweet; - - setTimeout(function() { - - /** - * Save twitter data via data-attributes - */ - twitterContent.dataset.profileImageUrl = data.user.profile_image_url; - twitterContent.dataset.profileImageUrlHttps = data.user.profile_image_url_https; - twitterContent.dataset.screenName = data.user.screen_name; - twitterContent.dataset.name = data.user.name; - twitterContent.dataset.id = +data.id; - twitterContent.dataset.idStr = data.id_str; - twitterContent.dataset.text = data.text; - twitterContent.dataset.createdAt = data.created_at; - twitterContent.dataset.media = data.entities.urls.length > 0 ? "false" : "true"; - - }, 50); - - } - }; - - /** - * @private - * Fires after tweet widget rendered - */ - function tweetInsertedCallback_(widget) { - - var pluginWrapper = findParent_( widget , css_.pluginWrapper ); - - pluginWrapper.classList.remove('ce-redactor__loader'); - - } - - /** - * @private - * Find closiest parent Element with CSS class - */ - function findParent_ (el, cls) { - while ((el = el.parentElement) && !el.classList.contains(cls)); - return el; - } - - /** - * Prepare twitter scripts - * @param {object} config - */ - twitter_plugin.prepare = function(config) { - - /** - * Save configs - */ - config_ = config; - - return new Promise(function(resolve, reject){ - - codex.editor.core.importScript("https://platform.twitter.com/widgets.js", 'twitter-api').then(function(){ - resolve(); - }).catch(function(){ - reject(Error('Twitter API was not loaded')); - }); - - }); - - }; - - /** - * @private - * - * @param data - * @returns {*} - */ - make_ = function(data) { - - if (!data.id || !data.status_url) - return; - - if (!data.id_str) { - data.id_str = data.status_url.match(/[^\/]+$/)[0]; - } - - var twitterBlock = methods.drawTwitterBlock(); - - var tweet = methods.twitter(data, twitterBlock); - - return tweet; - }; - - twitter_plugin.validate = function(data) { - return true; - }; - - twitter_plugin.save = function(blockContent) { - - var data, - caption = blockContent.querySelector('.ce-twitter__caption'); - - data = { - media:blockContent.dataset.media, - conversation:false, - user:{ - profile_image_url: blockContent.dataset.profileImageUrl, - profile_image_url_https: blockContent.dataset.profileImageUrlHttps, - screen_name: blockContent.dataset.screenName, - name: blockContent.dataset.name - }, - id: blockContent.dataset.id || blockContent.dataset.tweetId, - id_str : blockContent.dataset.idStr, - text: blockContent.dataset.text, - created_at: blockContent.dataset.createdAt, - status_url: blockContent.dataset.statusUrl, - caption: caption.innerHTML || '' - }; - - return data; - }; - - twitter_plugin.render = function(data) { - return make_(data); - }; - - twitter_plugin.urlPastedCallback = function(url) { - - var tweetId, - arr, - data; - - arr = url.split('/'); - tweetId = arr.pop(); - - /** Example */ - data = { - "media" : true, - "conversation" : false, - "user" : null, - "id" : +tweetId, - "text" : null, - "created_at" : null, - "status_url" : url, - "caption" : null - }; - - make_(data); - }; - - twitter_plugin.pastePatterns = [ - { - type: 'twitter', - regex: /http?.+twitter.com?.+\//, - callback: twitter_plugin.urlPastedCallback - } - ]; - - twitter_plugin.destroy = function () { - - twitter = null; - delete window.twttr; - - }; - - return twitter_plugin; - -})({}); - - - - - diff --git a/server/index.php b/server/index.php deleted file mode 100644 index e6286c87..00000000 --- a/server/index.php +++ /dev/null @@ -1,95 +0,0 @@ -loadHTML($html); - $nodes = $doc->getElementsByTagName('title'); - - $title = $nodes->item(0)->nodeValue; - $description = ""; - $keywords = ""; - $image = ""; - - $metas = $doc->getElementsByTagName('meta'); - - for ($i = 0; $i < $metas->length; $i++) - { - $meta = $metas->item($i); - if($meta->getAttribute('name') == 'description') - $description = $meta->getAttribute('content'); - if($meta->getAttribute('name') == 'keywords') - $keywords = $meta->getAttribute('content'); - if($meta->getAttribute('property')=='og:image'){ - $image = $meta->getAttribute('content'); - } - } - - return [ - 'image' => $image, - 'title' => $title, - 'description' => $description - ]; -} - -$url = get_url(); - -$url_params = parse_url($url); - -if (!$url) -{ - exit(0); -} - -$html = file_get_contents_curl($url); - -$result = get_meta_from_html($html); - -$result = array_merge( - - get_meta_from_html($html), - - array( - 'linkUrl' => $url, - 'linkText' => $url_params["host"] . isset($url_params["path"])?$url_params["path"]:"", - ) - -); - -echo json_encode($result); - -?> diff --git a/src/assets/arrow-down.svg b/src/assets/arrow-down.svg new file mode 100644 index 00000000..2ce78b4e --- /dev/null +++ b/src/assets/arrow-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/arrow-up.svg b/src/assets/arrow-up.svg new file mode 100644 index 00000000..4bc8ed14 --- /dev/null +++ b/src/assets/arrow-up.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/bold.svg b/src/assets/bold.svg new file mode 100644 index 00000000..f10a3d6d --- /dev/null +++ b/src/assets/bold.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/cross.svg b/src/assets/cross.svg new file mode 100644 index 00000000..54055697 --- /dev/null +++ b/src/assets/cross.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/dots.svg b/src/assets/dots.svg new file mode 100644 index 00000000..661f880d --- /dev/null +++ b/src/assets/dots.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/assets/italic.svg b/src/assets/italic.svg new file mode 100644 index 00000000..0c23b1ff --- /dev/null +++ b/src/assets/italic.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/link.svg b/src/assets/link.svg new file mode 100644 index 00000000..7a6068f4 --- /dev/null +++ b/src/assets/link.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/plus.svg b/src/assets/plus.svg new file mode 100644 index 00000000..5f2fe1b4 --- /dev/null +++ b/src/assets/plus.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/unlink.svg b/src/assets/unlink.svg new file mode 100644 index 00000000..780c3812 --- /dev/null +++ b/src/assets/unlink.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/codex.js b/src/codex.js new file mode 100644 index 00000000..ec04ab36 --- /dev/null +++ b/src/codex.js @@ -0,0 +1,111 @@ +/** + * Codex Editor + * + * Short Description (눈_눈;) + * @version 2.0.0 + * + * How to start? + * Example: + * new CodexEditor({ + * holderId : 'codex-editor', + * initialBlock : 'text', + * placeholder : 'Write your story....', + * tools: { + * quote: Quote, + * anotherTool : AnotherTool + * }, + * toolsConfig: { + * quote: { + * iconClassname : 'quote-icon', + * displayInToolbox : true, + * enableLineBreaks : true + * }, + * anotherTool: { + * iconClassname : 'tool-icon' + * } + * } + * }); + * + * - tools is an object: { + * pluginName: PluginClass, + * ..... + * } + * - toolsConfig is an additional configuration that uses Codex Editor API + * iconClassname - CSS classname of toolbox icon + * displayInToolbox - if you want to see your Tool in toolbox hided in "plus" button, than set "True". By default : "False" + * enableLineBreaks - by default enter creates new block that set as initialblock, but if you set this property "True", enter will break the lines in current block + * + * @author CodeX-Team + * + */ + +'use strict'; + +/** + * Apply polyfills + */ +import 'babel-core/register'; +import 'babel-polyfill'; +import 'components/polyfills'; +import Core from './components/core'; + +export default class CodexEditor { + /** Editor version */ + static get version() { + return VERSION; + } + + /** + * @constructor + * + * @param {EditorConfig} configuration - user configuration + */ + constructor(configuration) { + let {onReady} = configuration; + + onReady = onReady && typeof onReady === 'function' ? onReady : () => {}; + + configuration.onReady = () => {}; + + const editor = new Core(configuration); + + /** + * We need to export isReady promise in the constructor as it can be used before other API methods are exported + * @type {Promise} + */ + this.isReady = editor.isReady.then(() => { + this.exportAPI(editor); + onReady(); + }); + } + + /** + * Export external API methods + * + * @param editor + */ + exportAPI(editor) { + const fieldsToExport = [ 'configuration' ]; + const destroy = () => { + editor.moduleInstances.Listeners.removeAll(); + editor.moduleInstances.UI.destroy(); + editor = null; + + for (const field in this) { + delete this[field]; + } + + Object.setPrototypeOf(this, null); + }; + + fieldsToExport.forEach(field => { + this[field] = editor[field]; + }); + + this.destroy = destroy; + + Object.setPrototypeOf(this, editor.moduleInstances.API.methods); + + delete this['exportAPI']; + } +} diff --git a/src/components/__module.ts b/src/components/__module.ts new file mode 100644 index 00000000..f8496fe2 --- /dev/null +++ b/src/components/__module.ts @@ -0,0 +1,47 @@ +import IEditor from './interfaces/editor'; +import IEditorConfig from './interfaces/editor-config'; +import IModuleConfig from './interfaces/module-config'; + +/** + * @abstract + * @class Module + * @classdesc All modules inherits from this class. + * + * @typedef {Module} Module + * @property {Object} config - Editor user settings + * @property {IEditorConfig} Editor - List of Editor modules + */ +export default class Module { + + /** + * Editor modules list + * @type {IEditor} + */ + protected Editor: IEditor; + + /** + * Editor configuration object + * @type {IEditorConfig} + */ + protected config: IEditorConfig; + + /** + * @constructor + * @param {IModuleConfig} + */ + constructor({config}: IModuleConfig) { + if (new.target === Module) { + throw new TypeError('Constructors for abstract class Module are not allowed.'); + } + + this.config = config; + } + + /** + * Editor modules setter + * @param {IEditor} Editor + */ + set state(Editor) { + this.Editor = Editor; + } +} diff --git a/src/components/block-tunes/block-tune-delete.ts b/src/components/block-tunes/block-tune-delete.ts new file mode 100644 index 00000000..c991d752 --- /dev/null +++ b/src/components/block-tunes/block-tune-delete.ts @@ -0,0 +1,116 @@ +/** + * @class DeleteTune + * @classdesc Editor's default tune that moves up selected block + * + * @copyright 2018 + */ +import IBlockTune from '../interfaces/block-tune'; + +declare var $: any; +declare var _: any; + +export default class DeleteTune implements IBlockTune { + + /** + * Property that contains CodeX Editor API methods + * @see {docs/api.md} + */ + private readonly api: any; + + /** + * Styles + * @type {{wrapper: string}} + */ + private CSS = { + wrapper: 'ass', + button: 'ce-settings__button', + buttonDelete: 'ce-settings__button--delete', + buttonConfirm: 'ce-settings__button--confirm', + }; + + /** + * Delete confirmation + */ + private needConfirmation: boolean; + + /** + * set false confirmation state + */ + private resetConfirmation: () => void; + + /** + * Tune nodes + */ + private nodes = { + button: null, + }; + + /** + * DeleteTune constructor + * + * @param {Object} api + */ + constructor({api}) { + this.api = api; + + this.resetConfirmation = () => { + this.setConfirmation(false); + }; + } + + /** + * Create "Delete" button and add click event listener + * @returns [Element} + */ + public render() { + this.nodes.button = $.make('div', [this.CSS.button, this.CSS.buttonDelete], {}); + this.nodes.button.appendChild($.svg('cross', 12, 12)); + this.api.listener.on(this.nodes.button, 'click', (event) => this.handleClick(event), false); + return this.nodes.button; + } + + /** + * Delete block conditions passed + * @param {MouseEvent} event + */ + public handleClick(event: MouseEvent): void { + + /** + * if block is not waiting the confirmation, subscribe on block-settings-closing event to reset + * otherwise delete block + */ + if (!this.needConfirmation) { + this.setConfirmation(true); + + /** + * Subscribe on event. + * When toolbar block settings is closed but block deletion is not confirmed, + * then reset confirmation state + */ + this.api.events.on('block-settings-closed', this.resetConfirmation); + + } else { + + /** + * Unsubscribe from block-settings closing event + */ + this.api.events.off('block-settings-closed', this.resetConfirmation); + + this.api.blocks.delete(); + + /** + * Prevent firing ui~documentClicked that can drop currentBlock pointer + */ + event.stopPropagation(); + } + } + + /** + * change tune state + */ + private setConfirmation(state): void { + this.needConfirmation = state; + this.nodes.button.classList.add(this.CSS.buttonConfirm); + } + +} diff --git a/src/components/block-tunes/block-tune-move-down.ts b/src/components/block-tunes/block-tune-move-down.ts new file mode 100644 index 00000000..e848ca4e --- /dev/null +++ b/src/components/block-tunes/block-tune-move-down.ts @@ -0,0 +1,88 @@ +/** + * @class MoveDownTune + * @classdesc Editor's default tune - Moves down highlighted block + * + * @copyright 2018 + */ +import IBlockTune from '../interfaces/block-tune'; + +declare var $: any; +declare var _: any; + +export default class MoveDownTune implements IBlockTune { + /** + * Property that contains CodeX Editor API methods + * @see {api.md} + */ + private readonly api: any; + + /** + * Styles + * @type {{wrapper: string}} + */ + private CSS = { + button: 'ce-settings__button', + wrapper: 'ce-tune-move-down', + animation: 'wobble', + }; + + /** + * MoveDownTune constructor + * + * @param {Object} api + */ + public constructor({api}) { + this.api = api; + } + + /** + * Return 'move down' button + */ + public render() { + const moveDownButton = $.make('div', [this.CSS.button, this.CSS.wrapper], {}); + moveDownButton.appendChild($.svg('arrow-down', 14, 14)); + this.api.listener.on(moveDownButton, 'click', (event) => this.handleClick(event, moveDownButton), false); + return moveDownButton; + } + + /** + * Handle clicks on 'move down' button + * @param {MouseEvent} event + * @param {HTMLElement} button + */ + public handleClick(event: MouseEvent, button: HTMLElement) { + + const currentBlockIndex = this.api.blocks.getCurrentBlockIndex(); + + // If Block is last do nothing + if (currentBlockIndex === this.api.blocks.getBlocksCount() - 1) { + button.classList.add(this.CSS.animation); + + window.setTimeout( () => { + button.classList.remove(this.CSS.animation); + }, 500); + return; + } + + const nextBlockElement = this.api.blocks.getBlockByIndex(currentBlockIndex + 1).holder, + nextBlockCoords = nextBlockElement.getBoundingClientRect(); + + let scrollOffset = Math.abs(window.innerHeight - nextBlockElement.offsetHeight); + + /** + * Next block ends on screen. + * Increment scroll by next block's height to save element onscreen-position + */ + if (nextBlockCoords.top < window.innerHeight) { + + scrollOffset = window.scrollY + nextBlockElement.offsetHeight; + + } + + window.scrollTo(0, scrollOffset); + + /** Change blocks positions */ + this.api.blocks.swap(currentBlockIndex, currentBlockIndex + 1); + + } +} diff --git a/src/components/block-tunes/block-tune-move-up.ts b/src/components/block-tunes/block-tune-move-up.ts new file mode 100644 index 00000000..22b952e9 --- /dev/null +++ b/src/components/block-tunes/block-tune-move-up.ts @@ -0,0 +1,95 @@ +/** + * @class MoveUpTune + * @classdesc Editor's default tune that moves up selected block + * + * @copyright 2018 + */ +import IBlockTune from '../interfaces/block-tune'; + +declare var $: any; +declare var _: any; + +export default class MoveUpTune implements IBlockTune { + + /** + * Property that contains CodeX Editor API methods + * @see {api.md} + */ + private readonly api: any; + + /** + * Styles + * @type {{wrapper: string}} + */ + private CSS = { + button: 'ce-settings__button', + wrapper: 'ce-tune-move-up', + animation: 'wobble', + }; + + /** + * MoveUpTune constructor + * + * @param {Object} api + */ + public constructor({api}) { + this.api = api; + } + + /** + * Create "MoveUp" button and add click event listener + * @returns [HTMLElement} + */ + public render(): HTMLElement { + const moveUpButton = $.make('div', [this.CSS.button, this.CSS.wrapper], {}); + moveUpButton.appendChild($.svg('arrow-up', 14, 14)); + this.api.listener.on(moveUpButton, 'click', (event) => this.handleClick(event, moveUpButton), false); + return moveUpButton; + } + + /** + * Move current block up + * @param {MouseEvent} event + * @param {HTMLElement} button + */ + public handleClick(event: MouseEvent, button: HTMLElement): void { + + const currentBlockIndex = this.api.blocks.getCurrentBlockIndex(); + + if (currentBlockIndex === 0) { + button.classList.add(this.CSS.animation); + + window.setTimeout( () => { + button.classList.remove(this.CSS.animation); + }, 500); + return; + } + + const currentBlockElement = this.api.blocks.getBlockByIndex(currentBlockIndex).holder, + previousBlockElement = this.api.blocks.getBlockByIndex(currentBlockIndex - 1).holder; + + /** + * Here is two cases: + * - when previous block has negative offset and part of it is visible on window, then we scroll + * by window's height and add offset which is mathematically difference between two blocks + * + * - when previous block is visible and has offset from the window, + * than we scroll window to the difference between this offsets. + */ + const currentBlockCoords = currentBlockElement.getBoundingClientRect(), + previousBlockCoords = previousBlockElement.getBoundingClientRect(); + + let scrollUpOffset; + + if (previousBlockCoords.top > 0) { + scrollUpOffset = Math.abs(currentBlockCoords.top) - Math.abs(previousBlockCoords.top); + } else { + scrollUpOffset = window.innerHeight - Math.abs(currentBlockCoords.top) + Math.abs(previousBlockCoords.top); + } + + window.scrollBy(0, -1 * scrollUpOffset); + + /** Change blocks positions */ + this.api.blocks.swap(currentBlockIndex, currentBlockIndex - 1); + } +} diff --git a/src/components/block.ts b/src/components/block.ts new file mode 100644 index 00000000..f850aec7 --- /dev/null +++ b/src/components/block.ts @@ -0,0 +1,379 @@ +import IBlockTune, {IBlockTuneConstructor} from './interfaces/block-tune'; +import $ from './dom'; +import _ from './utils'; + +type Tool = any; + +/** + * @class Block + * @classdesc This class describes editor`s block, including block`s HTMLElement, data and tool + * + * @property {Tool} tool — current block tool (Paragraph, for example) + * @property {Object} CSS — block`s css classes + * + */ + +/** Import default tunes */ +import MoveUpTune from './block-tunes/block-tune-move-up'; +import DeleteTune from './block-tunes/block-tune-delete'; +import MoveDownTune from './block-tunes/block-tune-move-down'; + +/** + * @classdesc Abstract Block class that contains Block information, Tool name and Tool class instance + * + * @property tool - Tool instance + * @property html - Returns HTML content of plugin + * @property holder - Div element that wraps block content with Tool's content. Has `ce-block` CSS class + * @property pluginsContent - HTML content that returns by Tool's render function + */ +export default class Block { + + /** + * CSS classes for the Block + * @return {{wrapper: string, content: string}} + */ + static get CSS(): {wrapper: string, wrapperStretched: string, content: string, selected: string} { + return { + wrapper: 'ce-block', + wrapperStretched: 'ce-block--stretched', + content: 'ce-block__content', + selected: 'ce-block--selected', + }; + } + + /** + * Find and return all editable elements (contenteditables and native inputs) in the Tool HTML + * + * @returns {HTMLElement[]} + */ + get inputs(): HTMLElement[] { + const content = this.holder; + const allowedInputTypes = ['text', 'password', 'email', 'number', 'search', 'tel', 'url']; + + const selector = '[contenteditable], textarea, input, ' + + allowedInputTypes.map((type) => `input[type="${type}"]`).join(', '); + + const inputs = _.array(content.querySelectorAll(selector)); + + /** + * If inputs amount was changed we need to check if input index is bigger then inputs array length + */ + if (this.inputIndex > inputs.length - 1) { + this.inputIndex = inputs.length - 1; + } + + return inputs; + } + + /** + * Return current Tool`s input + * + * @returns {HTMLElement} + */ + get currentInput(): HTMLElement { + return this.inputs[this.inputIndex]; + } + + /** + * Set input index to the passed element + * + * @param {HTMLElement} element + */ + set currentInput(element: HTMLElement) { + const index = this.inputs.findIndex((input) => input === element || input.contains(element)); + + if (index !== -1) { + this.inputIndex = index; + } + } + + /** + * Return first Tool`s input + * + * @returns {HTMLElement} + */ + get firstInput(): HTMLElement { + return this.inputs[0]; + } + + /** + * Return first Tool`s input + * + * @returns {HTMLElement} + */ + get lastInput(): HTMLElement { + const inputs = this.inputs; + + return inputs[inputs.length - 1]; + } + + /** + * Return next Tool`s input or undefined if it doesn't exist + * + * @returns {HTMLElement} + */ + get nextInput(): HTMLElement { + return this.inputs[this.inputIndex + 1]; + } + + /** + * Return previous Tool`s input or undefined if it doesn't exist + * + * @returns {HTMLElement} + */ + get previousInput(): HTMLElement { + return this.inputs[this.inputIndex - 1]; + } + + /** + * Returns Plugins content + * @return {Node} + */ + get pluginsContent(): Node { + const pluginsContent = this.holder.querySelector(`.${Block.CSS.content}`); + + if (pluginsContent && pluginsContent.childNodes.length) { + return pluginsContent.childNodes[0]; + } + + return null; + } + + /** + * Get Block's JSON data + * @return {Object} + */ + get data(): object { + return this.save(); + } + + /** + * is block mergeable + * We plugin have merge function then we call it mergable + * @return {boolean} + */ + get mergeable(): boolean { + return typeof this.tool.merge === 'function'; + } + + /** + * Check block for emptiness + * @return {Boolean} + */ + get isEmpty(): boolean { + /** + * Allow Tool to represent decorative contentless blocks: for example "* * *"-tool + * That Tools are not empty + */ + if (this.class.contentless) { + return false; + } + + const emptyText = $.isEmpty(this.pluginsContent), + emptyMedia = !this.hasMedia; + + return emptyText && emptyMedia; + } + + /** + * Check if block has a media content such as images, iframes and other + * @return {Boolean} + */ + get hasMedia(): boolean { + /** + * This tags represents media-content + * @type {string[]} + */ + const mediaTags = [ + 'img', + 'iframe', + 'video', + 'audio', + 'source', + 'input', + 'textarea', + 'twitterwidget', + ]; + + return !!this.holder.querySelector(mediaTags.join(',')); + } + + /** + * Set selected state + * @param {Boolean} state - 'true' to select, 'false' to remove selection + */ + set selected(state: boolean) { + /** + * We don't need to mark Block as Selected when it is not empty + */ + if (state === true && !this.isEmpty) { + this.holder.classList.add(Block.CSS.selected); + } else { + this.holder.classList.remove(Block.CSS.selected); + } + } + + /** + * Set stretched state + * @param {Boolean} state - 'true' to enable, 'false' to disable stretched statte + */ + set stretched(state: boolean) { + this.holder.classList.toggle(Block.CSS.wrapperStretched, state); + } + + public name: string; + public tool: Tool; + public class: any; + public settings: object; + public holder: HTMLDivElement; + public tunes: IBlockTune[]; + private readonly api: object; + private inputIndex = 0; + + /** + * @constructor + * @param {String} toolName - Tool name that passed on initialization + * @param {Object} toolInstance — passed Tool`s instance that rendered the Block + * @param {Object} toolClass — Tool's class + * @param {Object} settings - default settings + * @param {Object} apiMethods - Editor API + */ + constructor(toolName: string, toolInstance: Tool, toolClass: object, settings: object, apiMethods: object) { + this.name = toolName; + this.tool = toolInstance; + this.class = toolClass; + this.settings = settings; + this.api = apiMethods; + this.holder = this.compose(); + + /** + * @type {IBlockTune[]} + */ + this.tunes = this.makeTunes(); + } + + /** + * Calls Tool's method + * + * Method checks tool property {MethodName}. Fires method with passes params If it is instance of Function + * + * @param {String} methodName + * @param {Object} params + */ + public call(methodName: string, params: object) { + /** + * call Tool's method with the instance context + */ + if (this.tool[methodName] && this.tool[methodName] instanceof Function) { + this.tool[methodName].call(this.tool, params); + } + } + + /** + * Call plugins merge method + * @param {Object} data + */ + public mergeWith(data: object): Promise { + return Promise.resolve() + .then(() => { + this.tool.merge(data); + }); + } + /** + * Extracts data from Block + * Groups Tool's save processing time + * @return {Object} + */ + public save(): Promise { + const extractedBlock = this.tool.save(this.pluginsContent); + + /** + * Measuring execution time + */ + const measuringStart = window.performance.now(); + let measuringEnd; + + return Promise.resolve(extractedBlock) + .then((finishedExtraction) => { + /** measure promise execution */ + measuringEnd = window.performance.now(); + + return { + tool: this.name, + data: finishedExtraction, + time : measuringEnd - measuringStart, + }; + }) + .catch(function(error) { + _.log(`Saving proccess for ${this.tool.name} tool failed due to the ${error}`, 'log', 'red'); + }); + } + + /** + * Uses Tool's validation method to check the correctness of output data + * Tool's validation method is optional + * + * @description Method also can return data if it passed the validation + * + * @param {Object} data + * @returns {Boolean|Object} valid + */ + public validateData(data: object): object|false { + let isValid = true; + + if (this.tool.validate instanceof Function) { + isValid = this.tool.validate(data); + } + + if (!isValid) { + return false; + } + + return data; + } + + /** + * Make an array with default settings + * Each block has default tune instance that have states + * @return {IBlockTune[]} + */ + public makeTunes(): IBlockTune[] { + const tunesList = [MoveUpTune, DeleteTune, MoveDownTune]; + + // Pluck tunes list and return tune instances with passed Editor API and settings + return tunesList.map( (tune: IBlockTuneConstructor) => { + return new tune({ + api: this.api, + settings: this.settings, + }); + }); + } + + /** + * Enumerates initialized tunes and returns fragment that can be appended to the toolbars area + * @return {DocumentFragment} + */ + public renderTunes(): DocumentFragment { + const tunesElement = document.createDocumentFragment(); + + this.tunes.forEach( (tune) => { + $.append(tunesElement, tune.render()); + }); + + return tunesElement; + } + + /** + * Make default Block wrappers and put Tool`s content there + * @returns {HTMLDivElement} + */ + private compose(): HTMLDivElement { + const wrapper = $.make('div', Block.CSS.wrapper) as HTMLDivElement, + contentNode = $.make('div', Block.CSS.content), + pluginsContent = this.tool.render(); + + contentNode.appendChild(pluginsContent); + wrapper.appendChild(contentNode); + return wrapper; + } +} diff --git a/src/components/core.js b/src/components/core.js new file mode 100644 index 00000000..9baec590 --- /dev/null +++ b/src/components/core.js @@ -0,0 +1,276 @@ +/** + * @typedef {Core} Core - editor core class + */ + +/** + * Dynamically imported utils + * + * @typedef {Dom} $ - {@link components/dom.js} + * @typedef {Util} _ - {@link components/utils.js} + */ + +/** + * Require Editor modules places in components/modules dir + */ +// eslint-disable-next-line +let modules = editorModules.map( module => require('./modules/' + module )); + +/** + * @class Core + * + * @classdesc CodeX Editor core class + * + * @property this.config - all settings + * @property this.moduleInstances - constructed editor components + * + * @type {Core} + */ +export default class Core { + /** + * @param {EditorConfig} config - user configuration + * + */ + constructor(config) { + /** + * Configuration object + * @type {EditorConfig} + */ + this.config = {}; + + /** + * @typedef {Object} EditorComponents + * @property {BlockManager} BlockManager + * @property {Tools} Tools + * @property {Events} Events + * @property {UI} UI + * @property {Toolbar} Toolbar + * @property {Toolbox} Toolbox + * @property {BlockSettings} BlockSettings + * @property {Renderer} Renderer + * @property {InlineToolbar} InlineToolbar + */ + this.moduleInstances = {}; + + /** + * Ready promise. Resolved if CodeX Editor is ready to work, rejected otherwise + */ + let onReady, onFail; + + this.isReady = new Promise((resolve, reject) => { + onReady = resolve; + onFail = reject; + }); + + Promise.resolve() + .then(() => { + this.configuration = config; + }) + .then(() => this.validate()) + .then(() => this.init()) + .then(() => this.start()) + .then(() => { + _.log('I\'m ready! (ノ◕ヮ◕)ノ*:・゚✧'); + + setTimeout(() => { + this.config.onReady.call(); + onReady(); + }, 500); + }) + .catch(error => { + _.log(`CodeX Editor does not ready because of ${error}`, 'error'); + onFail(error); + }); + } + + /** + * Setting for configuration + * @param {EditorConfig|string|null} config + */ + set configuration(config) { + /** + * Process zero-configuration or with only holderId + */ + if (typeof config === 'string' || typeof config === 'undefined') { + config = { + holderId: config || 'codex-editor' + }; + } + + /** + * If initial Block's Tool was not passed, use the Paragraph Tool + */ + this.config.initialBlock = config.initialBlock || 'paragraph'; + + /** + * Initial block type + * Uses in case when there is no blocks passed + * @type {{type: (*), data: {text: null}}} + */ + let initialBlockData = { + type : this.config.initialBlock, + data : {} + }; + + this.config.holderId = config.holderId; + this.config.placeholder = config.placeholder || 'write your story...'; + this.config.sanitizer = config.sanitizer || { + p: true, + b: true, + a: true + }; + + this.config.hideToolbar = config.hideToolbar ? config.hideToolbar : false; + this.config.tools = config.tools || {}; + this.config.data = config.data || {}; + this.config.onReady = config.onReady || function () {}; + + /** + * Initialize Blocks to pass data to the Renderer + */ + if (_.isEmpty(this.config.data)) { + this.config.data = {}; + this.config.data.blocks = [ initialBlockData ]; + } else { + if (!this.config.data.blocks || this.config.data.blocks.length === 0) { + this.config.data.blocks = [ initialBlockData ]; + } + } + } + + /** + * Returns private property + * @returns {EditorConfig} + */ + get configuration() { + return this.config; + } + + /** + * Checks for required fields in Editor's config + * @returns {void|Promise} + */ + validate() { + /** + * Check if holderId is not empty + */ + if (!this.config.holderId) { + return Promise.reject('«holderId» param must being not empty'); + } + + /** + * Check for a holder element's existence + */ + if (!$.get(this.config.holderId)) { + return Promise.reject(`element with ID «${this.config.holderId}» is missing. Pass correct holder's ID.`); + } + + /** + * Check Tools for a class containing + */ + for (let toolName in this.config.tools) { + const tool = this.config.tools[toolName]; + + if (!_.isFunction(tool) && !_.isFunction(tool.class)) { + return Promise.reject(`Tool «${toolName}» must be a constructor function or an object with that function in the «class» property`); + } + } + } + + /** + * Initializes modules: + * - make and save instances + * - configure + */ + init() { + /** + * Make modules instances and save it to the @property this.moduleInstances + */ + this.constructModules(); + + /** + * Modules configuration + */ + this.configureModules(); + } + + /** + * Make modules instances and save it to the @property this.moduleInstances + */ + constructModules() { + modules.forEach( Module => { + try { + /** + * We use class name provided by displayName property + * + * On build, Babel will transform all Classes to the Functions so, name will always be 'Function' + * To prevent this, we use 'babel-plugin-class-display-name' plugin + * @see https://www.npmjs.com/package/babel-plugin-class-display-name + */ + this.moduleInstances[Module.displayName] = new Module({ + config : this.configuration + }); + } catch ( e ) { + console.log('Module %o skipped because %o', Module, e); + } + }); + } + + /** + * Modules instances configuration: + * - pass other modules to the 'state' property + * - ... + */ + configureModules() { + for(let name in this.moduleInstances) { + /** + * Module does not need self-instance + */ + this.moduleInstances[name].state = this.getModulesDiff( name ); + } + } + + /** + * Return modules without passed name + */ + getModulesDiff( name ) { + let diff = {}; + + for(let moduleName in this.moduleInstances) { + /** + * Skip module with passed name + */ + if (moduleName === name) { + continue; + } + diff[moduleName] = this.moduleInstances[moduleName]; + } + + return diff; + } + + /** + * Start Editor! + * + * Get list of modules that needs to be prepared and return a sequence (Promise) + * @return {Promise} + */ + async start() { + const modulesToPrepare = ['Tools', 'UI', 'BlockManager', 'Paste']; + + await modulesToPrepare.reduce( + (promise, module) => promise.then(async () => { + _.log(`Preparing ${module} module`, 'time'); + + try { + await this.moduleInstances[module].prepare(); + } catch (e) { + _.log(`Module ${module} was skipped because of %o`, 'warn', e); + } + _.log(`Preparing ${module} module`, 'timeEnd'); + }), + Promise.resolve() + ); + + return this.moduleInstances.Renderer.render(this.config.data.blocks); + } +}; diff --git a/src/components/dom.ts b/src/components/dom.ts new file mode 100644 index 00000000..545e76ab --- /dev/null +++ b/src/components/dom.ts @@ -0,0 +1,423 @@ +/** + * DOM manipulations helper + */ +export default class Dom { + /** + * Check if passed tag has no closed tag + * @param {HTMLElement} tag + * @return {Boolean} + */ + public static isSingleTag(tag: HTMLElement): boolean { + return tag.tagName && [ + 'AREA', + 'BASE', + 'BR', + 'COL', + 'COMMAND', + 'EMBED', + 'HR', + 'IMG', + 'INPUT', + 'KEYGEN', + 'LINK', + 'META', + 'PARAM', + 'SOURCE', + 'TRACK', + 'WBR', + ].includes(tag.tagName); + } + + /** + * Helper for making Elements with classname and attributes + * + * @param {string} tagName - new Element tag name + * @param {array|string} classNames - list or name of CSS classname(s) + * @param {Object} attributes - any attributes + * @return {Element} + */ + public static make(tagName: string, classNames: string|string[] = null, attributes: object = {}): Element { + const el = document.createElement(tagName); + + if ( Array.isArray(classNames) ) { + el.classList.add(...classNames); + } else if ( classNames ) { + el.classList.add(classNames); + } + + for (const attrName in attributes) { + if (attributes.hasOwnProperty(attrName)) { + el[attrName] = attributes[attrName]; + } + } + + return el; + } + + /** + * Creates Text Node with the passed content + * @param {String} content - text content + * @return {Text} + */ + public static text(content: string): Text { + return document.createTextNode(content); + } + + /** + * Creates SVG icon linked to the sprite + * @param {string} name - name (id) of icon from sprite + * @param {number} width + * @param {number} height + * @return {SVGElement} + */ + public static svg(name: string, width: number = 14, height: number = 14): SVGElement { + const icon = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + + icon.classList.add('icon', 'icon--' + name); + icon.setAttribute('width', width + 'px'); + icon.setAttribute('height', height + 'px'); + icon.innerHTML = ``; + + return icon; + } + + /** + * Append one or several elements to the parent + * + * @param {Element|DocumentFragment} parent - where to append + * @param {Element|Element[]} elements - element or elements list + */ + public static append(parent: Element|DocumentFragment, elements: Element|Element[]): void { + if ( Array.isArray(elements) ) { + elements.forEach( (el) => parent.appendChild(el) ); + } else { + parent.appendChild(elements); + } + } + + /** + * Append element or a couple to the beginning of the parent elements + * + * @param {Element} parent - where to append + * @param {Element|Element[]} elements - element or elements list + */ + public static prepend(parent: Element, elements: Element|Element[]): void { + if ( Array.isArray(elements) ) { + elements = elements.reverse(); + elements.forEach( (el) => parent.prepend(el) ); + } else { + parent.prepend(elements); + } + } + + /** + * Swap two elements in parent + * @param {HTMLElement} el1 - from + * @param {HTMLElement} el2 - to + */ + public static swap(el1: HTMLElement, el2: HTMLElement): void { + // create marker element and insert it where el1 is + const temp = document.createElement('div'), + parent = el1.parentNode; + + parent.insertBefore(temp, el1); + + // move el1 to right before el2 + parent.insertBefore(el1, el2); + + // move el2 to right before where el1 used to be + parent.insertBefore(el2, temp); + + // remove temporary marker node + parent.removeChild(temp); + } + + /** + * Selector Decorator + * + * Returns first match + * + * @param {Element} el - element we searching inside. Default - DOM Document + * @param {String} selector - searching string + * + * @returns {Element} + */ + public static find(el: Element|Document = document, selector: string): Element { + return el.querySelector(selector); + } + + /** + * Get Element by Id + * + * @param {string} id + * @returns {HTMLElement | null} + */ + public static get(id: string): HTMLElement { + return document.getElementById(id); + } + + /** + * Selector Decorator. + * + * Returns all matches + * + * @param {Element} el - element we searching inside. Default - DOM Document + * @param {String} selector - searching string + * @returns {NodeList} + */ + public static findAll(el: Element|Document = document, selector: string): NodeList { + return el.querySelectorAll(selector); + } + + /** + * Search for deepest node which is Leaf. + * Leaf is the vertex that doesn't have any child nodes + * + * @description Method recursively goes throw the all Node until it finds the Leaf + * + * @param {Node} node - root Node. From this vertex we start Deep-first search + * {@link https://en.wikipedia.org/wiki/Depth-first_search} + * @param {Boolean} atLast - find last text node + * @return {Node} - it can be text Node or Element Node, so that caret will able to work with it + */ + public static getDeepestNode(node: Node, atLast: boolean = false): Node { + /** + * Current function have two directions: + * - starts from first child and every time gets first or nextSibling in special cases + * - starts from last child and gets last or previousSibling + * @type {string} + */ + const child = atLast ? 'lastChild' : 'firstChild', + sibling = atLast ? 'previousSibling' : 'nextSibling'; + + if (node && node.nodeType === Node.ELEMENT_NODE && node[child]) { + let nodeChild = node[child]; + + /** + * special case when child is single tag that can't contain any content + */ + if (Dom.isSingleTag(nodeChild as HTMLElement) && !Dom.isNativeInput(nodeChild)) { + /** + * 1) We need to check the next sibling. If it is Node Element then continue searching for deepest + * from sibling + * + * 2) If single tag's next sibling is null, then go back to parent and check his sibling + * In case of Node Element continue searching + * + * 3) If none of conditions above happened return parent Node Element + */ + if (nodeChild[sibling]) { + nodeChild = nodeChild[sibling]; + } else if (nodeChild.parentNode[sibling]) { + nodeChild = nodeChild.parentNode[sibling]; + } else { + return nodeChild.parentNode; + } + } + + return this.getDeepestNode(nodeChild, atLast); + } + + return node; + } + + /** + * Check if object is DOM node + * + * @param {Object} node + * @returns {boolean} + */ + public static isElement(node: any): boolean { + return node && typeof node === 'object' && node.nodeType && node.nodeType === Node.ELEMENT_NODE; + } + + /** + * Check if object is DocumentFragmemt node + * + * @param {Object} node + * @returns {boolean} + */ + public static isFragment(node: any): boolean { + return node && typeof node === 'object' && node.nodeType && node.nodeType === Node.DOCUMENT_FRAGMENT_NODE; + } + + /** + * Checks target if it is native input + * @param {Element|String|Node} target - HTML element or string + * @return {Boolean} + */ + public static isNativeInput(target: any): boolean { + const nativeInputs = [ + 'INPUT', + 'TEXTAREA', + ]; + + return target && target.tagName ? nativeInputs.includes(target.tagName) : false; + } + + /** + * Checks node if it is empty + * + * @description Method checks simple Node without any childs for emptiness + * If you have Node with 2 or more children id depth, you better use {@link Dom#isEmpty} method + * + * @param {Node} node + * @return {Boolean} true if it is empty + */ + public static isNodeEmpty(node: Node): boolean { + let nodeText; + + if ( this.isElement(node) && this.isNativeInput(node) ) { + nodeText = (node as HTMLInputElement).value; + } else { + nodeText = node.textContent.replace('\u200B', ''); + } + + return nodeText.trim().length === 0; + } + + /** + * checks node if it is doesn't have any child nodes + * @param {Node} node + * @return {boolean} + */ + public static isLeaf(node: Node): boolean { + if (!node) { + return false; + } + + return node.childNodes.length === 0; + } + + /** + * breadth-first search (BFS) + * {@link https://en.wikipedia.org/wiki/Breadth-first_search} + * + * @description Pushes to stack all DOM leafs and checks for emptiness + * + * @param {Node} node + * @return {boolean} + */ + public static isEmpty(node: Node): boolean { + const treeWalker = [], + leafs = []; + + if (!node) { + return true; + } + + if (!node.childNodes.length) { + return this.isNodeEmpty(node); + } + + treeWalker.push(node.firstChild); + + while ( treeWalker.length > 0 ) { + node = treeWalker.shift(); + + if (!node) { continue; } + + if ( this.isLeaf(node) ) { + leafs.push(node); + } else { + treeWalker.push(node.firstChild); + } + + while ( node && node.nextSibling ) { + node = node.nextSibling; + + if (!node) { continue; } + + treeWalker.push(node); + } + + /** + * If one of childs is not empty, checked Node is not empty too + */ + if (node && !this.isNodeEmpty(node)) { + return false; + } + } + + return leafs.every( (leaf) => this.isNodeEmpty(leaf) ); + } + + /** + * Check if string contains html elements + * + * @returns {boolean} + * @param {String} str + */ + public static isHTMLString(str: string): boolean { + const wrapper = Dom.make('div'); + + wrapper.innerHTML = str; + + return wrapper.childElementCount > 0; + } + + /** + * Return length of node`s text content + * + * @param {Node} node + * @returns {number} + */ + public static getContentLength(node: Node): number { + if (Dom.isNativeInput(node)) { + return (node as HTMLInputElement).value.length; + } + + if (node.nodeType === Node.TEXT_NODE) { + return (node as Text).length; + } + + return node.textContent.length; + } + + /** + * Return array of names of block html elements + * + * @returns {string[]} + */ + static get blockElements(): string[] { + return [ + 'address', + 'article', + 'aside', + 'blockquote', + 'canvas', + 'div', + 'dl', + 'dt', + 'fieldset', + 'figcaption', + 'figure', + 'footer', + 'form', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'header', + 'hgroup', + 'hr', + 'li', + 'main', + 'nav', + 'noscript', + 'ol', + 'output', + 'p', + 'pre', + 'ruby', + 'section', + 'table', + 'tr', + 'tfoot', + 'ul', + 'video', + ]; + } +} diff --git a/src/components/inline-tools/inline-tool-bold.ts b/src/components/inline-tools/inline-tool-bold.ts new file mode 100644 index 00000000..94fee145 --- /dev/null +++ b/src/components/inline-tools/inline-tool-bold.ts @@ -0,0 +1,77 @@ +import InlineTool from '../interfaces/tools/inline-tool'; +import SelectionUtils from '../selection'; + +declare var $: any; + +/** + * Bold Tool + * + * Inline Toolbar Tool + * + * Makes selected text bolder + */ +export default class BoldInlineTool implements InlineTool { + + /** + * Native Document's command that uses for Bold + */ + private readonly commandName: string = 'bold'; + + /** + * Styles + */ + private readonly CSS = { + button: 'ce-inline-tool', + buttonActive: 'ce-inline-tool--active', + buttonModifier: 'ce-inline-tool--bold', + }; + + /** + * Elements + */ + private nodes = { + button: undefined, + }; + + /** + * @param {{api: IAPI}} - CodeX Editor API + */ + constructor({api}) { + } + + /** + * Create button for Inline Toolbar + */ + public render(): HTMLElement { + this.nodes.button = document.createElement('button'); + this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier); + this.nodes.button.appendChild($.svg('bold', 13, 15)); + return this.nodes.button; + } + + /** + * Wrap range with tag + * @param {Range} range + */ + public surround(range: Range): void { + document.execCommand(this.commandName); + } + + /** + * Check selection and set activated state to button if there are tag + * @param {Selection} selection + */ + public checkState(selection: Selection): boolean { + const isActive = document.queryCommandState(this.commandName); + + this.nodes.button.classList.toggle(this.CSS.buttonActive, isActive); + return isActive; + } + + /** + * Set a shortcut + */ + public get shortcut(): string { + return 'CMD+B'; + } +} diff --git a/src/components/inline-tools/inline-tool-italic.ts b/src/components/inline-tools/inline-tool-italic.ts new file mode 100644 index 00000000..387656a2 --- /dev/null +++ b/src/components/inline-tools/inline-tool-italic.ts @@ -0,0 +1,77 @@ +import InlineTool from '../interfaces/tools/inline-tool'; + +declare var $: any; + +/** + * Italic Tool + * + * Inline Toolbar Tool + * + * Style selected text with italic + */ +export default class ItalicInlineTool implements InlineTool { + + /** + * Native Document's command that uses for Italic + */ + private readonly commandName: string = 'italic'; + + /** + * Styles + */ + private readonly CSS = { + button: 'ce-inline-tool', + buttonActive: 'ce-inline-tool--active', + buttonModifier: 'ce-inline-tool--italic', + }; + + /** + * Elements + */ + private nodes = { + button: null, + }; + + /** + * @param {{api: IAPI}} - CodeX Editor API + */ + constructor({api}) { + } + + /** + * Create button for Inline Toolbar + */ + public render(): HTMLElement { + this.nodes.button = document.createElement('button'); + this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier); + this.nodes.button.appendChild($.svg('italic', 6, 15)); + return this.nodes.button; + } + + /** + * Wrap range with tag + * @param {Range} range + */ + public surround(range: Range): void { + document.execCommand(this.commandName); + } + + /** + * Check selection and set activated state to button if there are tag + * @param {Selection} selection + */ + public checkState(selection: Selection): boolean { + const isActive = document.queryCommandState(this.commandName); + + this.nodes.button.classList.toggle(this.CSS.buttonActive, isActive); + return isActive; + } + + /** + * Set a shortcut + */ + public get shortcut(): string { + return 'CMD+I'; + } + +} diff --git a/src/components/inline-tools/inline-tool-link.ts b/src/components/inline-tools/inline-tool-link.ts new file mode 100644 index 00000000..0addf2c4 --- /dev/null +++ b/src/components/inline-tools/inline-tool-link.ts @@ -0,0 +1,316 @@ +import InlineTool from '../interfaces/tools/inline-tool'; +import SelectionUtils from '../selection'; + +declare var $: any; +declare var _: any; + +/** + * Link Tool + * + * Inline Toolbar Tool + * + * Wrap selected text with tag + */ +export default class LinkInlineTool implements InlineTool { + /** + * Native Document's commands for link/unlink + */ + private readonly commandLink: string = 'createLink'; + private readonly commandUnlink: string = 'unlink'; + + /** + * Enter key code + */ + private readonly ENTER_KEY: number = 13; + + /** + * Styles + */ + private readonly CSS = { + button: 'ce-inline-tool', + buttonActive: 'ce-inline-tool--active', + buttonModifier: 'ce-inline-tool--link', + buttonUnlink: 'ce-inline-tool--unlink', + input: 'ce-inline-tool-input', + inputShowed: 'ce-inline-tool-input--showed', + }; + + /** + * Elements + */ + private nodes = { + button: null, + input: null, + }; + + /** + * SelectionUtils instance + */ + private selection: SelectionUtils; + + /** + * Input opening state + */ + private inputOpened: boolean = false; + + /** + * Available Inline Toolbar methods (open/close) + */ + private inlineToolbar: any; + + /** + * @param {{api: IAPI}} - CodeX Editor API + */ + constructor({api}) { + this.inlineToolbar = api.toolbar; + this.selection = new SelectionUtils(); + } + + /** + * Create button for Inline Toolbar + */ + public render(): HTMLElement { + this.nodes.button = document.createElement('button'); + this.nodes.button.classList.add(this.CSS.button, this.CSS.buttonModifier); + this.nodes.button.appendChild($.svg('link', 15, 14)); + this.nodes.button.appendChild($.svg('unlink', 16, 18)); + return this.nodes.button; + } + + /** + * Input for the link + */ + public renderActions(): HTMLElement { + this.nodes.input = document.createElement('input'); + this.nodes.input.placeholder = 'Add a link'; + this.nodes.input.classList.add(this.CSS.input); + this.nodes.input.addEventListener('keydown', (event) => { + if (event.keyCode === this.ENTER_KEY ) { + this.enterPressed(event); + } + }); + return this.nodes.input; + } + + /** + * Handle clicks on the Inline Toolbar icon + * @param {Range} range + */ + public surround(range: Range): void { + /** + * Range will be null when user makes second click on the 'link icon' to close opened input + */ + if (range) { + /** + * Save selection before change focus to the input + */ + this.selection.save(); + const parentAnchor = this.selection.findParentTag('A'); + + /** + * Unlink icon pressed + */ + if (parentAnchor) { + this.selection.expandToTag(parentAnchor); + this.unlink(); + this.closeActions(); + this.checkState(); + this.inlineToolbar.close(); + return; + } + } + + this.toggleActions(); + } + + /** + * Check selection and set activated state to button if there are tag + * @param {Selection} selection + */ + public checkState(selection?: Selection): boolean { + const anchorTag = this.selection.findParentTag('A'); + + if (anchorTag) { + this.nodes.button.classList.add(this.CSS.buttonUnlink); + this.nodes.button.classList.add(this.CSS.buttonActive); + this.openActions(); + + /** + * Fill input value with link href + */ + const hrefAttr = anchorTag.getAttribute('href'); + this.nodes.input.value = hrefAttr !== 'null' ? hrefAttr : ''; + + this.selection.save(); + } else { + this.nodes.button.classList.remove(this.CSS.buttonUnlink); + this.nodes.button.classList.remove(this.CSS.buttonActive); + } + + return !!anchorTag; + } + + /** + * Function called with Inline Toolbar closing + */ + public clear(): void { + this.closeActions(); + } + + /** + * Set a shortcut + */ + public get shortcut(): string { + return 'CMD+K'; + } + + private toggleActions(): void { + if (!this.inputOpened) { + this.openActions(true); + } else { + this.closeActions(false); + } + } + + /** + * @param {boolean} needFocus - on link creation we need to focus input. On editing - nope. + */ + private openActions(needFocus: boolean = false): void { + this.nodes.input.classList.add(this.CSS.inputShowed); + if (needFocus) { + this.nodes.input.focus(); + } + this.inputOpened = true; + } + + /** + * Close input + * @param {boolean} clearSavedSelection — we don't need to clear saved selection + * on toggle-clicks on the icon of opened Toolbar + */ + private closeActions(clearSavedSelection: boolean = true): void { + this.nodes.input.classList.remove(this.CSS.inputShowed); + this.nodes.input.value = ''; + if (clearSavedSelection) { + this.selection.clearSaved(); + } + this.inputOpened = false; + } + + /** + * Enter pressed on input + * @param {KeyboardEvent} event + */ + private enterPressed(event: KeyboardEvent): void { + let value = this.nodes.input.value || ''; + + if (!value.trim()) { + this.selection.restore(); + this.unlink(); + event.preventDefault(); + this.closeActions(); + } + + if (!this.validateURL(value)) { + /** + * @todo show notification 'Incorrect Link' + */ + _.log('Incorrect Link pasted', 'warn', value); + return; + } + + value = this.prepareLink(value); + + this.selection.restore(); + this.insertLink(value); + + /** + * Preventing events that will be able to happen + */ + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); + + this.closeActions(); + this.inlineToolbar.close(); + this.checkState(); + } + + /** + * Detects if passed string is URL + * @param {string} str + * @return {Boolean} + */ + private validateURL(str: string): boolean { + /** + * Don't allow spaces + */ + return !/\s/.test(str); + } + + /** + * Process link before injection + * - sanitize + * - add protocol for links like 'google.com' + * @param {string} link - raw user input + */ + private prepareLink(link: string): string { + link = link.trim(); + link = this.addProtocol(link); + return link; + } + + /** + * Add 'http' protocol to the links like 'vc.ru', 'google.com' + * @param {String} link + */ + private addProtocol(link: string): string { + /** + * If protocol already exists, do nothing + */ + if (/^(\w+):\/\//.test(link)) { + return link; + } + + /** + * We need to add missed HTTP protocol to the link, but skip 2 cases: + * 1) Internal links like "/general" + * 2) Anchors looks like "#results" + * 3) Protocol-relative URLs like "//google.com" + */ + const isInternal = /^\/[^\/\s]/.test(link), + isAnchor = link.substring(0, 1) === '#', + isProtocolRelative = /^\/\/[^\/\s]/.test(link); + + if (!isInternal && !isAnchor && !isProtocolRelative) { + link = 'http://' + link; + } + + return link; + } + + /** + * Inserts tag with "href" + * @param {string} link - "href" value + */ + private insertLink(link: string): void { + + /** + * Edit all link, not selected part + */ + const anchorTag = this.selection.findParentTag('A'); + + if (anchorTag) { + this.selection.expandToTag(anchorTag); + } + + document.execCommand(this.commandLink, false, link); + } + + /** + * Removes tag + */ + private unlink(): void { + document.execCommand(this.commandUnlink); + } +} diff --git a/src/components/interfaces/api.ts b/src/components/interfaces/api.ts new file mode 100644 index 00000000..15931761 --- /dev/null +++ b/src/components/interfaces/api.ts @@ -0,0 +1,213 @@ +import IInputOutputData from './input-output-data'; + +/** + * CodeX Editor Public API + * + * @copyright 2018 + */ +export interface IAPI { + blocks: IBlocksAPI; + caret: ICaretAPI; + events: IEventsAPI; + listener: IListenerAPI; + sanitizer: ISanitizerAPI; + saver: ISaverAPI; + selection: ISelectionAPI; + styles: IStylesAPI; + toolbar: IToolbarAPI; +} + +/** + * Working with Blocks list: moving, removing, etc + */ +export interface IBlocksAPI { + + /** + * Clears Blocks list + */ + clear: () => void; + + /** + * Fills editor with Blocks data + */ + render: (data: IInputOutputData) => void; + + /** + * Removes block + */ + delete: (blockIndex?: number) => void; + + /** + * Swap two Blocks by positions + * @param {number} fromIndex - position of first Block + * @param {number} toIndex - position of second Block + */ + swap: (fromIndex: number, toIndex: number) => void; + + /** + * Returns block by passed index + * + * @param {Number} index - needed block with index + * @return {object} + */ + getBlockByIndex: (index: number) => object; + + /** + * Returns current block index + * @return {number} + */ + getCurrentBlockIndex: () => number; + + /** + * Returns Block's count + * @return {number} + */ + getBlocksCount: () => number; + + /** + * Stretch Block's content + * @param {number} index - index of Block + * @param {boolean} [status] - true to enable, false to disable + */ + stretchBlock: (index: number, status: boolean) => void; + + /** + * Insert new initial typed Block + */ + insertNewBlock: () => void; +} + +/** + * Methods for working with Caret + */ +export interface ICaretAPI { + /** + * @todo Add caret methods + */ +} + +/** + * Events Module API methods + */ +export interface IEventsAPI { + + /** + * Subsribe on events + */ + on: (eventName: string, callback: () => void) => void; + + /** + * Trigger subsribed callbacks + */ + emit: (eventName: string, data: object) => void; + + /** + * Unsubsribe callback + */ + off: (eventName: string, callback: () => void) => void; +} + +/** + * DOM Listener API + */ +export interface IListenerAPI { + + /** + * Adds event listener + * @param {HTMLElement} element + * @param {string} eventType + * @param {() => void} handler + * @param useCapture + * @return {boolean} + */ + on: (element: HTMLElement, eventType: string, handler: () => void, useCapture: boolean) => void; + + /** + * Remove event listener + * @param {HTMLElement} element + * @param {string} eventType + * @param {() => void} handler + */ + off: (element: HTMLElement, eventType: string, handler: () => void) => void; +} + +/** + * Sanitizer's methods + */ +export interface ISanitizerAPI { + + /** + * Clean taint string from disallowed tags and attributes + * + * @param taintString + * @param config + */ + clean: (taintString, config?) => string; +} + +/** + * Saver's methods + */ +export interface ISaverAPI { + /** + * Return current blocks + * + * @return {IInputOutputData} + */ + save: () => IInputOutputData; +} + +/** + * SelectionUtils's methods + */ +export interface ISelectionAPI { + + /** + * Looks ahead to find passed tag from current selection + * + * @param {String} tagName + * @param {String} className + */ + findParentTag: (tagName: string, className: string) => HTMLElement|null; + + /** + * Expands selection range to the passed parent node + * + * @param {HTMLElement} node + */ + expandToTag: (node: HTMLElement) => void; +} + +export interface IStylesAPI { + + block: string; + + inlineToolButton: string; + + inlineToolButtonActive: string; + + input: string; + + loader: string; + + settingsButton: string; + + settingsButtonActive: string; +} + +/** + * Toolbar's methods + * Basic toolbar methods + */ +export interface IToolbarAPI { + + /** + * Opens only toolbar + */ + open: () => void; + + /** + * Closes toolbar. If toolbox or toolbar-blockSettings are opened then they will be closed too + */ + close: () => void; +} diff --git a/src/components/interfaces/block-tune.ts b/src/components/interfaces/block-tune.ts new file mode 100644 index 00000000..83139236 --- /dev/null +++ b/src/components/interfaces/block-tune.ts @@ -0,0 +1,29 @@ +export interface IBlockTuneConstructor { + new (data: {api: any, settings: any}); +} + +/** + * BlockTune interface + * + * All tunes must implement this interface + */ +export interface IBlockTune { + /** + * Settings will be described later + */ + settings?: object; + + /** + * Returns tune button that will be appended in default settings area + */ + render(): HTMLElement; + + /** + * handle Click event + * @param {MouseEvent} event + * @param {HTMLElement} button + */ + handleClick(event: MouseEvent, button: HTMLElement): void; +} + +export default IBlockTune; diff --git a/src/components/interfaces/editor-config.ts b/src/components/interfaces/editor-config.ts new file mode 100644 index 00000000..0c631589 --- /dev/null +++ b/src/components/interfaces/editor-config.ts @@ -0,0 +1,52 @@ +import ISanitizerConfig from './sanitizer-config'; +import IInputOutputData from './input-output-data'; +import IToolSettings from './tools/tool-settings'; +import ITool from './tools/tool'; + +/** + * Editor Instance config + */ +export default interface IEditorConfig { + + /** + * Element to append Editor + */ + holderId: string; + + /** + * Map of used Tools with or without configuration + */ + tools: {[toolName: string]: ITool|IToolSettings}; + + /** + * This Tool will be added by default + * Name should be equal a one Tool's key of Editor's Tools + */ + initialBlock: string; + + /** + * Blocks list in JSON-format + */ + data?: IInputOutputData; + + /** + * First Block placeholder + */ + placeholder?: string; + + /** + * Define tags not to be stripped off while pasting + * @see {@link sanitizer} + */ + sanitizer?: ISanitizerConfig; + + /** + * Do not show toolbar + */ + hideToolbar?: boolean; + + /** + * Editor initialization callback + */ + onReady?(): void; +} diff --git a/src/components/interfaces/editor.ts b/src/components/interfaces/editor.ts new file mode 100644 index 00000000..72c22e0c --- /dev/null +++ b/src/components/interfaces/editor.ts @@ -0,0 +1,60 @@ +import BlockEvents from '../modules/block-events'; +import InlineToolbar from '../modules/toolbar-inline'; +import ListenerAPI from '../modules/api-listener'; +import Module from '../__module'; +import SanitizerAPI from '../modules/api-sanitizer'; +import SaverAPI from '../modules/api-saver'; +import SelectionAPI from '../modules/api-selection'; +import ToolbarAPI from '../modules/api-toolbar'; +import API from '../modules/api'; +import StylesAPI from '../modules/api-styles'; +import Shortcuts from '../modules/shortcuts'; + +export default interface IEditor { + + API: API; + + BlockEvents: BlockEvents; + + BlockSettings: Module; // @todo create interface + + BlocksAPI: Module; // @todo create interface + + Caret: Module; // @todo create interface + + Events: Module; // @todo create interface + + EventsAPI: Module; // @todo create interface + + InlineToolbar: InlineToolbar; + + ListenerAPI: ListenerAPI; + + Listeners: Module; // @todo create interface + + Renderer: Module; // @todo create interface + + Sanitizer: Module; // @todo create interface + + SanitizerAPI: SanitizerAPI; + + Saver: Module; // @todo create interface + + SaverAPI: SaverAPI; + + SelectionAPI: SelectionAPI; + + Shortcuts: Shortcuts; + + StylesAPI: StylesAPI; + + Toolbar: Module; // @todo create interface + + ToolbarAPI: ToolbarAPI; + + Toolbox: Module; // @todo create interface + + Tools: Module; // @todo create interface + + UI: Module; // @todo create interface +} diff --git a/src/components/interfaces/input-output-data.ts b/src/components/interfaces/input-output-data.ts new file mode 100644 index 00000000..824faa20 --- /dev/null +++ b/src/components/interfaces/input-output-data.ts @@ -0,0 +1,23 @@ +import IBlockToolData from './tools/block-tool-data'; + +/** + * Interface represents input CodeX Editor data + * that passed with initial configuration object as 'data' key + */ +export default interface IInputOutputData { + + /** + * Timestamp of saving in milliseconds + */ + readonly time?: number; + + /** + * Saved Blocks + */ + readonly blocks: IBlockToolData[]; + + /** + * Editor's version + */ + readonly version?: string; +} diff --git a/src/components/interfaces/module-config.ts b/src/components/interfaces/module-config.ts new file mode 100644 index 00000000..6afa04f3 --- /dev/null +++ b/src/components/interfaces/module-config.ts @@ -0,0 +1,12 @@ +import IEditorConfig from './editor-config'; + +/** + * Internal Module's construction parameters + */ +export default interface IModuleConfig { + + /** + * Editor's config + */ + config: IEditorConfig; +} diff --git a/src/components/interfaces/sanitizer-config.ts b/src/components/interfaces/sanitizer-config.ts new file mode 100644 index 00000000..bbc303d6 --- /dev/null +++ b/src/components/interfaces/sanitizer-config.ts @@ -0,0 +1,36 @@ +export default interface ISanitizerConfig { + + /** + * Tag name and params not to be stripped off + * @see {@link https://github.com/guardian/html-janitor} + * + * @example Save P tags + * p: true + * + * @example Save A tags and do not strip HREF attribute + * a: { + * href: true + * } + * + * @example Save A tags with TARGET="_blank" attribute + * a: function (aTag) { + * return aTag.target === '_black'; + * } + * + * @example Save U tags that are not empty + * u: function(el){ + * return el.textContent !== ''; + * } + * + * @example For blockquote with class 'indent' save CLASS and STYLE attributes + * Otherwise strip all attributes + * blockquote: function(el) { + * if (el.classList.contains('indent')) { + * return { 'class': true, 'style': true }; + * } else { + * return {}; + * } + * } + */ + [key: string]: boolean|object|(() => any); +} diff --git a/src/components/interfaces/shortcuts.ts b/src/components/interfaces/shortcuts.ts new file mode 100644 index 00000000..214aa401 --- /dev/null +++ b/src/components/interfaces/shortcuts.ts @@ -0,0 +1,40 @@ +/** + * Shortcuts Interface + * + * implements CodeX-Team shortcuts Module + * @see https://github.com/codex-team/codex.shortcuts + */ +export interface IShortcuts { + + /** + * Adds shortcut + * @param {IShortcut} shortcut + */ + add(shortcut: IShortcut): void; + + /** + * removes shortcut + * @param {string} shortcut + */ + remove(shortcut: string): void; +} + +/** + * Shortcut interface + * Each shortcut must have name and handler + * `name` is a shortcut, like 'CMD+K', 'CMD+B' etc + * `handler` is a callback + */ +export interface IShortcut { + + /** + * Shortcut name + * Ex. CMD+I, CMD+B .... + */ + name: string; + + /** + * Shortcut handler + */ + handler(event): (event) => void; +} diff --git a/src/components/interfaces/tools/block-tool-data.ts b/src/components/interfaces/tools/block-tool-data.ts new file mode 100644 index 00000000..66ff166f --- /dev/null +++ b/src/components/interfaces/tools/block-tool-data.ts @@ -0,0 +1,4 @@ +/** + * Object returned by Tool's {@link IBlockTool#save} method + */ +export default interface IBlockToolData {} diff --git a/src/components/interfaces/tools/block-tool.ts b/src/components/interfaces/tools/block-tool.ts new file mode 100644 index 00000000..729e8dec --- /dev/null +++ b/src/components/interfaces/tools/block-tool.ts @@ -0,0 +1,62 @@ +import IBlockToolData from './tool-settings'; +import ITool from './tool'; + +/** + * Describe Block Tool object + * @see {@link docs/tools.md} + */ +export default interface IBlockTool extends ITool { + + /** + * Pass `true` if Tool represents decorative empty Block + */ + contentless?: boolean; + + /** + * Should this Tool be displayed in the Editor's Toolbox + */ + displayInToolbox?: boolean; + + /** + * Disable ability to replace empty Block by Toolbox + */ + irreplaceable?: boolean; + + /** + * String with an icon for Toolbox + */ + toolboxIcon?: string; + + /** + * Return Tool's main block-wrapper + * @return {HTMLElement} + */ + render(): HTMLElement; + + /** + * Process Tool's element in DOM and return raw data + * @param {HTMLElement} block - element created by {@link IBlockTool#render} function + * @return {IBlockToolData} + */ + save(block: HTMLElement): IBlockToolData; + + /** + * Create Block's settings block + * @return {HTMLElement} + */ + renderSettings?(): HTMLElement; + + /** + * Validate Block's data + * @param {IBlockToolData} blockData + * @return {boolean} + */ + validate?(blockData: IBlockToolData): boolean; + + /** + * Method that specified how to merge two Blocks with same type. + * Called by backspace at the beginning of the Block + * @param {IBlockToolData} blockData + */ + merge?(blockData: IBlockToolData): void; +} diff --git a/src/components/interfaces/tools/inline-tool.ts b/src/components/interfaces/tools/inline-tool.ts new file mode 100644 index 00000000..282dbcf2 --- /dev/null +++ b/src/components/interfaces/tools/inline-tool.ts @@ -0,0 +1,41 @@ +/** + * Base structure for the Inline Toolbar Tool + */ +import ITool from './tool'; + +export default interface IInlineTool extends ITool { + /** + * Public getter with shortcut string + */ + shortcut?: string; + + /** + * Returns button for the Inline Toolbar + */ + render(): HTMLElement; + + /** + * Method that accepts selected range and wrap it somehow + * @param {Range} range - selection's range + */ + surround(range: Range): void; + + /** + * Get SelectionUtils and detect if Tool was applied + * For example, after that Tool can highlight button or show some details + * @param {Selection} selection - current Selection + */ + checkState(selection: Selection): boolean; + + /** + * Make additional element with actions + * For example, input for the 'link' tool or textarea for the 'comment' tool + */ + renderActions?(): HTMLElement; + + /** + * Function called with Inline Toolbar closing + */ + clear?(): void; + +} diff --git a/src/components/interfaces/tools/tool-settings.ts b/src/components/interfaces/tools/tool-settings.ts new file mode 100644 index 00000000..89a9ec13 --- /dev/null +++ b/src/components/interfaces/tools/tool-settings.ts @@ -0,0 +1,39 @@ +/** + * Object passed to the Tool's constructor by {@link IEditorConfig#tools} + */ +import ITool from './tool'; + +export default interface IToolSettings { + + /** + * Tool's class + */ + class: ITool; + + /** + * User configuration object that will be passed to the Tool's constructor + */ + config?: object; + + /** + * Disable module {@link Paste} for this Tool + * @todo remove this option + */ + disallowPaste?: boolean; + + /** + * Is user available to add line brakes in Tool (for example by Shift+Enter) + */ + enableLineBreaks?: boolean; + + /** + * Is need to show Inline Toolbar. + * Can accept array of Tools for InlineToolbar or boolean. + */ + inlineToolbar?: boolean|string[]; + + /** + * Define shortcut that will render Tool + */ + shortcut?: string; +} diff --git a/src/components/interfaces/tools/tool.ts b/src/components/interfaces/tools/tool.ts new file mode 100644 index 00000000..f5b6e5dd --- /dev/null +++ b/src/components/interfaces/tools/tool.ts @@ -0,0 +1,10 @@ +/** + * Abstract interface of all Tools + */ +export default interface ITool { + + /** + * Define Tool type as Inline + */ + isInline?: boolean; +} diff --git a/src/components/modules/_destroyer.js b/src/components/modules/_destroyer.js new file mode 100644 index 00000000..c168f3fb --- /dev/null +++ b/src/components/modules/_destroyer.js @@ -0,0 +1,70 @@ +/** + * Codex Editor Destroyer module + * + * @auhor Codex Team + * @version 1.0 + */ + +module.exports = function (destroyer) { + let editor = codex.editor; + + destroyer.removeNodes = function () { + editor.nodes.wrapper.remove(); + editor.nodes.notifications.remove(); + }; + + destroyer.destroyPlugins = function () { + for (var tool in editor.tools) { + if (typeof editor.tools[tool].destroy === 'function') { + editor.tools[tool].destroy(); + } + } + }; + + destroyer.destroyScripts = function () { + var scripts = document.getElementsByTagName('SCRIPT'); + + for (var i = 0; i < scripts.length; i++) { + if (scripts[i].id.indexOf(editor.scriptPrefix) + 1) { + scripts[i].remove(); + i--; + } + } + }; + + + /** + * Delete editor data from webpage. + * You should send settings argument with boolean flags: + * @param settings.ui- remove redactor event listeners and DOM nodes + * @param settings.scripts - remove redactor scripts from DOM + * @param settings.plugins - remove plugin's objects + * @param settings.core - remove editor core. You can remove core only if UI and scripts flags is true + * } + * + */ + destroyer.destroy = function (settings) { + if (!settings || typeof settings !== 'object') { + return; + } + + if (settings.ui) { + destroyer.removeNodes(); + editor.listeners.removeAll(); + } + + if (settings.scripts) { + destroyer.destroyScripts(); + } + + if (settings.plugins) { + destroyer.destroyPlugins(); + } + + if (settings.ui && settings.scripts && settings.core) { + delete codex.editor; + } + }; + + return destroyer; +}({}); \ No newline at end of file diff --git a/src/components/modules/_notifications.js b/src/components/modules/_notifications.js new file mode 100644 index 00000000..1746ae6f --- /dev/null +++ b/src/components/modules/_notifications.js @@ -0,0 +1,187 @@ +/** + * Codex Editor Notification Module + * + * @author Codex Team + * @version 1.0 + */ + +module.exports = (function (notifications) { + let editor = codex.editor; + + var queue = []; + + var addToQueue = function (settings) { + queue.push(settings); + + var index = 0; + + while ( index < queue.length && queue.length > 5) { + if (queue[index].type == 'confirm' || queue[index].type == 'prompt') { + index++; + continue; + } + + queue[index].close(); + queue.splice(index, 1); + } + }; + + notifications.createHolder = function () { + var holder = editor.draw.node('DIV', 'cdx-notifications-block'); + + editor.nodes.notifications = document.body.appendChild(holder); + + return holder; + }; + + + /** + * Error notificator. Shows block with message + * @protected + */ + notifications.errorThrown = function (errorMsg, event) { + editor.notifications.notification({message: 'This action is not available currently', type: event.type}); + }; + + /** + * + * Appends notification + * + * settings = { + * type - notification type (reserved types: alert, confirm, prompt). Just add class 'cdx-notification-'+type + * message - notification message + * okMsg - confirm button text (default - 'Ok') + * cancelBtn - cancel button text (default - 'Cancel'). Only for confirm and prompt types + * confirm - function-handler for ok button click + * cancel - function-handler for cancel button click. Only for confirm and prompt types + * time - time (in seconds) after which notification will close (default - 10s) + * } + * + * @param settings + */ + notifications.notification = function (constructorSettings) { + /** Private vars and methods */ + var notification = null, + cancel = null, + type = null, + confirm = null, + inputField = null; + + var confirmHandler = function () { + close(); + + if (typeof confirm !== 'function' ) { + return; + } + + if (type == 'prompt') { + confirm(inputField.value); + return; + } + + confirm(); + }; + + var cancelHandler = function () { + close(); + + if (typeof cancel !== 'function' ) { + return; + } + + cancel(); + }; + + + /** Public methods */ + function create(settings) { + if (!(settings && settings.message)) { + editor.core.log('Can\'t create notification. Message is missed'); + return; + } + + settings.type = settings.type || 'alert'; + settings.time = settings.time*1000 || 10000; + + var wrapper = editor.draw.node('DIV', 'cdx-notification'), + message = editor.draw.node('DIV', 'cdx-notification__message'), + input = editor.draw.node('INPUT', 'cdx-notification__input'), + okBtn = editor.draw.node('SPAN', 'cdx-notification__ok-btn'), + cancelBtn = editor.draw.node('SPAN', 'cdx-notification__cancel-btn'); + + message.textContent = settings.message; + okBtn.textContent = settings.okMsg || 'ОК'; + cancelBtn.textContent = settings.cancelMsg || 'Отмена'; + + editor.listeners.add(okBtn, 'click', confirmHandler); + editor.listeners.add(cancelBtn, 'click', cancelHandler); + + wrapper.appendChild(message); + + if (settings.type == 'prompt') { + wrapper.appendChild(input); + } + + wrapper.appendChild(okBtn); + + if (settings.type == 'prompt' || settings.type == 'confirm') { + wrapper.appendChild(cancelBtn); + } + + wrapper.classList.add('cdx-notification-' + settings.type); + wrapper.dataset.type = settings.type; + + notification = wrapper; + type = settings.type; + confirm = settings.confirm; + cancel = settings.cancel; + inputField = input; + + if (settings.type != 'prompt' && settings.type != 'confirm') { + window.setTimeout(close, settings.time); + } + }; + + /** + * Show notification block + */ + function send() { + editor.nodes.notifications.appendChild(notification); + inputField.focus(); + + editor.nodes.notifications.classList.add('cdx-notification__notification-appending'); + + window.setTimeout(function () { + editor.nodes.notifications.classList.remove('cdx-notification__notification-appending'); + }, 100); + + addToQueue({type: type, close: close}); + }; + + /** + * Remove notification block + */ + function close() { + notification.remove(); + }; + + + if (constructorSettings) { + create(constructorSettings); + send(); + } + + return { + create: create, + send: send, + close: close + }; + }; + + notifications.clear = function () { + editor.nodes.notifications.innerHTML = ''; + queue = []; + }; + + return notifications; +})({}); \ No newline at end of file diff --git a/src/components/modules/api-blocks.ts b/src/components/modules/api-blocks.ts new file mode 100644 index 00000000..eae5fad7 --- /dev/null +++ b/src/components/modules/api-blocks.ts @@ -0,0 +1,145 @@ +declare var Module: any; + +import { IBlocksAPI } from '../interfaces/api'; +import IInputOutputData from '../interfaces/input-output-data'; +import IModuleConfig from '../interfaces/module-config'; + +/** + * @class BlocksAPI + * provides with methods working with Block + */ +export default class BlocksAPI extends Module implements IBlocksAPI { + + /** + * Save Editor config. API provides passed configuration to the Blocks + */ + constructor({config}: IModuleConfig) { + super({config}); + } + + /** + * Available methods + * @return {IBlocksAPI} + */ + get methods(): IBlocksAPI { + return { + clear: () => this.clear(), + render: (data: IInputOutputData) => this.render(data), + delete: () => this.delete(), + swap: (fromIndex: number, toIndex: number) => this.swap(fromIndex, toIndex), + getBlockByIndex: (index: number) => this.getBlockByIndex(index), + getCurrentBlockIndex: () => this.getCurrentBlockIndex(), + getBlocksCount: () => this.getBlocksCount(), + stretchBlock: (index: number, status: boolean) => this.stretchBlock(index, status), + insertNewBlock: () => this.insertNewBlock(), + }; + } + + /** + * Returns Blocks count + * @return {number} + */ + public getBlocksCount(): number { + return this.Editor.BlockManager.blocks.length; + } + + /** + * Returns current block index + * @return {number} + */ + public getCurrentBlockIndex(): number { + return this.Editor.BlockManager.currentBlockIndex; + } + + /** + * Returns Current Block + * @param {Number} index + * + * @return {Object} + */ + public getBlockByIndex(index: number): object { + return this.Editor.BlockManager.getBlockByIndex(index); + } + + /** + * Call Block Manager method that swap Blocks + * @param {number} fromIndex - position of first Block + * @param {number} toIndex - position of second Block + */ + public swap(fromIndex: number, toIndex: number): void { + this.Editor.BlockManager.swap(fromIndex, toIndex); + + /** + * Move toolbar + * DO not close the settings + */ + this.Editor.Toolbar.move(false); + } + + /** + * Deletes Block + * @param blockIndex + */ + public delete(blockIndex?: number): void { + this.Editor.BlockManager.removeBlock(blockIndex); + + /** + * in case of last block deletion + * Insert new initial empty block + */ + if (this.Editor.BlockManager.blocks.length === 0) { + this.Editor.BlockManager.insert(); + } + + /** + * In case of deletion first block we need to set caret to the current Block + */ + if (this.Editor.BlockManager.currentBlockIndex === 0) { + this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock); + } else { + this.Editor.Caret.navigatePrevious(true); + } + + this.Editor.Toolbar.close(); + } + + /** + * Clear Editor's area + */ + public clear(): void { + this.Editor.BlockManager.clear(true); + } + + /** + * Fills Editor with Blocks data + * @param {IInputOutputData} data — Saved Editor data + */ + public render(data: IInputOutputData): void { + this.Editor.BlockManager.clear(); + this.Editor.Renderer.render(data.blocks); + } + + /** + * Stretch Block's content + * @param {number} index + * @param {boolean} status - true to enable, false to disable + */ + public stretchBlock(index: number, status: boolean): void { + const block = this.Editor.BlockManager.getBlockByIndex(index); + + if (!block) { + return; + } + + block.stretched = status !== undefined ? status : true; + } + + /** + * Insert new Block + * After set caret to this Block + */ + public insertNewBlock() { + const newBlock = this.Editor.BlockManager.insert(); + this.Editor.Caret.setToBlock(newBlock); + } +} diff --git a/src/components/modules/api-caret.ts b/src/components/modules/api-caret.ts new file mode 100644 index 00000000..5fb27a0f --- /dev/null +++ b/src/components/modules/api-caret.ts @@ -0,0 +1,26 @@ +declare var Module: any; + +import {ICaretAPI} from '../interfaces/api'; +import IModuleConfig from '../interfaces/module-config'; + +/** + * @class CaretAPI + * provides with methods to work with caret + */ +export default class CaretAPI extends Module implements ICaretAPI { + + /** + * Save Editor config. API provides passed configuration to the Blocks + */ + constructor({config}: IModuleConfig) { + super({config}); + } + + /** + * Available methods + * @return {ICaretAPI} + */ + get methods(): ICaretAPI { + return {}; + } +} diff --git a/src/components/modules/api-events.ts b/src/components/modules/api-events.ts new file mode 100644 index 00000000..43bb55b0 --- /dev/null +++ b/src/components/modules/api-events.ts @@ -0,0 +1,58 @@ +declare var Module: any; + +import {IEventsAPI} from '../interfaces/api'; +import IModuleConfig from '../interfaces/module-config'; + +/** + * @class EventsAPI + * provides with methods working with Toolbar + */ +export default class EventsAPI extends Module implements IEventsAPI { + + /** + * Save Editor config. API provides passed configuration to the Blocks + */ + constructor({config}: IModuleConfig) { + super({config}); + } + + /** + * Available methods + * @return {IEventsAPI} + */ + get methods(): IEventsAPI { + return { + emit: (eventName: string, data: object) => this.emit(eventName, data), + off: (eventName: string, callback: () => void) => this.off(eventName, callback), + on: (eventName: string, callback: () => void) => this.on(eventName, callback), + }; + } + + /** + * Subscribe on Events + * @param {String} eventName + * @param {Function} callback + */ + public on(eventName, callback): void { + this.Editor.Events.on(eventName, callback); + } + + /** + * Emit event with data + * @param {String} eventName + * @param {Object} data + */ + public emit(eventName, data): void { + this.Editor.Events.emit(eventName, data); + } + + /** + * Unsubscribe from Event + * @param {String} eventName + * @param {Function} callback + */ + public off(eventName, callback): void { + this.Editor.Events.off(eventName, callback); + } + +} diff --git a/src/components/modules/api-listener.ts b/src/components/modules/api-listener.ts new file mode 100644 index 00000000..4e3eb8d2 --- /dev/null +++ b/src/components/modules/api-listener.ts @@ -0,0 +1,52 @@ +declare var Module: any; + +import {IListenerAPI} from '../interfaces/api'; +import IModuleConfig from '../interfaces/module-config'; + +/** + * @class API + * Provides with methods working with DOM Listener + */ +export default class ListenerAPI extends Module implements IListenerAPI { + + /** + * Save Editor config. API provides passed configuration to the Blocks + */ + constructor({config}: IModuleConfig) { + super({config}); + } + + /** + * Available methods + * @return {IToolbarAPI} + */ + get methods(): IListenerAPI { + return { + on: (element, eventType, handler, useCapture) => this.on(element, eventType, handler, useCapture), + off: (element, eventType, handler) => this.off(element, eventType, handler), + }; + } + + /** + * adds DOM event listener + * + * @param {HTMLElement} element + * @param {string} eventType + * @param {() => void} handler + * @param {boolean} useCapture + */ + public on(element: HTMLElement, eventType: string, handler: () => void, useCapture?: boolean): void { + this.Editor.Listeners.on(element, eventType, handler, useCapture); + } + + /** + * Removes DOM listener from element + * + * @param element + * @param eventType + * @param handler + */ + public off(element, eventType, handler): void { + this.Editor.Listeners.off(element, eventType, handler); + } +} diff --git a/src/components/modules/api-sanitizer.ts b/src/components/modules/api-sanitizer.ts new file mode 100644 index 00000000..3baf074d --- /dev/null +++ b/src/components/modules/api-sanitizer.ts @@ -0,0 +1,33 @@ +declare var Module: any; + +import {ISanitizerAPI} from '../interfaces/api'; +import IModuleConfig from '../interfaces/module-config'; + +/** + * @class API + * Provides CodeX Editor Sanitizer that allows developers to clean their HTML + */ +export default class SanitizerAPI extends Module implements ISanitizerAPI { + + /** + * Save Editor config. API provides passed configuration to the Blocks + */ + constructor({config}: IModuleConfig) { + super({config}); + } + + /** + * Available methods + * @return {ISanitizerAPI} + */ + get methods(): ISanitizerAPI { + return { + clean: (taintString, config) => this.clean(taintString, config), + }; + } + + public clean(taintString, config) { + return this.Editor.Sanitizer.clean(taintString, config); + } + +} diff --git a/src/components/modules/api-saver.ts b/src/components/modules/api-saver.ts new file mode 100644 index 00000000..fef987c8 --- /dev/null +++ b/src/components/modules/api-saver.ts @@ -0,0 +1,36 @@ +declare var Module: any; + +import {ISaverAPI} from '../interfaces/api'; +import IInputOutputData from '../interfaces/input-output-data'; +import IModuleConfig from '../interfaces/module-config'; + +/** + * @class SaverAPI + * provides with methods to save data + */ +export default class SaverAPI extends Module implements ISaverAPI { + + /** + * Save Editor config. API provides passed configuration to the Blocks + */ + constructor({config}: IModuleConfig) { + super({config}); + } + + /** + * Available methods + * @return {ISaverAPI} + */ + get methods(): ISaverAPI { + return { + save: () => this.save(), + }; + } + + /** + * Return Editor's data + */ + public save(): IInputOutputData { + return this.Editor.Saver.save(); + } +} diff --git a/src/components/modules/api-selection.ts b/src/components/modules/api-selection.ts new file mode 100644 index 00000000..187bd8f2 --- /dev/null +++ b/src/components/modules/api-selection.ts @@ -0,0 +1,49 @@ +declare var Module: any; + +import {ISelectionAPI} from '../interfaces/api'; +import IModuleConfig from '../interfaces/module-config'; +import Selection from '../selection'; + +/** + * @class API + * Provides with methods working with SelectionUtils + */ +export default class SelectionAPI extends Module implements ISelectionAPI { + + /** + * Save Editor config. API provides passed configuration to the Blocks + */ + constructor({config}: IModuleConfig) { + super({config}); + } + + /** + * Available methods + * @return {ISelectionAPI} + */ + get methods(): ISelectionAPI { + return { + findParentTag: (tagName: string, className: string) => this.findParentTag(tagName, className), + expandToTag: (node: HTMLElement) => this.expandToTag(node), + }; + } + + /** + * Looks ahead from selection and find passed tag with class name + * @param {string} tagName - tag to find + * @param {string} className - tag's class name + * @return {HTMLElement|null} + */ + public findParentTag(tagName: string, className: string): HTMLElement|null { + return new Selection().findParentTag(tagName, className); + } + + /** + * Expand selection to passed tag + * @param {HTMLElement} node - tag that should contain selection + */ + public expandToTag(node: HTMLElement): void { + new Selection().expandToTag(node); + } + +} diff --git a/src/components/modules/api-styles.ts b/src/components/modules/api-styles.ts new file mode 100644 index 00000000..901f7630 --- /dev/null +++ b/src/components/modules/api-styles.ts @@ -0,0 +1,45 @@ +declare var Module: any; + +import IModuleConfig from '../interfaces/module-config'; +import {IStylesAPI} from '../interfaces/api'; + +/** + * + */ +export default class StylesAPI extends Module { + + /** + * Save Editor config + * API provides passed configuration to the Blocks + */ + constructor({config}: IModuleConfig) { + super({config}); + } + + get classes(): IStylesAPI { + return { + /** + * Base Block styles + */ + block: 'cdx-block', + + /** + * Inline Tools styles + */ + inlineToolButton: 'ce-inline-tool', + inlineToolButtonActive: 'ce-inline-tool--active', + + /** + * UI elements + */ + input: 'cdx-input', + loader: 'cdx-loader', + + /** + * Settings styles + */ + settingsButton: 'cdx-settings-button', + settingsButtonActive: 'cdx-settings-button--active', + }; + } +} diff --git a/src/components/modules/api-toolbar.ts b/src/components/modules/api-toolbar.ts new file mode 100644 index 00000000..d3b11660 --- /dev/null +++ b/src/components/modules/api-toolbar.ts @@ -0,0 +1,44 @@ +declare var Module: any; + +import {IToolbarAPI} from '../interfaces/api'; +import IModuleConfig from '../interfaces/module-config'; + +/** + * @class ToolbarsAPI + * provides with methods working with Toolbar + */ +export default class ToolbarAPI extends Module implements IToolbarAPI { + + /** + * Save Editor config. API provides passed configuration to the Blocks + */ + constructor({config}: IModuleConfig) { + super({config}); + } + + /** + * Available methods + * @return {IToolbarAPI} + */ + get methods(): IToolbarAPI { + return { + close: () => this.close(), + open: () => this.open(), + }; + } + + /** + * Open toolbar + */ + public open(): void { + this.Editor.Toolbar.open(); + } + + /** + * Close toolbar and all included elements + */ + public close(): void { + this.Editor.Toolbar.close(); + } + +} diff --git a/src/components/modules/api.ts b/src/components/modules/api.ts new file mode 100644 index 00000000..b9f6687c --- /dev/null +++ b/src/components/modules/api.ts @@ -0,0 +1,40 @@ +/** + * @module API + * @copyright 2018 + * + * Each block has an Editor API instance to use provided public methods + * if you cant to read more about how API works, please see docs + */ +declare var Module: any; +declare var $: any; +declare var _: any; + +import { IAPI } from '../interfaces/api'; + +/** + * @class API + */ +export default class API extends Module { + + /** + * Save Editor config. API provides passed configuration to the Blocks + * @param {EditorConfig} config + */ + constructor({config}) { + super({config}); + } + + public get methods(): IAPI { + return { + blocks: this.Editor.BlocksAPI.methods, + caret: this.Editor.CaretAPI.methods, + events: this.Editor.EventsAPI.methods, + listener: this.Editor.ListenerAPI.methods, + sanitizer: this.Editor.SanitizerAPI.methods, + saver: this.Editor.SaverAPI.methods, + selection: this.Editor.SelectionAPI.methods, + styles: this.Editor.StylesAPI.classes, + toolbar: this.Editor.ToolbarAPI.methods, + }; + } +} diff --git a/src/components/modules/block-events.ts b/src/components/modules/block-events.ts new file mode 100644 index 00000000..bdd65f9b --- /dev/null +++ b/src/components/modules/block-events.ts @@ -0,0 +1,301 @@ +/** + * Contains keyboard and mouse events binded on each Block by Block Manager + */ +declare var Module: any; +declare var $: any; +declare var _: any; + +export default class BlockEvents extends Module { + /** + * @constructor + */ + constructor({config}) { + super({config}); + } + + /** + * All keydowns on Block + * @param {KeyboardEvent} event - keydown + */ + public keydown(event: KeyboardEvent): void { + /** + * Run common method for all keydown events + */ + this.beforeKeydownProcessing(event); + + /** + * Fire keydown processor by event.keyCode + */ + switch (event.keyCode) { + case _.keyCodes.BACKSPACE: + this.backspace(event); + break; + + case _.keyCodes.ENTER: + this.enter(event); + break; + + case _.keyCodes.DOWN: + case _.keyCodes.RIGHT: + this.arrowRightAndDown(event); + break; + + case _.keyCodes.UP: + case _.keyCodes.LEFT: + this.arrowLeftAndUp(event); + break; + + case _.keyCodes.TAB: + this.tabPressed(event); + break; + + default: + this.defaultHandler(); + break; + } + } + + /** + * Fires on keydown before event processing + * @param {KeyboardEvent} event - keydown + */ + public beforeKeydownProcessing(event): void { + /** + * Clear all highlightings + */ + this.Editor.BlockManager.clearHighlightings(); + + /** + * Do not close Toolbox on Tabs or on Enter with opened Toolbox + */ + if (!this.needToolbarClosing(event)) { + return; + } + + this.Editor.Toolbar.close(); + } + + /** + * Key up on Block: + * - shows Inline Toolbar if something selected + */ + public keyup(event): void { + this.Editor.InlineToolbar.handleShowingEvent(event); + } + + /** + * Mouse up on Block: + * - shows Inline Toolbar if something selected + */ + public mouseUp(event): void { + this.Editor.InlineToolbar.handleShowingEvent(event); + } + + /** + * Open Toolbox to leaf Tools + * @param {KeyboardEvent} event + */ + public tabPressed(event): void { + + const {currentBlock} = this.Editor.BlockManager; + + /** Prevent Default behaviour */ + event.preventDefault(); + event.stopPropagation(); + + /** this property defines leaf direction */ + const shiftKey = event.shiftKey, + direction = shiftKey ? 'left' : 'right'; + + if (this.Editor.Toolbar.opened && currentBlock.isEmpty) { + this.Editor.Toolbox.open(); + } else if (currentBlock.isEmpty) { + this.Editor.Toolbar.open(); + this.Editor.Toolbox.open(); + } + + if (this.Editor.Toolbox.opened) { + this.Editor.Toolbox.leaf(direction); + } + } + + /** + * ENTER pressed on block + * @param {KeyboardEvent} event - keydown + */ + private enter(event: KeyboardEvent): void { + const currentBlock = this.Editor.BlockManager.currentBlock, + tool = this.Editor.Tools.toolsAvailable[currentBlock.name]; + + /** + * Don't handle Enter keydowns when Tool sets enableLineBreaks to true. + * Uses for Tools like where line breaks should be handled by default behaviour. + */ + if (tool && tool[this.Editor.Tools.apiSettings.IS_ENABLED_LINE_BREAKS]) { + return; + } + + if (this.Editor.Toolbox.opened && this.Editor.Toolbox.getActiveTool) { + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); + this.Editor.Toolbox.toolButtonActivate(event, this.Editor.Toolbox.getActiveTool); + return; + } + + /** + * Allow to create linebreaks by Shift+Enter + */ + if (event.shiftKey) { + return; + } + /** + * Split the Current Block into two blocks + * Renew local current node after split + */ + const newCurrent = this.Editor.BlockManager.split(); + + this.Editor.Caret.setToBlock(newCurrent); + + /** + * If new Block is empty + */ + if (this.Editor.Tools.isInitial(newCurrent.tool) && newCurrent.isEmpty) { + /** + * Show Toolbar + */ + this.Editor.Toolbar.open(); + + /** + * Show Plus Button + */ + this.Editor.Toolbar.plusButton.show(); + } + + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); + } + + /** + * Handle backspace keydown on Block + * @param {KeyboardEvent} event - keydown + */ + private backspace(event: KeyboardEvent): void { + const BM = this.Editor.BlockManager; + const currentBlock = this.Editor.BlockManager.currentBlock, + tool = this.Editor.Tools.toolsAvailable[currentBlock.name]; + + /** + * Don't handle Backspaces when Tool sets enableLineBreaks to true. + * Uses for Tools like where line breaks should be handled by default behaviour. + */ + if (tool && tool[this.Editor.Tools.apiSettings.IS_ENABLED_LINE_BREAKS]) { + return; + } + + const isFirstBlock = BM.currentBlockIndex === 0, + canMergeBlocks = this.Editor.Caret.isAtStart && !isFirstBlock; + + /** If current Block is empty just remove this Block */ + if (this.Editor.BlockManager.currentBlock.isEmpty) { + this.Editor.BlockManager.removeBlock(); + + /** + * in case of last block deletion + * Insert new initial empty block + */ + if (this.Editor.BlockManager.blocks.length === 0) { + this.Editor.BlockManager.insert(); + } + + /** + * In case of deletion first block we need to set caret to the current Block + * After BlockManager removes the Block (which is current now), + * pointer that references to the current Block, now points to the Next + */ + if (this.Editor.BlockManager.currentBlockIndex === 0) { + this.Editor.Caret.setToBlock(this.Editor.BlockManager.currentBlock); + } else { + this.Editor.Caret.navigatePrevious(true); + } + + this.Editor.Toolbar.close(); + return; + } + + if (!canMergeBlocks) { + return; + } + + // preventing browser default behaviour + event.preventDefault(); + + const targetBlock = BM.getBlockByIndex(BM.currentBlockIndex - 1), + blockToMerge = BM.currentBlock; + + /** + * Blocks that can be merged: + * 1) with the same Name + * 2) Tool has 'merge' method + * + * other case will handle as usual ARROW LEFT behaviour + */ + if (blockToMerge.name !== targetBlock.name || !targetBlock.mergeable) { + if (this.Editor.Caret.navigatePrevious()) { + this.Editor.Toolbar.close(); + } + + return; + } + + this.Editor.Caret.createShadow(targetBlock.pluginsContent); + BM.mergeBlocks(targetBlock, blockToMerge) + .then( () => { + /** Restore caret position after merge */ + this.Editor.Caret.restoreCaret(targetBlock.pluginsContent); + targetBlock.pluginsContent.normalize(); + this.Editor.Toolbar.close(); + }); + } + + /** + * Handle right and down keyboard keys + */ + private arrowRightAndDown(event: KeyboardEvent): void { + if (this.Editor.Caret.navigateNext()) { + /** + * Default behaviour moves cursor by 1 character, we need to prevent it + */ + event.preventDefault(); + } + } + + /** + * Handle left and up keyboard keys + */ + private arrowLeftAndUp(event: KeyboardEvent): void { + if (this.Editor.Caret.navigatePrevious()) { + /** + * Default behaviour moves cursor by 1 character, we need to prevent it + */ + event.preventDefault(); + } + } + + /** + * Default keydown handler + */ + private defaultHandler(): void {} + + /** + * Cases when we need to close Toolbar + */ + private needToolbarClosing(event) { + const toolboxItemSelected = (event.keyCode === _.keyCodes.ENTER && this.Editor.Toolbox.opened), + flippingToolboxItems = event.keyCode === _.keyCodes.TAB; + + return !(event.shiftKey || flippingToolboxItems || toolboxItemSelected); + } + +} diff --git a/src/components/modules/blockManager.js b/src/components/modules/blockManager.js new file mode 100644 index 00000000..7cff7359 --- /dev/null +++ b/src/components/modules/blockManager.js @@ -0,0 +1,663 @@ +/** + * @class BlockManager + * @classdesc Manage editor`s blocks storage and appearance + * + * @module BlockManager + * + * @version 2.0.0 + */ + +import Block from '../block'; + +/** + * @typedef {BlockManager} BlockManager + * @property {Number} currentBlockIndex - Index of current working block + * @property {Proxy} _blocks - Proxy for Blocks instance {@link Blocks} + */ +export default class BlockManager extends Module { + /** + * @constructor + * @param {EditorConfig} config + */ + constructor({config}) { + super({config}); + + /** + * Proxy for Blocks instance {@link Blocks} + * + * @type {Proxy} + * @private + */ + this._blocks = null; + + /** + * Index of current working block + * + * @type {number} + * @private + */ + this.currentBlockIndex = -1; + } + + /** + * Should be called after Editor.UI preparation + * Define this._blocks property + * + * @returns {Promise} + */ + prepare() { + return new Promise(resolve => { + let blocks = new Blocks(this.Editor.UI.nodes.redactor); + + /** + * We need to use Proxy to overload set/get [] operator. + * So we can use array-like syntax to access blocks + * + * @example + * this._blocks[0] = new Block(...); + * + * block = this._blocks[0]; + * + * @todo proxy the enumerate method + * + * @type {Proxy} + * @private + */ + this._blocks = new Proxy(blocks, { + set: Blocks.set, + get: Blocks.get + }); + + resolve(); + }); + } + + /** + * Creates Block instance by tool name + * + * @param {String} toolName - tools passed in editor config {@link EditorConfig#tools} + * @param {Object} data - constructor params + * @param {Object} settings - block settings + * + * @return {Block} + */ + composeBlock(toolName, data, settings) { + let toolInstance = this.Editor.Tools.construct(toolName, data), + toolClass = this.Editor.Tools.available[toolName], + block = new Block(toolName, toolInstance, toolClass, settings, this.Editor.API.methods); + + this.bindEvents(block); + /** + * Apply callback before inserting html + */ + block.call('appendCallback', {}); + + return block; + } + + /** + * Bind Events + * @param {Object} block + */ + bindEvents(block) { + this.Editor.Listeners.on(block.holder, 'keydown', (event) => this.Editor.BlockEvents.keydown(event), true); + this.Editor.Listeners.on(block.holder, 'mouseup', (event) => this.Editor.BlockEvents.mouseUp(event)); + this.Editor.Listeners.on(block.holder, 'keyup', (event) => this.Editor.BlockEvents.keyup(event)); + } + + /** + * Insert new block into _blocks + * + * @param {String} toolName — plugin name, by default method inserts initial block type + * @param {Object} data — plugin data + * @param {Object} settings - default settings + * + * @return {Block} + */ + insert(toolName = this.config.initialBlock, data = {}, settings = {}) { + // Increment index before construct, + // because developers can use API/Blocks/getCurrentInputIndex on the render() method + let newIndex = ++this.currentBlockIndex; + let block = this.composeBlock(toolName, data, settings); + + this._blocks[newIndex] = block; + return block; + } + + /** + * Always inserts at the end + * @return {Block} + */ + insertAtEnd() { + /** + * Define new value for current block index + */ + this.currentBlockIndex = this.blocks.length - 1; + + /** + * Insert initial typed block + */ + return this.insert(); + } + + /** + * Merge two blocks + * @param {Block} targetBlock - previous block will be append to this block + * @param {Block} blockToMerge - block that will be merged with target block + * + * @return {Promise} - the sequence that can be continued + */ + mergeBlocks(targetBlock, blockToMerge) { + let blockToMergeIndex = this._blocks.indexOf(blockToMerge); + + return Promise.resolve() + .then( () => { + if (blockToMerge.isEmpty) { + return; + } + + return blockToMerge.data + .then((blockToMergeInfo) => { + targetBlock.mergeWith(blockToMergeInfo.data); + }); + }) + .then( () => { + this.removeBlock(blockToMergeIndex); + this.currentBlockIndex = this._blocks.indexOf(targetBlock); + }); + } + + /** + * Remove block with passed index or remove last + * @param {Number|null} index + */ + removeBlock(index) { + if (!index) { + index = this.currentBlockIndex; + } + this._blocks.remove(index); + + /** + * If first Block was removed, insert new Initial Block and set focus on it`s first input + */ + if (!this.blocks.length) { + this.currentBlockIndex = -1; + this.insert(); + this.currentBlock.firstInput.focus(); + } + } + + /** + * Split current Block + * 1. Extract content from Caret position to the Block`s end + * 2. Insert a new Block below current one with extracted content + * + * @return {Block} + */ + split() { + let extractedFragment = this.Editor.Caret.extractFragmentFromCaretPosition(), + wrapper = $.make('div'); + + wrapper.append(extractedFragment); + + /** + * @todo make object in accordance with Tool + */ + let data = { + text: $.isEmpty(wrapper) ? '' : wrapper.innerHTML, + }; + + /** + * Renew current Block + * @type {Block} + */ + return this.insert(this.config.initialBlock, data); + } + + /** + * Replace current working block + * + * @param {String} toolName — plugin name + * @param {Object} data — plugin data + * + * @return {Block} + */ + replace(toolName, data = {}) { + let block = this.composeBlock(toolName, data); + + this._blocks.insert(this.currentBlockIndex, block, true); + + return block; + } + + /** + * returns last Block + * @return {Block} + */ + get lastBlock() { + return this._blocks[this._blocks.length - 1]; + } + + /** + * Returns Block by passed index + * @param {Number} index + * @return {Block} + */ + getBlockByIndex(index) { + return this._blocks[index]; + } + + /** + * Get Block instance by html element + * @param {Node} element + * @returns {Block} + */ + getBlock(element) { + if (!$.isElement(element)) { + element = element.parentNode; + } + + let nodes = this._blocks.nodes, + firstLevelBlock = element.closest(`.${Block.CSS.wrapper}`), + index = nodes.indexOf(firstLevelBlock); + + if (index >= 0) { + return this._blocks[index]; + } + } + + /** + * Get current Block instance + * + * @return {Block} + */ + get currentBlock() { + return this._blocks[this.currentBlockIndex]; + } + + /** + * Returns next Block instance + * @return {Block|null} + */ + get nextBlock() { + let isLastBlock = this.currentBlockIndex === (this._blocks.length - 1); + + if (isLastBlock) { + return null; + } + + return this._blocks[this.currentBlockIndex + 1]; + } + + /** + * Return first Block with inputs after current Block + * + * @returns {Block | undefined} + */ + get nextContentfulBlock() { + const nextBlocks = this.blocks.slice(this.currentBlockIndex + 1); + + return nextBlocks.find(block => !!block.inputs.length); + } + + /** + * Return first Block with inputs before current Block + * + * @returns {Block | undefined} + */ + get previousContentfulBlock() { + const previousBlocks = this.blocks.slice(0, this.currentBlockIndex).reverse(); + + return previousBlocks.find(block => !!block.inputs.length); + } + + /** + * Returns previous Block instance + * @return {Block|null} + */ + get previousBlock() { + let isFirstBlock = this.currentBlockIndex === 0; + + if (isFirstBlock) { + return null; + } + + return this._blocks[this.currentBlockIndex - 1]; + } + + /** + * Get working html element + * + * @return {HTMLElement} + */ + get currentNode() { + return this._blocks.nodes[this.currentBlockIndex]; + } + + /** + * Set currentBlockIndex to passed block + * @param {Node} element + */ + set currentNode(element) { + if (!$.isElement(element)) { + element = element.parentNode; + } + + let nodes = this._blocks.nodes, + firstLevelBlock = element.closest(`.${Block.CSS.wrapper}`); + + if (!firstLevelBlock) { + throw Error('Passed element is not a Block.'); + } + + + /** + * Update current Block's index + * @type {number} + */ + this.currentBlockIndex = nodes.indexOf(firstLevelBlock); + } + + /** + * Remove selection from all Blocks then highlight only Current Block + */ + highlightCurrentNode() { + /** + * Remove previous selected Block's state + */ + this.clearHighlightings(); + + /** + * Mark current Block as selected + * @type {boolean} + */ + this.currentBlock.selected = true; + } + + /** + * Remove selection from all Blocks + */ + clearHighlightings() { + this.blocks.forEach( block => block.selected = false); + } + + /** + * Get array of Block instances + * + * @returns {Block[]} {@link Blocks#array} + */ + get blocks() { + return this._blocks.array; + } + + /** + * 1) Find first-level Block from passed child Node + * 2) Mark it as current + * + * @param {Element|Text} childNode - look ahead from this node. + * @throws Error - when passed Node is not included at the Block + */ + setCurrentBlockByChildNode(childNode) { + /** + * If node is Text TextNode + */ + if (!$.isElement(childNode)) { + childNode = childNode.parentNode; + } + + let parentFirstLevelBlock = childNode.closest(`.${Block.CSS.wrapper}`); + + if (parentFirstLevelBlock) { + this.currentNode = parentFirstLevelBlock; + this.Editor.Caret.setToInput(childNode); + } else { + throw new Error('Can not find a Block from this child Node'); + } + } + + /** + * Swap Blocks Position + * @param {Number} fromIndex + * @param {Number} toIndex + */ + swap(fromIndex, toIndex) { + /** Move up current Block */ + this._blocks.swap(fromIndex, toIndex); + + /** Now actual block moved up so that current block index decreased */ + this.currentBlockIndex = toIndex; + } + + /** + * Sets current Block Index -1 which means unknown + * and clear highlightings + */ + dropPointer() { + this.currentBlockIndex = -1; + this.clearHighlightings(); + } + + /** + * Clears Editor + * @param {boolean} needAddInitialBlock - 1) in internal calls (for example, in api.blocks.render) + * we don't need to add empty initial block + * 2) in api.blocks.clear we should add empty block + */ + clear(needAddInitialBlock = false) { + this._blocks.removeAll(); + this.dropPointer(); + + if (needAddInitialBlock) { + this.insert(this.config.initialBlock); + } + } +}; + +/** + * @class Blocks + * @classdesc Class to work with Block instances array + * + * @private + * + * @property {HTMLElement} workingArea — editor`s working node + * + */ +class Blocks { + /** + * @constructor + * + * @param {HTMLElement} workingArea — editor`s working node + */ + constructor(workingArea) { + this.blocks = []; + this.workingArea = workingArea; + } + + /** + * Push back new Block + * + * @param {Block} block + */ + push(block) { + this.blocks.push(block); + this.workingArea.appendChild(block.holder); + } + + /** + * Swaps blocks with indexes first and second + * @param {Number} first - first block index + * @param {Number} second - second block index + */ + swap(first, second) { + let secondBlock = this.blocks[second]; + + /** + * Change in DOM + */ + $.swap(this.blocks[first].holder, secondBlock.holder); + + /** + * Change in array + */ + this.blocks[second] = this.blocks[first]; + this.blocks[first] = secondBlock; + } + + /** + * Insert new Block at passed index + * + * @param {Number} index — index to insert Block + * @param {Block} block — Block to insert + * @param {Boolean} replace — it true, replace block on given index + */ + insert(index, block, replace = false) { + if (!this.length) { + this.push(block); + return; + } + + if (index > this.length) { + index = this.length; + } + + if (replace) { + this.blocks[index].holder.remove(); + } + + let deleteCount = replace ? 1 : 0; + + this.blocks.splice(index, deleteCount, block); + + if (index > 0) { + let previousBlock = this.blocks[index - 1]; + + previousBlock.holder.insertAdjacentElement('afterend', block.holder); + } else { + let nextBlock = this.blocks[index + 1]; + + if (nextBlock) { + nextBlock.holder.insertAdjacentElement('beforebegin', block.holder); + } else { + this.workingArea.appendChild(block.holder); + } + } + } + + /** + * Remove block + * @param {Number|null} index + */ + remove(index) { + if (isNaN(index)) { + index = this.length - 1; + } + + this.blocks[index].holder.remove(); + this.blocks.splice(index, 1); + } + + /** + * Remove all blocks + */ + removeAll() { + this.workingArea.innerHTML = ''; + this.blocks.length = 0; + } + + /** + * Insert Block after passed target + * + * @todo decide if this method is necessary + * + * @param {Block} targetBlock — target after wich Block should be inserted + * @param {Block} newBlock — Block to insert + */ + insertAfter(targetBlock, newBlock) { + let index = this.blocks.indexOf(targetBlock); + + this.insert(index + 1, newBlock); + } + + /** + * Get Block by index + * + * @param {Number} index — Block index + * @returns {Block} + */ + get(index) { + return this.blocks[index]; + } + + /** + * Return index of passed Block + * + * @param {Block} block + * @returns {Number} + */ + indexOf(block) { + return this.blocks.indexOf(block); + } + + /** + * Get length of Block instances array + * + * @returns {Number} + */ + get length() { + return this.blocks.length; + } + + /** + * Get Block instances array + * + * @returns {Block[]} + */ + get array() { + return this.blocks; + } + + /** + * Get blocks html elements array + * + * @returns {HTMLElement[]} + */ + get nodes() { + return _.array(this.workingArea.children); + } + + /** + * Proxy trap to implement array-like setter + * + * @example + * blocks[0] = new Block(...) + * + * @param {Blocks} instance — Blocks instance + * @param {Number|String} index — block index + * @param {Block} block — Block to set + * @returns {Boolean} + */ + static set(instance, index, block) { + if (isNaN(Number(index))) { + return false; + } + + instance.insert(index, block); + + return true; + } + + /** + * Proxy trap to implement array-like getter + * + * @param {Blocks} instance — Blocks instance + * @param {Number|String} index — Block index + * @returns {Block|*} + */ + static get(instance, index) { + if (isNaN(Number(index))) { + return instance[index]; + } + + return instance.get(index); + } +} diff --git a/src/components/modules/caret.js b/src/components/modules/caret.js new file mode 100644 index 00000000..f0b8b8c5 --- /dev/null +++ b/src/components/modules/caret.js @@ -0,0 +1,469 @@ +/** + * @class Caret + * @classdesc Contains methods for working Caret + * + * Uses Range methods to manipulate with caret + * + * @module Caret + * + * @version 2.0.0 + */ + +import Selection from '../selection'; + +/** + * @typedef {Caret} Caret + */ +export default class Caret extends Module { + /** + * @constructor + */ + constructor({config}) { + super({config}); + } + + /** + * Elements styles that can be useful for Caret Module + */ + static get CSS() { + return { + shadowCaret: 'cdx-shadow-caret' + }; + }; + + /** + * Allowed caret positions in input + * + * @static + * @returns {{START: string, END: string, DEFAULT: string}} + */ + static get positions() { + return { + START: 'start', + END: 'end', + DEFAULT: 'default' + }; + } + + /** + * Method gets Block instance and puts caret to the text node with offset + * There two ways that method applies caret position: + * - first found text node: sets at the beginning, but you can pass an offset + * - last found text node: sets at the end of the node. Also, you can customize the behaviour + * + * @param {Block} block - Block class + * @param {String} position - position where to set caret. If default - leave default behaviour and apply offset if it's passed + * @param {Number} offset - caret offset regarding to the text node + */ + setToBlock(block, position = Caret.positions.DEFAULT, offset = 0) { + const {BlockManager} = this.Editor; + let element; + + switch(position) { + case Caret.positions.START: + element = block.firstInput; + break; + case Caret.positions.END: + element = block.lastInput; + break; + default: + element = block.currentInput; + } + + if (!element) { + return; + } + + const nodeToSet = $.getDeepestNode(element, position === Caret.positions.END); + const contentLength = $.getContentLength(nodeToSet); + + switch (true) { + case position === Caret.positions.START: + offset = 0; + break; + case position === Caret.positions.END: + case offset > contentLength: + offset = contentLength; + break; + } + + /** + * @todo try to fix via Promises or use querySelectorAll to not to use timeout + */ + _.delay( () => { + this.set(nodeToSet, offset); + }, 20)(); + + BlockManager.currentNode = block.holder; + BlockManager.currentBlock.currentInput = element; + } + + /** + * Set caret to the current input of current Block. + * + * @param {HTMLElement} input - input where caret should be set + * @param {String} position - position of the caret. If default - leave default behaviour and apply offset if it's passed + * @param {number} offset - caret offset regarding to the text node + */ + setToInput(input, position = Caret.positions.DEFAULT, offset = 0) { + const {currentBlock} = this.Editor.BlockManager; + const nodeToSet = $.getDeepestNode(input); + + switch (position) { + case Caret.positions.START: + this.set(nodeToSet, 0); + break; + + case Caret.positions.END: + const contentLength = $.getContentLength(nodeToSet); + + this.set(nodeToSet, contentLength); + break; + + default: + if (offset) { + this.set(nodeToSet, offset); + } + } + + currentBlock.currentInput = input; + } + + /** + * Creates Document Range and sets caret to the element with offset + * @param {Element} element - target node. + * @param {Number} offset - offset + */ + set( element, offset = 0) { + const range = document.createRange(), + selection = Selection.get(); + + /** if found deepest node is native input */ + if ($.isNativeInput(element)) { + element.focus(); + element.selectionStart = element.selectionEnd = offset; + return; + } + + range.setStart(element, offset); + range.setEnd(element, offset); + + selection.removeAllRanges(); + selection.addRange(range); + + + /** If new cursor position is not visible, scroll to it */ + const {top, bottom} = range.getBoundingClientRect(); + const {innerHeight} = window; + + if (top < 0) window.scrollBy(0, top); + if (bottom > innerHeight) window.scrollBy(0, bottom - innerHeight); + }; + + /** + * Set Caret to the last Block + * If last block is not empty, append another empty block + */ + setToTheLastBlock() { + let lastBlock = this.Editor.BlockManager.lastBlock; + + if (!lastBlock) return; + + /** + * If last block is empty and it is an initialBlock, set to that. + * Otherwise, append new empty block and set to that + */ + if (lastBlock.isEmpty) { + this.setToBlock(lastBlock); + } else { + const newBlock = this.Editor.BlockManager.insertAtEnd(); + + this.setToBlock(newBlock); + } + } + + /** + * Extract content fragment of current Block from Caret position to the end of the Block + */ + extractFragmentFromCaretPosition() { + let selection = Selection.get(); + + if (selection.rangeCount) { + const selectRange = selection.getRangeAt(0); + const currentBlockInput = this.Editor.BlockManager.currentBlock.currentInput; + + + selectRange.deleteContents(); + + if (currentBlockInput) { + let range = selectRange.cloneRange(true); + + range.selectNodeContents(currentBlockInput); + range.setStart(selectRange.endContainer, selectRange.endOffset); + return range.extractContents(); + } + } + } + + /** + * Get all first-level (first child of [contenteditabel]) siblings from passed node + * Then you can check it for emptiness + * + * @example + * + * + * @return {Element[]} + */ + getHigherLevelSiblings(from, direction ) { + let current = from, + siblings = []; + + /** + * Find passed node's firs-level parent (in example - blockquote) + */ + while (current.parentNode && current.parentNode.contentEditable !== 'true') { + current = current.parentNode; + } + + let sibling = direction === 'left' ? 'previousSibling' : 'nextSibling'; + + /** + * Find all left/right siblings + */ + while (current[sibling]) { + current = current[sibling]; + siblings.push(current); + } + + return siblings; + } + + /** + * Set's caret to the next Block or Tool`s input + * Before moving caret, we should check if caret position is at the end of Plugins node + * Using {@link Dom#getDeepestNode} to get a last node and match with current selection + * + * @param {Boolean} force - force navigation even if caret is not at the end + * + * @return {Boolean} + */ + navigateNext(force = false) { + const {currentBlock, nextContentfulBlock} = this.Editor.BlockManager; + const {nextInput} = currentBlock; + + if (!nextContentfulBlock && !nextInput) { + return false; + } + + if (force) { + this.setToBlock(nextContentfulBlock, Caret.positions.START); + return true; + } + + if (this.isAtEnd) { + /** If next Tool`s input exists, focus on it. Otherwise set caret to the next Block */ + if (!nextInput) { + this.setToBlock(nextContentfulBlock, Caret.positions.START); + } else { + this.setToInput(nextInput, Caret.positions.START); + } + + return true; + } + + return false; + } + + /** + * Set's caret to the previous Tool`s input or Block + * Before moving caret, we should check if caret position is start of the Plugins node + * Using {@link Dom#getDeepestNode} to get a last node and match with current selection + * + * @param {Boolean} force - force navigation even if caret is not at the start + * + * @return {Boolean} + */ + navigatePrevious(force = false) { + const {currentBlock, previousContentfulBlock} = this.Editor.BlockManager; + const {previousInput} = currentBlock || {}; + + if (!previousContentfulBlock && !previousInput) { + return false; + } + + if (force) { + this.setToBlock( previousContentfulBlock, Caret.positions.END ); + } + + if (this.isAtStart) { + /** If previous Tool`s input exists, focus on it. Otherwise set caret to the previous Block */ + if (!previousInput) { + this.setToBlock( previousContentfulBlock, Caret.positions.END ); + } else { + this.setToInput(previousInput, Caret.positions.END); + } + return true; + } + + return false; + } + + /** + * Get's deepest first node and checks if offset is zero + * @return {boolean} + */ + get isAtStart() { + /** + * Don't handle ranges + */ + if (!Selection.isCollapsed) { + return false; + } + + let selection = Selection.get(), + anchorNode = selection.anchorNode, + firstNode = $.getDeepestNode(this.Editor.BlockManager.currentBlock.currentInput); + + /** In case lastNode is native input */ + if ($.isNativeInput(firstNode)) { + return firstNode.selectionEnd === 0; + } + + /** + * Workaround case when caret in the text like " |Hello!" + * selection.anchorOffset is 1, but real caret visible position is 0 + * @type {number} + */ + let firstLetterPosition = anchorNode.textContent.search(/\S/); + + if (firstLetterPosition === -1) { // empty text + firstLetterPosition = 0; + } + + /** + * In case of + *
    + *

    <-- first (and deepest) node is + * |adaddad <-- anchor node + *
    + */ + if ($.isEmpty(firstNode)) { + let leftSiblings = this.getHigherLevelSiblings(anchorNode, 'left'), + nothingAtLeft = leftSiblings.every( node => $.isEmpty(node) ); + + + + if (nothingAtLeft && selection.anchorOffset === firstLetterPosition) { + return true; + } + } + + /** + * We use <= comparison for case: + * "| Hello" <--- selection.anchorOffset is 0, but firstLetterPosition is 1 + */ + return firstNode === null || anchorNode === firstNode && selection.anchorOffset <= firstLetterPosition; + } + + /** + * Get's deepest last node and checks if offset is last node text length + * @return {boolean} + */ + get isAtEnd() { + /** + * Don't handle ranges + */ + if (!Selection.isCollapsed) { + return false; + } + + let selection = Selection.get(), + anchorNode = selection.anchorNode, + lastNode = $.getDeepestNode(this.Editor.BlockManager.currentBlock.currentInput, true); + + /** In case lastNode is native input */ + if ($.isNativeInput(lastNode)) { + return lastNode.selectionEnd === lastNode.value.length; + } + + /** + * In case of + *
    + * adaddad| <-- anchor node + *

    <-- first (and deepest) node is + *
    + */ + if ($.isEmpty(lastNode)) { + let leftSiblings = this.getHigherLevelSiblings(anchorNode, 'right'), + nothingAtRight = leftSiblings.every( node => $.isEmpty(node) ); + + if (nothingAtRight && selection.anchorOffset === anchorNode.textContent.length) { + return true; + } + } + + /** + * Workaround case: + * hello | <--- anchorOffset will be 5, but textContent.length will be 6. + * Why not regular .trim(): + * in case of ' hello |' trim() will also remove space at the beginning, so length will be lower than anchorOffset + */ + let rightTrimmedText = lastNode.textContent.replace(/\s+$/, ''); + + /** + * We use >= comparison for case: + * "Hello |" <--- selection.anchorOffset is 7, but rightTrimmedText is 6 + */ + return anchorNode === lastNode && selection.anchorOffset >= rightTrimmedText.length; + } + + /** + * Inserts shadow element after passed element where caret can be placed + * @param {Node} element + */ + createShadow(element) { + let shadowCaret = document.createElement('span'); + + shadowCaret.classList.add(Caret.CSS.shadowCaret); + element.insertAdjacentElement('beforeEnd', shadowCaret); + } + + /** + * Restores caret position + * @param {Node} element + */ + restoreCaret(element) { + let shadowCaret = element.querySelector(`.${Caret.CSS.shadowCaret}`); + + if (!shadowCaret) { + return; + } + + /** + * After we set the caret to the required place + * we need to clear shadow caret + * + * - make new range + * - select shadowed span + * - use extractContent to remove it from DOM + */ + let sel = new Selection(); + + sel.expandToTag(shadowCaret); + + setTimeout(() => { + let newRange = document.createRange(); + + newRange.selectNode(shadowCaret); + newRange.extractContents(); + }, 50); + } +} diff --git a/src/components/modules/events.js b/src/components/modules/events.js new file mode 100644 index 00000000..5cb42283 --- /dev/null +++ b/src/components/modules/events.js @@ -0,0 +1,78 @@ +/** + * @module eventDispatcher + * + * Has two important methods: + * - {Function} on - appends subscriber to the event. If event doesn't exist - creates new one + * - {Function} emit - fires all subscribers with data + * - {Function off - unsubsribes callback + * + * @version 1.0.0 + * + * @typedef {Events} Events + * @property {Object} subscribers - all subscribers grouped by event name + */ +export default class Events extends Module { + /** + * @constructor + */ + constructor({config}) { + super({config}); + this.subscribers = {}; + } + + /** + * Subscribe any event on callback + * + * @param {String} eventName - event name + * @param {Function} callback - subscriber + */ + on(eventName, callback) { + if (!(eventName in this.subscribers)) { + this.subscribers[eventName] = []; + } + + // group by events + this.subscribers[eventName].push(callback); + } + + /** + * Emit callbacks with passed data + * + * @param {String} eventName - event name + * @param {Object} data - subscribers get this data when they were fired + */ + emit(eventName, data) { + if (!this.subscribers[eventName]) { + return; + } + + this.subscribers[eventName].reduce(function (previousData, currentHandler) { + let newData = currentHandler(previousData); + + return newData ? newData : previousData; + }, data); + } + + /** + * Unsubsribe callback from event + * + * @param eventName + * @param callback + */ + off(eventName, callback) { + for(let i = 0; i < this.subscribers[eventName].length; i++) { + if (this.subscribers[eventName][i] === callback) { + delete this.subscribers[eventName][i]; + break; + } + } + } + + /** + * Destroyer + * clears subsribers list + */ + destroy() { + this.subscribers = null; + } +} diff --git a/src/components/modules/listeners.js b/src/components/modules/listeners.js new file mode 100644 index 00000000..435d75e5 --- /dev/null +++ b/src/components/modules/listeners.js @@ -0,0 +1,174 @@ +/** + * Codex Editor Listeners module + * + * @module Listeners + * + * Module-decorator for event listeners assignment + * + * @author Codex Team + * @version 2.0.0 + */ + +/** + * @typedef {Listeners} Listeners + * @property {Array} allListeners + */ +export default class Listeners extends Module { + /** + * @constructor + * @param {EditorConfig} config + */ + constructor({config}) { + super({config}); + this.allListeners = []; + } + + /** + * Assigns event listener on element + * + * @param {Element} element - DOM element that needs to be listened + * @param {String} eventType - event type + * @param {Function} handler - method that will be fired on event + * @param {Boolean} useCapture - use event bubbling + */ + on(element, eventType, handler, useCapture = false) { + let assignedEventData = { + element, + eventType, + handler, + useCapture + }; + + let alreadyExist = this.findOne(element, eventType, handler); + + if (alreadyExist) return; + + this.allListeners.push(assignedEventData); + element.addEventListener(eventType, handler, useCapture); + } + + /** + * Removes event listener from element + * + * @param {Element} element - DOM element that we removing listener + * @param {String} eventType - event type + * @param {Function} handler - remove handler, if element listens several handlers on the same event type + * @param {Boolean} useCapture - use event bubbling + */ + off(element, eventType, handler, useCapture = false) { + let existingListeners = this.findAll(element, eventType, handler); + + for (let i = 0; i < existingListeners.length; i++) { + let index = this.allListeners.indexOf(existingListeners[i]); + + if (index > 0) { + this.allListeners.splice(index, 1); + } + } + + element.removeEventListener(eventType, handler, useCapture); + } + + /** + * Search method: looks for listener by passed element + * @param {Element} element - searching element + * @returns {Array} listeners that found on element + */ + findByElement(element) { + let listenersOnElement = []; + + for (let i = 0; i < this.allListeners.length; i++) { + let listener = this.allListeners[i]; + + if (listener.element === element) { + listenersOnElement.push(listener); + } + } + + return listenersOnElement; + } + + /** + * Search method: looks for listener by passed event type + * @param {String} eventType + * @return {Array} listeners that found on element + */ + findByType(eventType) { + let listenersWithType = []; + + for (let i = 0; i < this.allListeners.length; i++) { + let listener = this.allListeners[i]; + + if (listener.type === eventType) { + listenersWithType.push(listener); + } + } + + return listenersWithType; + } + + /** + * Search method: looks for listener by passed handler + * @param {Function} handler + * @return {Array} listeners that found on element + */ + findByHandler(handler) { + let listenersWithHandler = []; + + for (let i = 0; i < this.allListeners.length; i++) { + let listener = this.allListeners[i]; + + if (listener.handler === handler) { + listenersWithHandler.push(listener); + } + } + + return listenersWithHandler; + } + + /** + * @param {Element} element + * @param {String} eventType + * @param {Function} handler + * @return {Element|null} + */ + findOne(element, eventType, handler) { + let foundListeners = this.findAll(element, eventType, handler); + + return foundListeners.length > 0 ? foundListeners[0] : null; + } + + /** + * @param {Element} element + * @param {String} eventType + * @param {Function} handler + * @return {Array} + */ + findAll(element, eventType, handler) { + let found, + foundByElements = element ? this.findByElement(element) : []; + // foundByEventType = eventType ? this.findByType(eventType) : [], + // foundByHandler = handler ? this.findByHandler(handler) : []; + + if (element && eventType && handler) { + found = foundByElements.filter( event => event.eventType === eventType && event.handler === handler ); + } else if (element && eventType) { + found = foundByElements.filter( event => event.eventType === eventType); + } else { + found = foundByElements; + } + + return found; + } + + /** + * Removes all listeners + */ + removeAll() { + this.allListeners.map( (current) => { + current.element.removeEventListener(current.eventType, current.handler); + }); + + this.allListeners = []; + } +} diff --git a/src/components/modules/paste.ts b/src/components/modules/paste.ts new file mode 100644 index 00000000..2e51ce93 --- /dev/null +++ b/src/components/modules/paste.ts @@ -0,0 +1,490 @@ +import IBlockToolData from '../interfaces/tools/block-tool'; +import IEditorConfig from '../interfaces/editor-config'; +import CaretClass from './caret'; + +declare const Module: any; +declare const $: any; +declare const _: any; + +/** + * Tag substitute object. + * + * @param {string} tool - name of related Tool + * @param {Function} handler - callback to handle pasted element + */ +interface ITagSubstitute { + tool: string; + handler: (element: HTMLElement) => IBlockToolData; +} + +/** + * Pattern substitute object. + * + * @param {string} key - pattern`s key + * @param {RegExp} pattern - pasted pattern + * @param {Function} handler - callback to handle pasted pattern + * @param {string} tool - name of related Tool + */ +interface IPatternSubstitute { + key: string; + pattern: RegExp; + handler: (text: string, key: string) => IBlockToolData; + tool: string; +} + +/** + * Processed paste data object. + * + * @param {string} tool - name of related Tool + * @param {HTMLElement} content - processed pasted content + * @param {boolean} isBlock - true if content should be inserted as new Block + * @param {Function} handler - callback that returns pasted data in IBlockToolData format + */ +interface IPasteData { + tool: string; + content: HTMLElement; + isBlock: boolean; + handler: (content: HTMLElement|string, patten?: RegExp) => IBlockToolData; +} +/** + * @class Paste + * @classdesc Contains methods to handle paste on editor + * + * @module Paste + * + * @version 2.0.0 + */ +export default class Paste extends Module { + + /** If string`s length is greater than this number we don't check paste patterns */ + public static readonly PATTERN_PROCESSING_MAX_LENGTH = 450; + + /** + * Tags` substitutions parameters + */ + private toolsTags: {[tag: string]: ITagSubstitute} = {}; + + /** Patterns` substitutions parameters */ + private toolsPatterns: IPatternSubstitute[] = []; + + /** + * @constructor + * @param {IEditorConfig} config + */ + constructor({config}) { + super({config}); + } + + public async prepare(): Promise { + this.setCallback(); + this.processTools(); + } + + /** + * Set onPaste callback handler + */ + private setCallback(): void { + const {Listeners, UI} = this.Editor; + + Listeners.on(UI.nodes.redactor, 'paste', this.processPastedData); + } + + /** + * Get and process tool`s paste configs + */ + private processTools(): void { + const tools = this.Editor.Tools.blockTools; + + Object.entries(tools).forEach(this.processTool); + } + + /** + * Process paste config for each tools + * + * @param {string} tool + */ + private processTool = ([name, tool]) => { + const toolPasteConfig = tool.onPaste || {}; + + if (this.config.initialBlock === name && !toolPasteConfig.handler) { + _.log( + `«${name}» Tool must provide a paste handler.`, + 'warn', + ); + } + + if (toolPasteConfig.handler && typeof toolPasteConfig.handler !== 'function') { + _.log( + `Paste handler for «${name}» Tool should be a function.`, + 'warn', + ); + } else { + const tags = toolPasteConfig.tags || []; + + tags.forEach((tag) => { + if (this.toolsTags.hasOwnProperty(tag)) { + _.log( + `Paste handler for «${name}» Tool on «${tag}» tag is skipped ` + + `because it is already used by «${this.toolsTags[tag].tool}» Tool.`, + 'warn', + ); + return; + } + + this.toolsTags[tag] = { + handler: toolPasteConfig.handler, + tool: name, + }; + }); + } + + if (!toolPasteConfig.patternHandler || _.isEmpty(toolPasteConfig.patterns)) { + return; + } + + if (typeof toolPasteConfig.patternHandler !== 'function') { + _.log( + `Pattern parser for "${name}" Tool should be a function.`, + 'warn', + ); + } else { + Object.entries(toolPasteConfig.patterns).forEach(([key, pattern]: [string, RegExp]) => { + /** Still need to validate pattern as it provided by user */ + if (!(pattern instanceof RegExp)) { + _.log( + `Pattern ${pattern} for "${tool}" Tool is skipped because it should be a Regexp instance.`, + 'warn', + ); + } + + this.toolsPatterns.push({ + key, + pattern, + handler: toolPasteConfig.patternHandler, + tool: name, + }); + }); + } + } + + /** + * Check if browser behavior suits better + * + * @param {EventTarget} element - element where content has been pasted + * @returns {boolean} + */ + private isNativeBehaviour(element: EventTarget): boolean { + const {Editor: {BlockManager}} = this; + + if ( $.isNativeInput(element) ) { + return true; + } + + const block = BlockManager.getBlock(element); + + return !block; + } + + /** + * Get pasted data, process it and insert into editor + * + * @param {ClipboardEvent} event + */ + private processPastedData = async (event: ClipboardEvent): Promise => { + const { + Editor: {Tools, Sanitizer, BlockManager, Caret}, + } = this; + + /** If target is native input or is not Block, use browser behaviour */ + if (this.isNativeBehaviour(event.target)) { + return; + } + + event.preventDefault(); + + const htmlData = event.clipboardData.getData('text/html'), + plainData = event.clipboardData.getData('text/plain'); + + /** Add all tags that can be substituted to sanitizer configuration */ + const toolsTags = Object.keys(this.toolsTags).reduce((result, tag) => { + result[tag.toLowerCase()] = {}; + + return result; + }, {}); + + const customConfig = {tags: Object.assign({}, toolsTags, Sanitizer.defaultConfig.tags)}; + const cleanData = Sanitizer.clean(htmlData, customConfig); + + let dataToInsert = []; + + /** If there is no HTML or HTML string is equal to plain one, process it as plain text */ + if (!cleanData.trim() || cleanData.trim() === plainData || !$.isHTMLString(cleanData)) { + dataToInsert = this.processPlain(plainData); + } else { + dataToInsert = this.processHTML(htmlData); + } + + if (dataToInsert.length === 1 && !dataToInsert[0].isBlock) { + this.processSingleBlock(dataToInsert.pop()); + return; + } + + this.splitBlock(); + + await Promise.all(dataToInsert.map( + async (data, i) => await this.insertBlock(data, i === 0), + )); + + Caret.setToBlock(BlockManager.currentBlock, CaretClass.positions.END); + } + + /** + * Process paste to single Block: + * 1. Find patterns` matches + * 2. Insert new block if it is not the same type as current one + * 3. Just insert text if there is no substitutions + * + * @param {IPasteData} dataToInsert + */ + private async processSingleBlock(dataToInsert: IPasteData): Promise { + const initialTool = this.config.initialBlock, + {BlockManager, Caret} = this.Editor, + {content, tool} = dataToInsert; + + if (tool === initialTool && content.textContent.length < Paste.PATTERN_PROCESSING_MAX_LENGTH) { + const blockData = await this.processPattern(content.textContent); + + if (blockData) { + this.splitBlock(); + let insertedBlock; + + if (BlockManager.currentBlock && BlockManager.currentBlock.isEmpty) { + BlockManager.replace(blockData.tool, blockData.data); + } else { + insertedBlock = BlockManager.insert(blockData.tool, blockData.data); + } + Caret.setToBlock(insertedBlock, CaretClass.positions.END); + return; + } + } + + /** If there is no pattern substitute - insert string as it is */ + document.execCommand('insertHTML', false, content.innerHTML); + } + + /** + * Get patterns` matches + * + * @param {string} text + * @returns Promise<{data: IBlockToolData, tool: string}> + */ + private async processPattern(text: string): Promise<{data: IBlockToolData, tool: string}> { + const pattern = this.toolsPatterns.find((substitute) => { + const execResult = substitute.pattern.exec(text); + + if (!execResult) { + return false; + } + + return text === execResult.shift(); + }); + + const data = pattern && await pattern.handler(text, pattern.key); + + return data && { + data, + tool: pattern.tool, + }; + } + + /** + * + * @param {IPasteData} data + * @param {Boolean} canReplaceCurrentBlock - if true and is current Block is empty, will replace current Block + * @returns {Promise} + */ + private async insertBlock(data: IPasteData, canReplaceCurrentBlock: boolean = false): Promise { + const blockData = await data.handler(data.content), + {BlockManager, Caret} = this.Editor, + {currentBlock} = BlockManager; + + if (canReplaceCurrentBlock && currentBlock && currentBlock.isEmpty) { + BlockManager.replace(data.tool, blockData); + return; + } + + const Block = BlockManager.insert(data.tool, blockData); + + Caret.setToBlock(Block); + } + + /** + * Split current block if paste isn't in the end of the block + */ + private splitBlock() { + const {BlockManager, Caret} = this.Editor; + + if (!BlockManager.currentBlock) { + return; + } + + /** If we paste into middle of the current block: + * 1. Split + * 2. Navigate to the first part + */ + if (!BlockManager.currentBlock.isEmpty && !Caret.isAtEnd) { + BlockManager.split(); + BlockManager.currentBlockIndex--; + } + } + + /** + * Split HTML string to blocks and return it as array of Block data + * + * @param {string} innerHTML + * @returns {IPasteData[]} + */ + private processHTML(innerHTML: string): IPasteData[] { + const {Tools, Sanitizer} = this.Editor, + initialTool = this.config.initialBlock, + wrapper = $.make('DIV'); + + wrapper.innerHTML = innerHTML; + + const nodes = this.getNodes(wrapper); + + return nodes + .map((node) => { + let content, tool = initialTool, isBlock = false; + + switch (node.nodeType) { + /** If node is a document fragment, use temp wrapper to get innerHTML */ + case Node.DOCUMENT_FRAGMENT_NODE: + content = $.make('div'); + content.appendChild(node); + break; + + /** If node is an element, then there might be a substitution */ + case Node.ELEMENT_NODE: + content = node as HTMLElement; + isBlock = true; + + if (this.toolsTags[content.tagName]) { + tool = this.toolsTags[content.tagName].tool; + } + break; + } + + const {handler, tags} = Tools.blockTools[tool].onPaste; + + const toolTags = tags.reduce((result, tag) => { + result[tag.toLowerCase()] = {}; + + return result; + }, {}); + const customConfig = {tags: Object.assign({}, toolTags, Sanitizer.defaultConfig.tags)}; + + content.innerHTML = Sanitizer.clean(content.innerHTML, customConfig); + + return {content, isBlock, handler, tool}; + }) + .filter((data) => !$.isNodeEmpty(data.content)); + } + + /** + * Split plain text by new line symbols and return it as array of Block data + * + * @param {string} plain + * @returns {IPasteData[]} + */ + private processPlain(plain: string): IPasteData[] { + const {initialBlock} = this.config as {initialBlock: string}, + {Tools} = this.Editor; + + if (!plain) { + return []; + } + + const tool = initialBlock, + handler = Tools.blockTools[tool].onPaste.handler; + + return plain.split('\n\n').map((text) => { + const content = $.make('div'); + + content.innerHTML = text; + + return {content, tool, isBlock: false, handler}; + }); + } + + /** + * Recursively divide HTML string to two types of nodes: + * 1. Block element + * 2. Document Fragments contained text and markup tags like a, b, i etc. + * + * @param {Node} wrapper + * @returns {Node[]} + */ + private getNodes(wrapper: Node): Node[] { + const children = Array.from(wrapper.childNodes), + tags = Object.keys(this.toolsTags); + + const reducer = (nodes: Node[], node: Node): Node[] => { + if ($.isEmpty(node)) { + return nodes; + } + + const lastNode = nodes[nodes.length - 1]; + + let destNode: Node = new DocumentFragment(); + + if (lastNode && $.isFragment(lastNode)) { + destNode = nodes.pop(); + } + + switch (node.nodeType) { + /** + * If node is HTML element: + * 1. Check if it is inline element + * 2. Check if it contains another block or substitutable elements + */ + case Node.ELEMENT_NODE: + const element = node as HTMLElement; + + /** Append inline elements to previous fragment */ + if ( + !$.blockElements.includes(element.tagName.toLowerCase()) && + !tags.includes(element.tagName) + ) { + destNode.appendChild(element); + return [...nodes, destNode]; + } + + if (tags.includes(element.tagName) || ( + $.blockElements.includes(element.tagName.toLowerCase()) && + Array.from(element.children).every( + ({tagName}) => !$.blockElements.includes(tagName.toLowerCase()), + ) + ) + ) { + return [...nodes, element]; + } + break; + + /** + * If node is text node, wrap it with DocumentFragment + */ + case Node.TEXT_NODE: + destNode.appendChild(node); + return [...nodes, destNode]; + + default: + return [...nodes, destNode]; + } + + return [...nodes, ...Array.from(node.childNodes).reduce(reducer, [])]; + }; + + return children.reduce(reducer, []); + } +} diff --git a/src/components/modules/renderer.js b/src/components/modules/renderer.js new file mode 100644 index 00000000..81f7a809 --- /dev/null +++ b/src/components/modules/renderer.js @@ -0,0 +1,92 @@ +/** + * Codex Editor Renderer Module + * + * @module Renderer + * @author CodeX Team + * + * @version 2.0.0 + */ +export default class Renderer extends Module { + /** + * @constructor + * @param {EditorConfig} config + */ + constructor({config}) { + super({config}); + } + + /** + * @typedef {Object} RendererBlocks + * @property {String} type - tool name + * @property {Object} data - tool data + */ + + /** + * @example + * + * blocks: [ + * { + * type : 'paragraph', + * data : { + * text : 'Hello from Codex!' + * } + * }, + * { + * type : 'paragraph', + * data : { + * text : 'Leave feedback if you like it!' + * } + * }, + * ] + * + */ + + /** + * Make plugin blocks from array of plugin`s data + * @param {RendererBlocks[]} blocks + */ + render(blocks) { + let chainData = []; + + for (let i = 0; i < blocks.length; i++) { + chainData.push({ + function: () => this.insertBlock(blocks[i]) + }); + } + + return _.sequence(chainData); + } + + /** + * Get plugin instance + * Add plugin instance to BlockManager + * Insert block to working zone + * + * @param {Object} item + * @returns {Promise.} + * @private + */ + insertBlock(item) { + let tool = item.type, + data = item.data, + settings = item.settings; + + if (tool in this.Editor.Tools.available) { + try { + this.Editor.BlockManager.insert(tool, data, settings); + } catch (error) { + _.log(`Block «${tool}» skipped because of plugins error`, 'warn', data); + Promise.reject(error); + } + } else { + /** + * @todo show warning notification message + * + * `${tool} blocks was skipped.` + */ + _.log(`Tool «${tool}» is not found. Check 'tools' property at your initial CodeX Editor config.`, 'warn'); + } + + return Promise.resolve(); + } +} diff --git a/src/components/modules/sanitizer.js b/src/components/modules/sanitizer.js new file mode 100644 index 00000000..b0cb51d6 --- /dev/null +++ b/src/components/modules/sanitizer.js @@ -0,0 +1,131 @@ +/** + * CodeX Sanitizer + * + * @module Sanitizer + * Clears HTML from taint tags + * + * @version 2.0.0 + * + * @example + * Module can be used within two ways: + * 1) When you have an instance + * - this.Editor.Sanitizer.clean(yourTaintString); + * 2) As static method + * - CodexEditor.Sanitizer.clean(yourTaintString, yourCustomConfiguration); + * + * {@link SanitizerConfig} + */ + + +/** + * @typedef {Object} SanitizerConfig + * @property {Object} tags - define tags restrictions + * + * @example + * + * tags : { + * p: true, + * a: { + * href: true, + * rel: "nofollow", + * target: "_blank" + * } + * } + */ +export default class Sanitizer extends Module { + /** + * Initializes Sanitizer module + * Sets default configuration if custom not exists + * + * @property {SanitizerConfig} this.defaultConfig + * @property {HTMLJanitor} this._sanitizerInstance - Sanitizer library + * + * @param {SanitizerConfig} config + */ + constructor({config}) { + super({config}); + + // default config + this.defaultConfig = null; + this._sanitizerInstance = null; + + /** Custom configuration */ + this.sanitizerConfig = config.settings ? config.settings.sanitizer : {}; + + /** HTML Janitor library */ + this.sanitizerInstance = require('html-janitor'); + } + + /** + * If developer uses editor's API, then he can customize sanitize restrictions. + * Or, sanitizing config can be defined globally in editors initialization. That config will be used everywhere + * At least, if there is no config overrides, that API uses Default configuration + * + * @uses https://www.npmjs.com/package/html-janitor + * + * @param {HTMLJanitor} library - sanitizer extension + */ + set sanitizerInstance(library) { + this._sanitizerInstance = new library(this.defaultConfig); + } + + /** + * Sets sanitizer configuration. Uses default config if user didn't pass the restriction + * @param {SanitizerConfig} config + */ + set sanitizerConfig(config) { + if (_.isEmpty(config)) { + this.defaultConfig = { + tags: { + p: {}, + a: { + href: true, + target: '_blank', + rel: 'nofollow' + }, + b: {}, + i: {} + } + }; + } else { + this.defaultConfig = config; + } + } + + /** + * Cleans string from unwanted tags + * @param {String} taintString - HTML string + * @param {Object} customConfig - custom sanitizer configuration. Method uses default if param is empty + * @return {String} clean HTML + */ + clean(taintString, customConfig = {}) { + if (_.isEmpty(customConfig)) { + return this._sanitizerInstance.clean(taintString); + } else { + return Sanitizer.clean(taintString, customConfig); + } + } + + /** + * Cleans string from unwanted tags + * @static + * + * Method allows to use default config + * + * @param {String} taintString - taint string + * @param {SanitizerConfig} customConfig - allowed tags + * + * @return {String} clean HTML + */ + static clean(taintString, customConfig) { + let newInstance = new Sanitizer({ + config: { + settings: { + sanitizer: customConfig + } + } + }); + + return newInstance.clean(taintString); + } +} diff --git a/src/components/modules/saver.js b/src/components/modules/saver.js new file mode 100644 index 00000000..c0e007e0 --- /dev/null +++ b/src/components/modules/saver.js @@ -0,0 +1,243 @@ +/** + * Codex Editor Saver + * + * @module Saver + * @author Codex Team + * @version 2.0.0 + */ + +/** + * @typedef {Object} SavedData + * @property {Date} time - saving proccess time + * @property {Object} items - extracted data + * @property {String} version - CodexEditor version + */ + +/** + * @classdesc This method reduces all Blocks asyncronically and calls Block's save method to extract data + * + * @typedef {Saver} Saver + * @property {Element} html - Editor HTML content + * @property {String} json - Editor JSON output + */ +export default class Saver extends Module { + /** + * @constructor + * @param config + */ + constructor({config}) { + super({config}); + + this.output = null; + this.blocksData = []; + } + + /** + * Composes new chain of Promises to fire them alternatelly + * @return {SavedData} + */ + save() { + let blocks = this.Editor.BlockManager.blocks, + chainData = []; + + blocks.forEach((block) => { + chainData.push(block.data); + }); + + return Promise.all(chainData) + .then((allExtractedData) => this.makeOutput(allExtractedData)) + .then((outputData) => { + return outputData; + }); + } + + /** + * Creates output object with saved data, time and version of editor + * @param {Object} allExtractedData + * @return {SavedData} + */ + makeOutput(allExtractedData) { + let blocks = [], + totalTime = 0; + + console.groupCollapsed('[CodexEditor saving]:'); + + allExtractedData.forEach((extraction) => { + /** Group process info */ + console.log(`«${extraction.tool}» saving info`, extraction); + totalTime += extraction.time; + blocks.push({ + type: extraction.tool, + data: extraction.data + }); + }); + + console.log('Total', totalTime); + console.groupEnd(); + + return { + time: +new Date(), + blocks: blocks, + version: VERSION, + }; + } +} + +// module.exports = (function (saver) { +// +// let editor = codex.editor; +// +// /** +// * @public +// * Save blocks +// */ +// saver.save = function () { +// +// /** Save html content of redactor to memory */ +// editor.state.html = editor.nodes.redactor.innerHTML; +// +// /** Clean jsonOutput state */ +// editor.state.jsonOutput = []; +// +// return saveBlocks(editor.nodes.redactor.childNodes); +// +// }; +// +// /** +// * @private +// * Save each block data +// * +// * @param blocks +// * @returns {Promise.} +// */ +// let saveBlocks = function (blocks) { +// +// let data = []; +// +// for(let index = 0; index < blocks.length; index++) { +// +// data.push(getBlockData(blocks[index])); +// +// } +// +// return Promise.all(data) +// .then(makeOutput) +// .catch(editor.core.log); +// +// }; +// +// /** Save and validate block data */ +// let getBlockData = function (block) { +// +// return saveBlockData(block) +// .then(validateBlockData) +// .catch(editor.core.log); +// +// }; +// +// /** +// * @private +// * Call block`s plugin save method and return saved data +// * +// * @param block +// * @returns {Object} +// */ +// let saveBlockData = function (block) { +// +// let pluginName = block.dataset.tool; +// +// /** Check for plugin existence */ +// if (!editor.tools[pluginName]) { +// +// editor.core.log(`Plugin «${pluginName}» not found`, 'error'); +// return {data: null, pluginName: null}; +// +// } +// +// /** Check for plugin having save method */ +// if (typeof editor.tools[pluginName].save !== 'function') { +// +// editor.core.log(`Plugin «${pluginName}» must have save method`, 'error'); +// return {data: null, pluginName: null}; +// +// } +// +// /** Result saver */ +// let blockContent = block.childNodes[0], +// pluginsContent = blockContent.childNodes[0], +// position = pluginsContent.dataset.inputPosition; +// +// /** If plugin wasn't available then return data from cache */ +// if ( editor.tools[pluginName].available === false ) { +// +// return Promise.resolve({data: codex.editor.state.blocks.items[position].data, pluginName}); +// +// } +// +// return Promise.resolve(pluginsContent) +// .then(editor.tools[pluginName].save) +// .then(data => Object({data, pluginName})); +// +// }; +// +// /** +// * Call plugin`s validate method. Return false if validation failed +// * +// * @param data +// * @param pluginName +// * @returns {Object|Boolean} +// */ +// let validateBlockData = function ({data, pluginName}) { +// +// if (!data || !pluginName) { +// +// return false; +// +// } +// +// if (editor.tools[pluginName].validate) { +// +// let result = editor.tools[pluginName].validate(data); +// +// /** +// * Do not allow invalid data +// */ +// if (!result) { +// +// return false; +// +// } +// +// } +// +// return {data, pluginName}; +// +// +// }; +// +// /** +// * Compile article output +// * +// * @param savedData +// * @returns {{time: number, version, items: (*|Array)}} +// */ +// let makeOutput = function (savedData) { +// +// savedData = savedData.filter(blockData => blockData); +// +// let items = savedData.map(blockData => Object({type: blockData.pluginName, data: blockData.data})); +// +// editor.state.jsonOutput = items; +// +// return { +// id: editor.state.blocks.id || null, +// time: +new Date(), +// version: editor.version, +// items +// }; +// +// }; +// +// return saver; +// +// })({}); diff --git a/src/components/modules/shortcuts.ts b/src/components/modules/shortcuts.ts new file mode 100644 index 00000000..5be9520a --- /dev/null +++ b/src/components/modules/shortcuts.ts @@ -0,0 +1,54 @@ + +import Shortcut from '@codexteam/shortcuts'; +import {IShortcut, IShortcuts} from '../interfaces/shortcuts'; +import IEditorConfig from '../interfaces/editor-config'; + +/** + * Contains keyboard and mouse events binded on each Block by Block Manager + */ +declare var Module: any; + +/** + * @class Shortcut + * @classdesc Allows to register new shortcut + * + * Internal Shortcuts Module + */ +export default class Shortcuts extends Module implements IShortcuts { + /** + * All registered shortcuts + * @type {IShortcut[]} + */ + private registeredShortcuts: IShortcut[]; + + /** + * @constructor + * @param {IEditorConfig} config + */ + constructor({config}) { + super({config}); + this.registeredShortcuts = []; + } + + /** + * Register shortcut + * @param {IShortcut} shortcut + */ + public add(shortcut: IShortcut): void { + const newShortcut = new Shortcut({ + name: shortcut.name, + on: document, + callback: shortcut.handler, + }); + + this.registeredShortcuts.push(newShortcut); + } + + /** + * Remove shortcut + * @param {IShortcut} shortcut + */ + public remove(shortcut: string): void { + // Remove + } +} diff --git a/src/components/modules/toolbar-blockSettings.js b/src/components/modules/toolbar-blockSettings.js new file mode 100644 index 00000000..e57163d8 --- /dev/null +++ b/src/components/modules/toolbar-blockSettings.js @@ -0,0 +1,126 @@ +/** + * Block Settings + * + * ____ Settings Panel ____ + * | ...................... | + * | . Tool Settings . | + * | ...................... | + * | . Default Settings . | + * | ...................... | + * |________________________| + */ +export default class BlockSettings extends Module { + /** + * @constructor + */ + constructor({config}) { + super({config}); + + this.nodes = { + wrapper: null, + toolSettings: null, + defaultSettings: null + }; + } + + /** + * Module Events + * @return {{opened: string, closed: string}} + */ + get events() { + return { + opened: 'block-settings-opened', + closed: 'block-settings-closed', + }; + } + + /** + * Block Settings CSS + * @return {{wrapper, wrapperOpened, toolSettings, defaultSettings, button}} + */ + static get CSS() { + return { + // Settings Panel + wrapper: 'ce-settings', + wrapperOpened: 'ce-settings--opened', + toolSettings: 'ce-settings__plugin-zone', + defaultSettings: 'ce-settings__default-zone', + + button: 'ce-settings__button' + }; + } + + /** + * Panel with block settings with 2 sections: + * - Tool's Settings + * - Default Settings [Move, Remove, etc] + * + * @return {Element} + */ + make() { + this.nodes.wrapper = $.make('div', BlockSettings.CSS.wrapper); + + this.nodes.toolSettings = $.make('div', BlockSettings.CSS.toolSettings); + this.nodes.defaultSettings = $.make('div', BlockSettings.CSS.defaultSettings); + + $.append(this.nodes.wrapper, [this.nodes.toolSettings, this.nodes.defaultSettings]); + } + + /** + * Add Tool's settings + */ + addToolSettings() { + if (typeof this.Editor.BlockManager.currentBlock.tool.renderSettings === 'function') { + $.append(this.nodes.toolSettings, this.Editor.BlockManager.currentBlock.tool.renderSettings()); + } + } + + /** + * Add default settings + */ + addDefaultSettings() { + $.append(this.nodes.defaultSettings, this.Editor.BlockManager.currentBlock.renderTunes()); + } + + /** + * Is Block Settings opened or not + * @returns {boolean} + */ + get opened() { + return this.nodes.wrapper.classList.contains(BlockSettings.CSS.wrapperOpened); + } + + /** + * Open Block Settings pane + */ + open() { + this.nodes.wrapper.classList.add(BlockSettings.CSS.wrapperOpened); + + /** + * Fill Tool's settings + */ + this.addToolSettings(); + + /** + * Add default settings that presents for all Blocks + */ + this.addDefaultSettings(); + + /** Tell to subscribers that block settings is opened */ + this.Editor.Events.emit(this.events.opened); + } + + /** + * Close Block Settings pane + */ + close() { + this.nodes.wrapper.classList.remove(BlockSettings.CSS.wrapperOpened); + + /** Clear settings */ + this.nodes.toolSettings.innerHTML = ''; + this.nodes.defaultSettings.innerHTML = ''; + + /** Tell to subscribers that block settings is closed */ + this.Editor.Events.emit(this.events.closed); + } +} diff --git a/src/components/modules/toolbar-inline.ts b/src/components/modules/toolbar-inline.ts new file mode 100644 index 00000000..218da058 --- /dev/null +++ b/src/components/modules/toolbar-inline.ts @@ -0,0 +1,403 @@ +declare var Module: any; +declare var $: any; + +import BoldInlineTool from '../inline-tools/inline-tool-bold'; +import ItalicInlineTool from '../inline-tools/inline-tool-italic'; +import LinkInlineTool from '../inline-tools/inline-tool-link'; +import EditorConfig from '../interfaces/editor-config'; +import InlineTool from '../interfaces/tools/inline-tool'; +import SelectionUtils from '../selection'; +import _ from '../utils'; + +/** + * Inline toolbar with actions that modifies selected text fragment + * + * |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯| + * | B i [link] [mark] | + * |________________________| + */ +export default class InlineToolbar extends Module { + + /** + * CSS styles + */ + public CSS = { + inlineToolbar: 'ce-inline-toolbar', + inlineToolbarShowed: 'ce-inline-toolbar--showed', + buttonsWrapper: 'ce-inline-toolbar__buttons', + actionsWrapper: 'ce-inline-toolbar__actions', + inlineToolButton: 'ce-inline-tool', + inlineToolButtonLast: 'ce-inline-tool--last', + }; + + /** + * Inline Toolbar elements + */ + private nodes = { + wrapper: null, + buttons: null, + /** + * Zone below the buttons where Tools can create additional actions by 'renderActions()' method + * For example, input for the 'link' tool or textarea for the 'comment' tool + */ + actions: null, + }; + + /** + * Margin above/below the Toolbar + */ + private readonly toolbarVerticalMargin: number = 20; + + /** + * Tools instances + */ + private toolsInstances: Map; + + /** + * @constructor + * @param {EditorConfig} config + */ + constructor({config}) { + super({config}); + } + + /** + * Inline Toolbar Tools + * includes internal and external tools + * + * @returns Map + */ + get tools(): Map { + if (!this.toolsInstances || this.toolsInstances.size === 0) { + const allTools = {...this.internalTools, ...this.externalTools}; + + this.toolsInstances = new Map(); + for (const tool in allTools) { + if (allTools.hasOwnProperty(tool)) { + this.toolsInstances.set(tool, allTools[tool]); + } + } + } + + return this.toolsInstances; + } + + /** + * Making DOM + */ + public make() { + this.nodes.wrapper = $.make('div', this.CSS.inlineToolbar); + this.nodes.buttons = $.make('div', this.CSS.buttonsWrapper); + this.nodes.actions = $.make('div', this.CSS.actionsWrapper); + + /** + * Append Inline Toolbar to the Editor + */ + $.append(this.nodes.wrapper, [this.nodes.buttons, this.nodes.actions]); + $.append(this.Editor.UI.nodes.wrapper, this.nodes.wrapper); + + /** + * Append Inline Toolbar Tools + */ + this.addTools(); + + } + + /** + * Moving / appearance + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Shows Inline Toolbar by keyup/mouseup + * @param {KeyboardEvent|MouseEvent} event + */ + public handleShowingEvent(event): void { + if (!this.allowedToShow(event)) { + this.close(); + return; + } + + this.move(); + this.open(); + + /** Check Tools state for selected fragment */ + this.checkToolsState(); + } + + /** + * Move Toolbar to the selected text + */ + public move(): void { + const selectionRect = SelectionUtils.rect as DOMRect; + const wrapperOffset = this.Editor.UI.nodes.wrapper.getBoundingClientRect(); + const newCoords = { + x: selectionRect.x - wrapperOffset.left, + y: selectionRect.y + + selectionRect.height + // + window.scrollY + - wrapperOffset.top + + this.toolbarVerticalMargin, + }; + + /** + * If we know selections width, place InlineToolbar to center + */ + if (selectionRect.width) { + newCoords.x += Math.floor(selectionRect.width / 2); + } + + this.nodes.wrapper.style.left = Math.floor(newCoords.x) + 'px'; + this.nodes.wrapper.style.top = Math.floor(newCoords.y) + 'px'; + } + + /** + * Shows Inline Toolbar + */ + private open() { + /** + * Filter inline-tools and show only allowed by Block's Tool + */ + this.filterTools(); + + /** + * Show Inline Toolbar + */ + this.nodes.wrapper.classList.add(this.CSS.inlineToolbarShowed); + + /** + * Call 'clear' method for Inline Tools (for example, 'link' want to clear input) + */ + this.tools.forEach( (toolInstance, toolName) => { + if (typeof toolInstance.clear === 'function') { + toolInstance.clear(); + } + }); + } + + /** + * Hides Inline Toolbar + */ + private close() { + this.nodes.wrapper.classList.remove(this.CSS.inlineToolbarShowed); + this.tools.forEach( (toolInstance, toolName) => { + if (typeof toolInstance.clear === 'function') { + toolInstance.clear(); + } + }); + } + + /** + * Need to show Inline Toolbar or not + * @param {KeyboardEvent|MouseEvent} event + */ + private allowedToShow(event): boolean { + /** + * Tags conflicts with window.selection function. + * Ex. IMG tag returns null (Firefox) or Redactors wrapper (Chrome) + */ + const tagsConflictsWithSelection = ['IMG', 'INPUT']; + + if (event && tagsConflictsWithSelection.includes(event.target.tagName)) { + return false; + } + + const currentSelection = SelectionUtils.get(), + selectedText = SelectionUtils.text; + + // old browsers + if (!currentSelection || !currentSelection.anchorNode) { + return false; + } + + // empty selection + if (currentSelection.isCollapsed || selectedText.length < 1) { + return false; + } + + // is enabled by current Block's Tool + const currentBlock = this.Editor.BlockManager.getBlock(currentSelection.anchorNode); + + if (!currentBlock) { + return false; + } + + const toolSettings = this.Editor.Tools.getToolSettings(currentBlock.name); + + return toolSettings && toolSettings[this.Editor.Tools.apiSettings.IS_ENABLED_INLINE_TOOLBAR]; + } + + /** + * Show only allowed Tools + */ + private filterTools(): void { + const currentSelection = SelectionUtils.get(), + currentBlock = this.Editor.BlockManager.getBlock(currentSelection.anchorNode); + + const toolSettings = this.Editor.Tools.getToolSettings(currentBlock.name), + inlineToolbarSettings = toolSettings && toolSettings[this.Editor.Tools.apiSettings.IS_ENABLED_INLINE_TOOLBAR]; + + /** + * All Inline Toolbar buttons + * @type {HTMLElement[]} + */ + const buttons = Array.from(this.nodes.buttons.querySelectorAll(`.${this.CSS.inlineToolButton}`)) as HTMLElement[]; + + /** + * Show previously hided + */ + buttons.forEach((button) => { + button.hidden = false; + button.classList.remove(this.CSS.inlineToolButtonLast); + }); + + /** + * Filter buttons if Block Tool pass config like inlineToolbar=['link'] + */ + if (Array.isArray(inlineToolbarSettings)) { + buttons.forEach((button) => { + button.hidden = !inlineToolbarSettings.includes(button.dataset.tool); + }); + } + + /** + * Tick for removing right-margin from last visible button. + * Current generation of CSS does not allow to filter :visible elements + */ + const lastVisibleButton = buttons.filter((button) => !button.hidden).pop(); + + if (lastVisibleButton) { + lastVisibleButton.classList.add(this.CSS.inlineToolButtonLast); + } + } + + /** + * Working with Tools + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + /** + * Fill Inline Toolbar with Tools + */ + private addTools(): void { + this.tools.forEach( (toolInstance, toolName) => { + this.addTool(toolName, toolInstance); + }); + } + + /** + * Add tool button and activate clicks + */ + private addTool(toolName: string, tool: InlineTool): void { + const button = tool.render(); + + if (!button) { + _.log('Render method must return an instance of Node', 'warn', toolName); + return; + } + + button.dataset.tool = toolName; + this.nodes.buttons.appendChild(button); + + if (typeof tool.renderActions === 'function') { + const actions = tool.renderActions(); + this.nodes.actions.appendChild(actions); + } + + this.Editor.Listeners.on(button, 'click', () => { + this.toolClicked(tool); + }); + + /** + * Enable shortcuts + * Ignore tool that doesn't have shortcut or empty string + */ + const toolSettings = this.Editor.Tools.getToolSettings(toolName); + + let shortcut = null; + + /** + * 1) For internal tools, check public getter 'shortcut' + * 2) For external tools, check tool's settings + */ + if (this.internalTools[toolName]) { + shortcut = this.internalTools[toolName].shortcut; + } else if (toolSettings && toolSettings[this.Editor.Tools.apiSettings.SHORTCUT]) { + shortcut = toolSettings[this.Editor.Tools.apiSettings.SHORTCUT]; + } + + if (shortcut) { + this.enableShortcuts(tool, shortcut); + } + } + + /** + * Enable Tool shortcut with Editor Shortcuts Module + * @param {InlineTool} tool - Tool instance + * @param {string} shortcut - shortcut according to the Shortcut Module format + */ + private enableShortcuts(tool: InlineTool, shortcut: string): void { + this.Editor.Shortcuts.add({ + name: shortcut, + handler: (event) => { + const {currentBlock} = this.Editor.BlockManager, + toolSettings = this.Editor.Tools.getToolSettings(currentBlock.name); + + if (!toolSettings || !toolSettings[this.Editor.Tools.apiSettings.IS_ENABLED_INLINE_TOOLBAR]) { + return; + } + + event.preventDefault(); + this.toolClicked(tool); + }, + }); + } + + /** + * Inline Tool button clicks + * @param {InlineTool} tool - Tool's instance + */ + private toolClicked(tool: InlineTool): void { + const range = SelectionUtils.range; + + tool.surround(range); + this.checkToolsState(); + } + + /** + * Check Tools` state by selection + */ + private checkToolsState(): void { + this.tools.forEach( (toolInstance, toolName) => { + toolInstance.checkState(SelectionUtils.get()); + }); + } + + /** + * Returns internal inline tools + * Includes Bold, Italic, Link + */ + private get internalTools(): {[name: string]: InlineTool} { + return { + bold: this.Editor.Tools.constructInline(BoldInlineTool), + italic: this.Editor.Tools.constructInline(ItalicInlineTool), + link: this.Editor.Tools.constructInline(LinkInlineTool), + }; + } + + /** + * Get external tools + * Tools that has isInline is true + */ + private get externalTools(): {[name: string]: InlineTool} { + const result = {}; + + for (const tool in this.Editor.Tools.inline) { + if (this.Editor.Tools.inline.hasOwnProperty(tool)) { + result[tool] = this.Editor.Tools.constructInline(this.Editor.Tools.inline[tool]); + } + } + + return result; + } +} diff --git a/src/components/modules/toolbar-toolbox.js b/src/components/modules/toolbar-toolbox.js new file mode 100644 index 00000000..063996fa --- /dev/null +++ b/src/components/modules/toolbar-toolbox.js @@ -0,0 +1,303 @@ +import IEditorConfig from '../interfaces/editor-config'; + +/** + * @class Toolbox + * @classdesc Holder for Tools + * + * @typedef {Toolbox} Toolbox + * @property {Boolean} opened - opening state + * @property {Object} nodes - Toolbox nodes + * @property {Object} CSS - CSS class names + * + */ +export default class Toolbox extends Module { + /** + * @constructor + * @param {IEditorConfig} config + */ + constructor({config}) { + super({config}); + + this.nodes = { + toolbox: null, + buttons: [] + }; + + /** + * Opening state + * @type {boolean} + */ + this.opened = false; + + /** + * Active button index + * -1 equals no chosen Tool + * @type {number} + */ + this.activeButtonIndex = -1; + } + + /** + * CSS styles + * @return {{toolbox: string, toolboxButton: string, toolboxOpened: string}} + */ + static get CSS() { + return { + toolbox: 'ce-toolbox', + toolboxButton: 'ce-toolbox__button', + toolboxButtonActive : 'ce-toolbox__button--active', + toolboxOpened: 'ce-toolbox--opened', + }; + } + + /** + * Makes the Toolbox + */ + make() { + this.nodes.toolbox = $.make('div', Toolbox.CSS.toolbox); + $.append(this.Editor.Toolbar.nodes.content, this.nodes.toolbox); + + this.addTools(); + } + + /** + * Iterates available tools and appends them to the Toolbox + */ + addTools() { + let tools = this.Editor.Tools.toolsAvailable; + + for (let toolName in tools) { + this.addTool(toolName, tools[toolName]); + } + } + + /** + * Append Tool to the Toolbox + * + * @param {string} toolName - tool name + * @param {IBlockTool} tool - tool class + */ + addTool(toolName, tool) { + const api = this.Editor.Tools.apiSettings; + + if (tool[api.IS_DISPLAYED_IN_TOOLBOX] && !tool[api.TOOLBAR_ICON]) { + _.log('Toolbar icon is missed. Tool %o skipped', 'warn', toolName); + return; + } + + /** + * @todo Add checkup for the render method + */ + // if (typeof tool.render !== 'function') { + // _.log('render method missed. Tool %o skipped', 'warn', tool); + // return; + // } + + /** + * Skip tools that pass 'displayInToolbox=false' + */ + if (!tool[api.IS_DISPLAYED_IN_TOOLBOX]) { + return; + } + + let button = $.make('li', [ Toolbox.CSS.toolboxButton ], { + title: toolName + }); + + button.innerHTML = tool.toolboxIcon; + + $.append(this.nodes.toolbox, button); + + this.nodes.toolbox.appendChild(button); + this.nodes.buttons.push(button); + + /** + * Add click listener + */ + this.Editor.Listeners.on(button, 'click', (event) => { + this.toolButtonActivate(event, toolName); + }); + + /** + * Enable shortcut + */ + const toolSettings = this.Editor.Tools.getToolSettings(toolName); + + if (toolSettings && toolSettings[this.Editor.Tools.apiSettings.SHORTCUT]) { + this.enableShortcut(tool, toolName, toolSettings[this.Editor.Tools.apiSettings.SHORTCUT]); + } + } + + /** + * Enable shortcut Block Tool implemented shortcut + * @param {IBlockTool} tool - Tool class + * @param {String} toolName - Tool name + * @param {String} shortcut - shortcut according to the Shortcut Module format + */ + enableShortcut(tool, toolName, shortcut) { + this.Editor.Shortcuts.add({ + name: shortcut, + handler: (event) => { + event.preventDefault(); + this.insertNewBlock(tool, toolName); + } + }); + } + + /** + * Inserts new block + * Can be called when button clicked on Toolbox or by Shortcut + * + * @param {IBlockTool} tool - Tool Class + * @param {String} toolName - Tool name + */ + insertNewBlock(tool, toolName) { + /** + * @type {Block} + */ + const currentBlock = this.Editor.BlockManager.currentBlock; + + /** + * We do replace if: + * - block is empty + * - block is not irreplaceable + * @type {Array} + */ + let newBlock; + + if (!tool[this.Editor.Tools.apiSettings.IS_IRREPLACEBLE_TOOL] && currentBlock.isEmpty) { + newBlock = this.Editor.BlockManager.replace(toolName); + } else { + newBlock = this.Editor.BlockManager.insert(toolName); + } + + this.Editor.Caret.setToBlock(newBlock); + + /** + * close toolbar when node is changed + */ + this.Editor.Toolbar.close(); + } + + /** + * Toolbox Tool's button click handler + * + * @param {MouseEvent|KeyboardEvent} event + * @param {string} toolName + */ + toolButtonActivate(event, toolName) { + const tool = this.Editor.Tools.toolsClasses[toolName]; + + this.insertNewBlock(tool, toolName); + } + + /** + * Open Toolbox with Tools + */ + open() { + this.nodes.toolbox.classList.add(Toolbox.CSS.toolboxOpened); + this.opened = true; + } + + /** + * Close Toolbox + */ + close() { + this.nodes.toolbox.classList.remove(Toolbox.CSS.toolboxOpened); + this.opened = false; + + /** remove active item pointer */ + this.activeButtonIndex = -1; + const activeButton = this.nodes.toolbox.querySelector(`.${Toolbox.CSS.toolboxButtonActive}`); + + if (activeButton) { + activeButton.classList.remove(Toolbox.CSS.toolboxButtonActive); + } + } + + /** + * Close Toolbox + */ + toggle() { + if (!this.opened) { + this.open(); + } else { + this.close(); + } + } + + /** + * Leaf + * flip through the toolbox items + * @param {String} direction - leaf direction, right is default + */ + leaf(direction = 'right') { + const childNodes = this.nodes.toolbox.childNodes; + + /** + * If activeButtonIndex === -1 then we have no chosen Tool in Toolbox + */ + if (this.activeButtonIndex === -1) { + /** + * Normalize "previous" Tool index depending on direction. + * We need to do this to highlight "first" Tool correctly + * + * Order of Tools: [0] [1] ... [n - 1] + * [0 = n] because of: n % n = 0 % n + * + * Direction 'right': for [0] the [n - 1] is a previous index + * [n - 1] -> [0] + * + * Direction 'left': for [n - 1] the [0] is a previous index + * [n - 1] <- [0] + * + * @type {number} + */ + this.activeButtonIndex = direction === 'right' ? -1 : 0; + } else { + /** + * If we have chosen Tool then remove highlighting + */ + childNodes[this.activeButtonIndex].classList.remove(Toolbox.CSS.toolboxButtonActive); + } + + /** + * Count index for next Tool + */ + if (direction === 'right') { + /** + * If we go right then choose next (+1) Tool + * @type {number} + */ + this.activeButtonIndex = (this.activeButtonIndex + 1) % childNodes.length; + } else { + /** + * If we go left then choose previous (-1) Tool + * Before counting module we need to add length before because of "The JavaScript Modulo Bug" + * @type {number} + */ + this.activeButtonIndex = (childNodes.length + this.activeButtonIndex - 1) % childNodes.length; + } + + /** + * Highlight new chosen Tool + */ + childNodes[this.activeButtonIndex].classList.add(Toolbox.CSS.toolboxButtonActive); + } + + /** + * get tool name when it is selected + * In case when nothing selection returns null + * + * @return {String|null} + */ + get getActiveTool() { + const childNodes = this.nodes.toolbox.childNodes; + + if (this.activeButtonIndex === -1) { + return null; + } + + return childNodes[this.activeButtonIndex].title; + } +} diff --git a/src/components/modules/toolbar.js b/src/components/modules/toolbar.js new file mode 100644 index 00000000..cc0908ef --- /dev/null +++ b/src/components/modules/toolbar.js @@ -0,0 +1,259 @@ +/** + * + * «Toolbar» is the node that moves up/down over current block + * + * ______________________________________ Toolbar ____________________________________________ + * | | + * | ..................... Content .................... ......... Block Actions .......... | + * | . . . . | + * | . . . [Open Settings] . | + * | . [Plus Button] [Toolbox: {Tool1}, {Tool2}] . . . | + * | . . . [Settings Panel] . | + * | .................................................. .................................. | + * | | + * |___________________________________________________________________________________________| + * + * + * Toolbox — its an Element contains tools buttons. Can be shown by Plus Button. + * + * _______________ Toolbox _______________ + * | | + * | [Header] [Image] [List] [Quote] ... | + * |_______________________________________| + * + * + * Settings Panel — is an Element with block settings: + * + * ____ Settings Panel ____ + * | ...................... | + * | . Tool Settings . | + * | ...................... | + * | . Default Settings . | + * | ...................... | + * |________________________| + * + * + * @class + * @classdesc Toolbar module + * + * @typedef {Toolbar} Toolbar + * @property {Object} nodes + * @property {Element} nodes.wrapper - Toolbar main element + * @property {Element} nodes.content - Zone with Plus button and toolbox. + * @property {Element} nodes.actions - Zone with Block Settings and Remove Button + * @property {Element} nodes.blockActionsButtons - Zone with Block Buttons: [Settings] + * @property {Element} nodes.plusButton - Button that opens or closes Toolbox + * @property {Element} nodes.toolbox - Container for tools + * @property {Element} nodes.settingsToggler - open/close Settings Panel button + * @property {Element} nodes.settings - Settings Panel + * @property {Element} nodes.pluginSettings - Plugin Settings section of Settings Panel + * @property {Element} nodes.defaultSettings - Default Settings section of Settings Panel + */ +export default class Toolbar extends Module { + /** + * @constructor + */ + constructor({config}) { + super({config}); + + this.nodes = { + wrapper : null, + content : null, + actions : null, + + // Content Zone + plusButton : null, + + // Actions Zone + blockActionsButtons: null, + settingsToggler : null, + }; + } + + /** + * CSS styles + * @return {Object} + * @constructor + */ + static get CSS() { + return { + toolbar: 'ce-toolbar', + content: 'ce-toolbar__content', + actions: 'ce-toolbar__actions', + + toolbarOpened: 'ce-toolbar--opened', + + // Content Zone + plusButton: 'ce-toolbar__plus', + plusButtonHidden: 'ce-toolbar__plus--hidden', + + // Actions Zone + blockActionsButtons: 'ce-toolbar__actions-buttons', + settingsToggler: 'ce-toolbar__settings-btn', + }; + } + + /** + * Makes toolbar + */ + make() { + this.nodes.wrapper = $.make('div', Toolbar.CSS.toolbar); + + /** + * Make Content Zone and Actions Zone + */ + ['content', 'actions'].forEach( el => { + this.nodes[el] = $.make('div', Toolbar.CSS[el]); + $.append(this.nodes.wrapper, this.nodes[el]); + }); + + + /** + * Fill Content Zone: + * - Plus Button + * - Toolbox + */ + this.nodes.plusButton = $.make('div', Toolbar.CSS.plusButton); + $.append(this.nodes.plusButton, $.svg('plus', 14, 14)); + $.append(this.nodes.content, this.nodes.plusButton); + this.nodes.plusButton.addEventListener('click', event => this.plusButtonClicked(event), false); + + + /** + * Make a Toolbox + */ + this.Editor.Toolbox.make(); + + /** + * Fill Actions Zone: + * - Settings Toggler + * - Remove Block Button + * - Settings Panel + */ + this.nodes.blockActionsButtons = $.make('div', Toolbar.CSS.blockActionsButtons); + this.nodes.settingsToggler = $.make('span', Toolbar.CSS.settingsToggler); + const settingsIcon = $.svg('dots', 18, 4); + + $.append(this.nodes.settingsToggler, settingsIcon); + $.append(this.nodes.blockActionsButtons, this.nodes.settingsToggler); + $.append(this.nodes.actions, this.nodes.blockActionsButtons); + + /** + * Make and append Settings Panel + */ + this.Editor.BlockSettings.make(); + $.append(this.nodes.actions, this.Editor.BlockSettings.nodes.wrapper); + + /** + * Append toolbar to the Editor + */ + $.append(this.Editor.UI.nodes.wrapper, this.nodes.wrapper); + + /** + * Bind events on the Toolbar elements + */ + this.bindEvents(); + } + + /** + * Move Toolbar to the Current Block + * @param {Boolean} forceClose - force close Toolbar Settings and Toolbar + */ + move(forceClose = true) { + if (forceClose) { + /** Close Toolbox when we move toolbar */ + this.Editor.Toolbox.close(); + this.Editor.BlockSettings.close(); + } + + let currentNode = this.Editor.BlockManager.currentNode; + + /** + * If no one Block selected as a Current + */ + if (!currentNode) { + return; + } + + /** + * @todo Compute dynamically on prepare + * @type {number} + */ + const defaultToolbarHeight = 49; + const defaultOffset = 34; + + var newYCoordinate = currentNode.offsetTop - (defaultToolbarHeight / 2) + defaultOffset; + + this.nodes.wrapper.style.transform = `translate3D(0, ${Math.floor(newYCoordinate)}px, 0)`; + } + + /** + * Open Toolbar with Plus Button + */ + open() { + this.move(); + this.nodes.wrapper.classList.add(Toolbar.CSS.toolbarOpened); + } + + /** + * returns toolbar opened state + * @return {Boolean} + */ + get opened() { + return this.nodes.wrapper.classList.contains(Toolbar.CSS.toolbarOpened); + } + + /** + * Close the Toolbar + */ + close() { + this.nodes.wrapper.classList.remove(Toolbar.CSS.toolbarOpened); + + /** Close components */ + this.Editor.Toolbox.close(); + this.Editor.BlockSettings.close(); + } + + /** + * Plus Button public methods + * @return {{hide: function(): void, show: function(): void}} + */ + get plusButton() { + return { + hide: () => this.nodes.plusButton.classList.add(Toolbar.CSS.plusButtonHidden), + show: () => this.nodes.plusButton.classList.remove(Toolbar.CSS.plusButtonHidden) + }; + } + + /** + * Handler for Plus Button + * @param {MouseEvent} event + */ + plusButtonClicked() { + this.Editor.Toolbox.toggle(); + } + + /** + * Bind events on the Toolbar Elements: + * - Block Settings + */ + bindEvents() { + /** + * Settings toggler + */ + this.Editor.Listeners.on(this.nodes.settingsToggler, 'click', (event) => { + this.settingsTogglerClicked(event); + }); + } + + /** + * Clicks on the Block Settings toggler + */ + settingsTogglerClicked() { + if (this.Editor.BlockSettings.opened) { + this.Editor.BlockSettings.close(); + } else { + this.Editor.BlockSettings.open(); + } + } +} diff --git a/src/components/modules/tools.js b/src/components/modules/tools.js new file mode 100644 index 00000000..812138fc --- /dev/null +++ b/src/components/modules/tools.js @@ -0,0 +1,371 @@ +const Paragraph = require('../tools/paragraph/dist/bundle'); + +/** + * @module Codex Editor Tools Submodule + * + * Creates Instances from Plugins and binds external config to the instances + */ + +/** + * Each Tool must contain the following important objects: + * + * @typedef {Object} ToolConfig {@link docs/tools.md} + * @property {String} iconClassname - this a icon in toolbar + * @property {Boolean} displayInToolbox - will be displayed in toolbox. Default value is TRUE + * @property {Boolean} enableLineBreaks - inserts new block or break lines. Default value is FALSE + * @property {Boolean|String[]} inlineToolbar - Pass `true` to enable the Inline Toolbar with all Tools, all pass an array with specified Tools list | + * @property render @todo add description + * @property save @todo add description + * @property settings @todo add description + * @property validate - method that validates output data before saving + */ + +/** + * @typedef {Function} Tool {@link docs/tools.md} + * @property {Boolean} displayInToolbox - By default, tools won't be added in the Toolbox. Pass true to add. + * @property {String} iconClassName - CSS class name for the Toolbox button + * @property {Boolean} irreplaceable - Toolbox behaviour: replace or add new block below + * @property render + * @property save + * @property settings + * @property validate + * + * @todo update according to current API + * @todo describe Tool in the {@link docs/tools.md} + */ + +/** + * Class properties: + * + * @typedef {Tools} Tools + * @property {Tools[]} toolsAvailable - available Tools + * @property {Tools[]} toolsUnavailable - unavailable Tools + * @property {object} toolsClasses - all classes + * @property {object} toolsSettings - Tools settings + * @property {EditorConfig} config - Editor config + */ +export default class Tools extends Module { + /** + * @constructor + * + * @param {EditorConfig} config + */ + constructor({config}) { + super({config}); + + /** + * Map {name: Class, ...} where: + * name — block type name in JSON. Got from EditorConfig.tools keys + * @type {Object} + */ + this.toolsClasses = {}; + + /** + * Tools settings in a map {name: settings, ...} + * @type {Object} + */ + this.toolsSettings = {}; + + /** + * Available tools list + * {name: Class, ...} + * @type {Object} + */ + this.toolsAvailable = {}; + + /** + * Tools that rejected a prepare method + * {name: Class, ... } + * @type {Object} + */ + this.toolsUnavailable = {}; + + /** + * Cache for the prepared inline tools + * @type {null|object} + * @private + */ + this._inlineTools = null; + } + + /** + * Returns available Tools + * @return {Tool[]} + */ + get available() { + return this.toolsAvailable; + } + + /** + * Returns unavailable Tools + * @return {Tool[]} + */ + get unavailable() { + return this.toolsUnavailable; + } + + /** + * Return Tools for the Inline Toolbar + * @return {Object} - object of Inline Tool's classes + */ + get inline() { + if (this._inlineTools) { + return this._inlineTools; + } + + const tools = Object.entries(this.available).filter( ([name, tool]) => { + if (!tool[this.apiSettings.IS_INLINE]) { + return false; + } + + /** + * Some Tools validation + */ + const inlineToolRequiredMethods = ['render', 'surround', 'checkState']; + const notImplementedMethods = inlineToolRequiredMethods.filter( method => !this.constructInline(tool)[method]); + + if (notImplementedMethods.length) { + _.log(`Incorrect Inline Tool: ${tool.name}. Some of required methods is not implemented %o`, 'warn', notImplementedMethods); + return false; + } + + return true; + }); + + /** + * collected inline tools with key of tool name + */ + const result = {}; + + tools.forEach(([name, tool]) => result[name] = tool); + + /** + * Cache prepared Tools + */ + this._inlineTools = result; + + return this._inlineTools; + } + + /** + * Return editor block tools + */ + get blockTools() { + // eslint-disable-next-line no-unused-vars + const tools = Object.entries(this.available).filter( ([name, tool]) => { + if (tool[this.apiSettings.IS_INLINE]) { + return false; + } + + return true; + }); + + /** + * collected block tools with key of tool name + */ + const result = {}; + + tools.forEach(([name, tool]) => result[name] = tool); + + return result; + } + + /** + * Constant for available Tools Settings + * @return {object} + */ + get apiSettings() { + return { + CONFIG: 'config', + IS_CONTENTLESS: 'contentless', + IS_DISPLAYED_IN_TOOLBOX: 'displayInToolbox', + IS_ENABLED_INLINE_TOOLBAR: 'inlineToolbar', + IS_ENABLED_LINE_BREAKS: 'enableLineBreaks', + IS_INLINE: 'isInline', + IS_IRREPLACEBLE_TOOL: 'irreplaceable', + IS_PASTE_DISALLOWED: 'disallowPaste', + SHORTCUT: 'shortcut', + TOOLBAR_ICON: 'toolboxIcon', + }; + } + + /** + * Creates instances via passed or default configuration + * @return {Promise} + */ + prepare() { + this.config.tools.paragraph = { + class: Paragraph, + inlineToolbar: true + }; + + if (!this.config.hasOwnProperty('tools') || Object.keys(this.config.tools).length === 0) { + return Promise.reject('Can\'t start without tools'); + } + + /** + * Save Tools settings to a map + */ + for(let toolName in this.config.tools) { + /** + * If Tool is an object not a Tool's class then + * save class and settings separately + */ + if (typeof this.config.tools[toolName] === 'object') { + /** + * Save Tool's class from 'class' field + * @type {ITool} + */ + this.toolsClasses[toolName] = this.config.tools[toolName].class; + + /** + * Save Tool's settings + * @type {IToolSettings} + */ + this.toolsSettings[toolName] = this.config.tools[toolName]; + + /** + * Remove Tool's class from settings + */ + delete this.toolsSettings[toolName].class; + } else { + /** + * Save Tool's class + * @type {ITool} + */ + this.toolsClasses[toolName] = this.config.tools[toolName]; + + /** + * Set empty settings for Block by default + * @type {{}} + */ + this.toolsSettings[toolName] = {}; + } + } + + /** + * getting classes that has prepare method + */ + let sequenceData = this.getListOfPrepareFunctions(); + + /** + * if sequence data contains nothing then resolve current chain and run other module prepare + */ + if (sequenceData.length === 0) { + return Promise.resolve(); + } + + /** + * to see how it works {@link Util#sequence} + */ + return _.sequence(sequenceData, (data) => { + this.success(data); + }, (data) => { + this.fallback(data); + }); + } + + /** + * Binds prepare function of plugins with user or default config + * @return {Array} list of functions that needs to be fired sequentially + */ + getListOfPrepareFunctions() { + let toolPreparationList = []; + + for(let toolName in this.toolsClasses) { + let toolClass = this.toolsClasses[toolName]; + + if (typeof toolClass.prepare === 'function') { + toolPreparationList.push({ + function : toolClass.prepare, + data : { + toolName + } + }); + } else { + /** + * If Tool hasn't a prepare method, mark it as available + */ + this.toolsAvailable[toolName] = toolClass; + } + } + + return toolPreparationList; + } + + /** + * @param {ChainData.data} data - append tool to available list + */ + success(data) { + this.toolsAvailable[data.toolName] = this.toolsClasses[data.toolName]; + } + + /** + * @param {ChainData.data} data - append tool to unavailable list + */ + fallback(data) { + this.toolsUnavailable[data.toolName] = this.toolsClasses[data.toolName]; + } + + /** + * Return Tool`s instance + * + * @param {String} tool — tool name + * @param {IBlockToolData} data — initial data + * @return {IBlockTool} + */ + construct(tool, data) { + const plugin = this.toolsClasses[tool]; + + /** + * Configuration to be passed to the Tool's constructor + */ + const config = this.toolsSettings[tool][this.apiSettings.CONFIG]; + + /** + * @type {{api: IAPI, config: ({}), data: IBlockToolData}} + */ + const constructorOptions = { + api: this.Editor.API.methods, + config: config || {}, + data: data + }; + + return new plugin(constructorOptions); + } + + /** + * Return Inline Tool's instance + * + * @param {IInlineTool} tool + * @return {IInlineTool} — instance + */ + constructInline(tool) { + /** + * @type {{api: IAPI}} + */ + const constructorOptions = { + api: this.Editor.API.methods + }; + + return new tool(constructorOptions); + } + + /** + * Check if passed Tool is an instance of Initial Block Tool + * @param {Tool} tool - Tool to check + * @return {Boolean} + */ + isInitial(tool) { + return tool instanceof this.available[this.config.initialBlock]; + } + + /** + * Return Tool's config by name + * @param {string} toolName + * @return {IToolSettings} + */ + getToolSettings(toolName) { + return this.toolsSettings[toolName]; + } +} diff --git a/src/components/modules/ui.js b/src/components/modules/ui.js new file mode 100644 index 00000000..4dcf3101 --- /dev/null +++ b/src/components/modules/ui.js @@ -0,0 +1,341 @@ +/** + * Module UI + * + * @type {UI} + */ + +/** + * Prebuilded sprite of SVG icons + */ +import sprite from '../../../build/sprite.svg'; + +import Selection from '../selection'; + +/** + * @class + * + * @classdesc Makes CodeX Editor UI: + * + * + * + * + * + * + * @typedef {UI} UI + * @property {EditorConfig} config - editor configuration {@link CodexEditor#configuration} + * @property {Object} Editor - available editor modules {@link CodexEditor#moduleInstances} + * @property {Object} nodes - + * @property {Element} nodes.holder - element where we need to append redactor + * @property {Element} nodes.wrapper - + * @property {Element} nodes.redactor - + */ +export default class UI extends Module { + /** + * @constructor + * + * @param {EditorConfig} config + */ + constructor({config}) { + super({config}); + + this.nodes = { + holder: null, + wrapper: null, + redactor: null + }; + } + + /** + * Making main interface + */ + async prepare() { + await this.make(); + + /** + * Append SVG sprite + */ + await this.appendSVGSprite(); + + /** + * Make toolbar + */ + await this.Editor.Toolbar.make(); + + /** + * Make the Inline toolbar + */ + await this.Editor.InlineToolbar.make(); + + /** + * Load and append CSS + */ + await this.loadStyles(); + + /** + * Bind events for the UI elements + */ + await this.bindEvents(); + } + + /** + * CodeX Editor UI CSS class names + * @return {{editorWrapper: string, editorZone: string, block: string}} + */ + get CSS() { + return { + editorWrapper : 'codex-editor', + editorZone : 'codex-editor__redactor', + }; + } + + /** + * Makes CodeX Editor interface + * @return {Promise} + */ + async make() { + /** + * Element where we need to append CodeX Editor + * @type {Element} + */ + this.nodes.holder = document.getElementById(this.config.holderId); + + if (!this.nodes.holder) { + throw Error("Holder wasn't found by ID: #" + this.config.holderId); + } + + /** + * Create and save main UI elements + */ + this.nodes.wrapper = $.make('div', this.CSS.editorWrapper); + this.nodes.redactor = $.make('div', this.CSS.editorZone); + + this.nodes.wrapper.appendChild(this.nodes.redactor); + this.nodes.holder.appendChild(this.nodes.wrapper); + } + + /** + * Appends CSS + */ + loadStyles() { + /** + * Load CSS + */ + let styles = require('../../styles/main.css'); + + /** + * Make tag + */ + let tag = $.make('style', null, { + textContent: styles.toString() + }); + + /** + * Append styles at the top of HEAD tag + */ + $.prepend(document.head, tag); + } + + /** + * Bind events on the CodeX Editor interface + */ + bindEvents() { + this.Editor.Listeners.on(this.nodes.redactor, 'click', event => this.redactorClicked(event), false ); + this.Editor.Listeners.on(document, 'keydown', event => this.documentKeydown(event), true ); + this.Editor.Listeners.on(document, 'click', event => this.documentClicked(event), false ); + } + + /** + * All keydowns on document + * @param {Event} event + */ + documentKeydown(event) { + switch (event.keyCode) { + case _.keyCodes.ENTER: + this.enterPressed(event); + break; + default: + this.defaultBehaviour(event); + break; + } + } + + /** + * Ignore all other document's keydown events + * @param {KeyboardEvent} event + */ + defaultBehaviour(event) { + const keyDownOnEditor = event.target.closest(`.${this.CSS.editorWrapper}`); + + /** + * Ignore keydowns on document + * clear pointer and close toolbar + */ + if (!keyDownOnEditor) { + /** + * Remove all highlights and remove caret + */ + this.Editor.BlockManager.dropPointer(); + + /** + * Close Toolbar + */ + this.Editor.Toolbar.close(); + } + } + + /** + * Enter pressed on document + * @param event + */ + enterPressed(event) { + let hasPointerToBlock = this.Editor.BlockManager.currentBlockIndex >= 0; + + /** + * If Caret is not set anywhere, event target on Enter is always Element that we handle + * In our case it is document.body + * + * So, BlockManager points some Block and Enter press is on Body + * We can create a new block + */ + if (hasPointerToBlock && event.target.tagName === 'BODY') { + /** + * Insert initial typed Block + */ + const newBlock = this.Editor.BlockManager.insert(); + + this.Editor.Caret.setToBlock(newBlock); + + /** + * And highlight + */ + this.Editor.BlockManager.highlightCurrentNode(); + + /** + * Move toolbar and show plus button because new Block is empty + */ + this.Editor.Toolbar.move(); + this.Editor.Toolbar.plusButton.show(); + } + } + + /** + * All clicks on document + * @param {MouseEvent} event - Click + */ + documentClicked(event) { + /** + * Close Inline Toolbar when nothing selected + * Do not fire check on clicks at the Inline Toolbar buttons + */ + const clickedOnInlineToolbarButton = event.target.closest(`.${this.Editor.InlineToolbar.CSS.inlineToolbar}`); + const clickedInsideofEditor = event.target.closest(`.${this.CSS.editorWrapper}`); + + /** Clear highlightings and pointer on BlockManager */ + if (!clickedInsideofEditor && !Selection.isAtEditor) { + this.Editor.BlockManager.dropPointer(); + this.Editor.Toolbar.close(); + } + + if (!clickedOnInlineToolbarButton) { + this.Editor.InlineToolbar.handleShowingEvent(event); + } + + if (Selection.isAtEditor) { + this.Editor.BlockManager.setCurrentBlockByChildNode(Selection.anchorNode); + } + } + + /** + * All clicks on the redactor zone + * + * @param {MouseEvent} event + * + * @description + * 1. Save clicked Block as a current {@link BlockManager#currentNode} + * it uses for the following: + * - add CSS modifier for the selected Block + * - on Enter press, we make a new Block under that + * + * 2. Move and show the Toolbar + * + * 3. Set a Caret + * + * 4. By clicks on the Editor's bottom zone: + * - if last Block is empty, set a Caret to this + * - otherwise, add a new empty Block and set a Caret to that + * + * 5. Hide the Inline Toolbar + * + * @see selectClickedBlock + * + */ + redactorClicked(event) { + const clickedNode = event.target; + + /** + * Select clicked Block as Current + */ + try { + /** + * Renew Current Block + */ + this.Editor.BlockManager.setCurrentBlockByChildNode(clickedNode); + + /** + * Highlight Current Node + */ + this.Editor.BlockManager.highlightCurrentNode(); + } catch (e) { + /** + * If clicked outside first-level Blocks, set Caret to the last empty Block + */ + this.Editor.Caret.setToTheLastBlock(); + } + + /** + * Move toolbar and open + */ + this.Editor.Toolbar.open(); + + /** + * Hide the Plus Button + * */ + this.Editor.Toolbar.plusButton.hide(); + + if (!this.Editor.BlockManager.currentBlock) { + this.Editor.BlockManager.insert(); + } + + /** + * Show the Plus Button if: + * - Block is an initial-block (Text) + * - Block is empty + */ + let isInitialBlock = this.Editor.Tools.isInitial(this.Editor.BlockManager.currentBlock.tool), + isEmptyBlock = this.Editor.BlockManager.currentBlock.isEmpty; + + if (isInitialBlock && isEmptyBlock) { + this.Editor.Toolbar.plusButton.show(); + } + } + + /** + * Append prebuilded sprite with SVG icons + */ + appendSVGSprite() { + let spriteHolder = $.make('div'); + + spriteHolder.hidden = true; + spriteHolder.style.display = 'none'; + spriteHolder.innerHTML = sprite; + + $.append(this.nodes.wrapper, spriteHolder); + } + + /** + * Clean editor`s UI + */ + destroy() { + this.nodes.holder.innerHTML = ''; + } +} diff --git a/src/components/polyfills.ts b/src/components/polyfills.ts new file mode 100644 index 00000000..9fc6c560 --- /dev/null +++ b/src/components/polyfills.ts @@ -0,0 +1,89 @@ +'use strict'; + +/** + * Extend Element interface to include prefixed and experimental properties + */ +interface Element { + matchesSelector: (selector: string) => boolean; + mozMatchesSelector: (selector: string) => boolean; + oMatchesSelector: (selector: string) => boolean; + + prepend: (nodes: Node|Node[]|any) => void; +} + +/** + * The Element.matches() method returns true if the element + * would be selected by the specified selector string; + * otherwise, returns false. + * + * {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/matches#Polyfill} + */ +if (!Element.prototype.matches) { + Element.prototype.matches = Element.prototype.matchesSelector || + Element.prototype.mozMatchesSelector || + Element.prototype.msMatchesSelector || + Element.prototype.oMatchesSelector || + Element.prototype.webkitMatchesSelector || + function(s) { + const matches = (this.document || this.ownerDocument).querySelectorAll(s); + let i = matches.length; + + while (--i >= 0 && matches.item(i) !== this) { + } + + return i > -1; + }; +} + +/** + * The Element.closest() method returns the closest ancestor + * of the current element (or the current element itself) which + * matches the selectors given in parameter. + * If there isn't such an ancestor, it returns null. + * + * {@link https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill} + */ +if (!Element.prototype.closest) { + Element.prototype.closest = function(s) { + let el = this; + + if (!document.documentElement.contains(el)) { + return null; + } + + do { + if (el.matches(s)) { + return el; + } + + el = el.parentElement || el.parentNode; + } while (el !== null); + + return null; + }; +} + +/** + * The ParentNode.prepend method inserts a set of Node objects + * or DOMString objects before the first child of the ParentNode. + * DOMString objects are inserted as equivalent Text nodes. + * + * {@link https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/prepend#Polyfill} + */ +if (!Element.prototype.prepend) { + Element.prototype.prepend = function prepend(nodes: Node|Node[]|any) { + const docFrag = document.createDocumentFragment(); + + if (!Array.isArray(nodes)) { + nodes = [ nodes ]; + } + + nodes.forEach((node: Node|any) => { + const isNode = node instanceof Node; + + docFrag.appendChild(isNode ? node : document.createTextNode(String(node))); + }); + + this.insertBefore(docFrag, this.firstChild); + }; +} diff --git a/src/components/selection.ts b/src/components/selection.ts new file mode 100644 index 00000000..809c5535 --- /dev/null +++ b/src/components/selection.ts @@ -0,0 +1,327 @@ +/** + * TextRange interface fot IE9- + */ +import _ from './utils'; + +interface TextRange { + boundingTop: number; + boundingLeft: number; + boundingBottom: number; + boundingRight: number; + boundingHeight: number; + boundingWidth: number; +} + +/** + * Interface for object returned by document.selection in IE9- + */ +interface MSSelection { + createRange: () => TextRange; + type: string; +} + +/** + * Extends Document interface for IE9- + */ +interface Document { + selection?: MSSelection; +} + +/** + * Working with selection + * @typedef {Selection} Selection + */ +export default class SelectionUtils { + + /** + * Editor styles + * @return {{editorWrapper: string, editorZone: string}} + * @constructor + */ + static get CSS(): {editorWrapper: string, editorZone: string} { + return { + editorWrapper : 'codex-editor', + editorZone : 'codex-editor__redactor', + }; + } + + /** + * Returns selected anchor + * {@link https://developer.mozilla.org/ru/docs/Web/API/Selection/anchorNode} + * @return {Node|null} + */ + static get anchorNode(): Node|null { + const selection = window.getSelection(); + + return selection ? selection.anchorNode : null; + } + + /** + * Returns selection offset according to the anchor node + * {@link https://developer.mozilla.org/ru/docs/Web/API/Selection/anchorOffset} + * @return {Number|null} + */ + static get anchorOffset(): number|null { + const selection = window.getSelection(); + + return selection ? selection.anchorOffset : null; + } + + /** + * Is current selection range collapsed + * @return {boolean|null} + */ + static get isCollapsed(): boolean|null { + const selection = window.getSelection(); + + return selection ? selection.isCollapsed : null; + } + + /** + * Check current selection if it is at Editor's zone + * @return {boolean} + */ + static get isAtEditor(): boolean { + const selection = SelectionUtils.get(); + + /** + * Something selected on document + */ + let selectedNode = (selection.anchorNode || selection.focusNode) as HTMLElement; + + if (selectedNode && selectedNode.nodeType === Node.TEXT_NODE) { + selectedNode = selectedNode.parentNode as HTMLElement; + } + + let editorZone = null; + if (selectedNode) { + editorZone = selectedNode.closest(`.${SelectionUtils.CSS.editorZone}`); + } + + /** + * SelectionUtils is not out of Editor because Editor's wrapper was found + */ + return editorZone && editorZone.nodeType === Node.ELEMENT_NODE; + } + + /** + * Return first range + * @return {Range|null} + */ + static get range(): Range { + const selection = window.getSelection(); + + return selection && selection.rangeCount ? selection.getRangeAt(0) : null; + } + + /** + * Calculates position and size of selected text + * @return {{x, y, width, height, top?, left?, bottom?, right?}} + */ + static get rect(): DOMRect|ClientRect { + let sel: Selection|MSSelection = (document as Document).selection, + range: TextRange|Range; + + let rect = { + x: 0, + y: 0, + width: 0, + height: 0, + } as DOMRect; + + if (sel && sel.type !== 'Control') { + sel = sel as MSSelection; + range = sel.createRange() as TextRange; + rect.x = range.boundingLeft; + rect.y = range.boundingTop; + rect.width = range.boundingWidth; + rect.height = range.boundingHeight; + + return rect; + } + + if (!window.getSelection) { + _.log('Method window.getSelection is not supported', 'warn'); + return rect; + } + + sel = window.getSelection(); + + if (!sel.rangeCount) { + _.log('Method SelectionUtils.rangeCount() is not supported', 'warn'); + return rect; + } + + range = sel.getRangeAt(0).cloneRange() as Range; + + if (range.getBoundingClientRect) { + rect = range.getBoundingClientRect() as DOMRect; + } + // Fall back to inserting a temporary element + if (rect.x === 0 && rect.y === 0) { + const span = document.createElement('span'); + + if (span.getBoundingClientRect) { + // Ensure span has dimensions and position by + // adding a zero-width space character + span.appendChild( document.createTextNode('\u200b') ); + range.insertNode(span); + rect = span.getBoundingClientRect() as DOMRect; + + const spanParent = span.parentNode; + + spanParent.removeChild(span); + + // Glue any broken text nodes back together + spanParent.normalize(); + } + } + + return rect; + } + + /** + * Returns selected text as String + * @returns {string} + */ + static get text(): string { + return window.getSelection ? window.getSelection().toString() : ''; + } + + /** + * Returns window SelectionUtils + * {@link https://developer.mozilla.org/ru/docs/Web/API/Window/getSelection} + * @return {Selection} + */ + public static get(): Selection { + return window.getSelection(); + } + + public instance: Selection = null; + public selection: Selection = null; + + /** + * This property can store SelectionUtils's range for restoring later + * @type {Range|null} + */ + public savedSelectionRange: Range = null; + + /** + * Save SelectionUtils's range + */ + public save(): void { + this.savedSelectionRange = SelectionUtils.range; + } + + /** + * Restore saved SelectionUtils's range + */ + public restore(): void { + if (!this.savedSelectionRange) { + return; + } + + const sel = window.getSelection(); + + sel.removeAllRanges(); + sel.addRange(this.savedSelectionRange); + } + + /** + * Clears saved selection + */ + public clearSaved(): void { + this.savedSelectionRange = null; + } + + /** + * Looks ahead to find passed tag from current selection + * + * @param {String} tagName - tag to found + * @param {String} [className] - tag's class name + * @param {Number} [searchDepth] - count of tags that can be included. For better performance. + * @return {HTMLElement|null} + */ + public findParentTag(tagName: string, className?: string, searchDepth = 10): HTMLElement|null { + const selection = window.getSelection(); + let parentTag = null; + + /** + * If selection is missing or no anchorNode or focusNode were found then return null + */ + if (!selection || !selection.anchorNode || !selection.focusNode) { + return null; + } + + /** + * Define Nodes for start and end of selection + */ + const boundNodes = [ + /** the Node in which the selection begins */ + selection.anchorNode as HTMLElement, + /** the Node in which the selection ends */ + selection.focusNode as HTMLElement, + ]; + + /** + * For each selection parent Nodes we try to find target tag [with target class name] + * It would be saved in parentTag variable + */ + boundNodes.forEach((parent) => { + /** Reset tags limit */ + let searchDepthIterable = searchDepth; + + while (searchDepthIterable > 0 && parent.parentNode) { + /** + * Check tag's name + */ + if (parent.tagName === tagName) { + /** + * Save the result + */ + parentTag = parent; + + /** + * Optional additional check for class-name mismatching + */ + if (className && parent.classList && !parent.classList.contains(className)) { + parentTag = null; + } + + /** + * If we have found required tag with class then go out from the cycle + */ + if (parentTag) { + break; + } + } + + /** + * Target tag was not found. Go up to the parent and check it + */ + parent = parent.parentNode as HTMLElement; + searchDepthIterable--; + } + }); + + /** + * Return found tag or null + */ + return parentTag; + } + + /** + * Expands selection range to the passed parent node + * + * @param {HTMLElement} element + */ + public expandToTag(element: HTMLElement): void { + const selection = window.getSelection(); + + selection.removeAllRanges(); + const range = document.createRange(); + + range.selectNodeContents(element); + selection.addRange(range); + } +} diff --git a/src/components/tools/paragraph b/src/components/tools/paragraph new file mode 160000 index 00000000..67af2369 --- /dev/null +++ b/src/components/tools/paragraph @@ -0,0 +1 @@ +Subproject commit 67af23696c591b6dc922b3c6c1b43070293281ea diff --git a/src/components/utils.ts b/src/components/utils.ts new file mode 100644 index 00000000..99083e40 --- /dev/null +++ b/src/components/utils.ts @@ -0,0 +1,199 @@ +/** + * @typedef {Object} ChainData + * @property {Object} data - data that will be passed to the success or fallback + * @property {Function} function - function's that must be called asynchronically + */ +interface ChainData { + data: any; + function: (...args: any[]) => any; +} + +/** + * Codex Editor Util + */ +export default class Util { + /** + * Custom logger + * + * @param {string} msg - message + * @param {string} type - logging type 'log'|'warn'|'error'|'info' + * @param {*} args - argument to log with a message + */ + public static log(msg: string, type: string = 'log', args?: any): void { + + if (!args) { + if (['time', 'timeEnd'].includes(type)) { + msg = `[codex-editor]: ${msg}`; + } else { + args = msg || 'undefined'; + msg = '[codex-editor]: %o'; + } + } else { + msg = '[codex-editor]: ' + msg; + } + + try { + if ( 'console' in window && window.console[ type ] ) { + if ( args ) { window.console[ type ]( msg, args ); } else { window.console[ type ]( msg ); } + } + } catch (e) { + // do nothing + } + } + + /** + * Returns basic keycodes as constants + * @return {{}} + */ + static get keyCodes(): object { + return { + BACKSPACE: 8, + TAB: 9, + ENTER: 13, + SHIFT: 16, + CTRL: 17, + ALT: 18, + ESC: 27, + SPACE: 32, + LEFT: 37, + UP: 38, + DOWN: 40, + RIGHT: 39, + DELETE: 46, + META: 91, + }; + } + + /** + * Fires a promise sequence asyncronically + * + * @param {ChainData[]} chains - list or ChainData's + * @param {Function} success - success callback + * @param {Function} fallback - callback that fires in case of errors + * + * @return {Promise} + */ + public static sequence(chains: ChainData[], success = () => {}, fallback = () => {}): Promise { + return new Promise((resolve) => { + /** + * pluck each element from queue + * First, send resolved Promise as previous value + * Each plugins "prepare" method returns a Promise, that's why + * reduce current element will not be able to continue while can't get + * a resolved Promise + */ + chains.reduce((previousValue, currentValue, iteration) => { + return previousValue + .then(() => waitNextBlock(currentValue, success, fallback)) + .then(() => { + // finished + if (iteration === chains.length - 1) { + resolve(); + } + }); + }, Promise.resolve()); + }); + + /** + * Decorator + * + * @param {ChainData} chainData + * + * @param {Function} successCallback + * @param {Function} fallbackCallback + * + * @return {Promise} + */ + function waitNextBlock( + chainData: ChainData, + successCallback: (data: any) => void, + fallbackCallback: (data: any) => void, + ): Promise { + return new Promise((resolve) => { + chainData.function() + .then(() => { + successCallback(chainData.data || {}); + }) + .then(resolve) + .catch(() => { + fallbackCallback(chainData.data || {}); + + // anyway, go ahead even it falls + resolve(); + }); + }); + } + } + + /** + * Make array from array-like collection + * + * @param {ArrayLike} collection + * + * @return {Array} + */ + public static array(collection: ArrayLike): any[] { + return Array.prototype.slice.call(collection); + } + + /** + * Check if passed variable is a function + * @param {*} fn + * @return {boolean} + */ + public static isFunction(fn: any): boolean { + return typeof fn === 'function'; + } + + /** + * Check if passed function is a class + * @param {function} fn + * @return {boolean} + */ + public static isClass(fn: any): boolean { + return typeof fn === 'function' && /^\s*class\s+/.test(fn.toString()); + } + + /** + * Checks if object is empty + * + * @param {Object} object + * @return {boolean} + */ + public static isEmpty(object: object): boolean { + return Object.keys(object).length === 0 && object.constructor === Object; + } + + /** + * Check if passed object is a Promise + * @param {*} object - object to check + * @return {Boolean} + */ + public static isPromise(object: any): boolean { + return Promise.resolve(object) === object; + } + + /** + * Check if passed element is contenteditable + * @param {HTMLElement} element + * @return {boolean} + */ + public static isContentEditable(element: HTMLElement): boolean { + return element.contentEditable === 'true'; + } + + /** + * Delays method execution + * + * @param {Function} method + * @param {Number} timeout + */ + public static delay(method: (...args: any[]) => any, timeout: number) { + return function() { + const context = this, + args = arguments; + + window.setTimeout(() => method.apply(context, args), timeout); + }; + } +} diff --git a/codex-editor.css b/src/styles/_legacy.css similarity index 97% rename from codex-editor.css rename to src/styles/_legacy.css index 1fb54f95..6b72d89e 100644 --- a/codex-editor.css +++ b/src/styles/_legacy.css @@ -1,24 +1,17 @@ -/** -* CodeX Editor stylesheets -* @author CodeX Team https://ifmo.su -* -* https://github.com/codex-team/codex.editor -*/ - - -@import url('icons.css'); - - /** * Editor wrapper */ .codex-editor{ position: relative; -} + border: 1px solid #ccc; + padding: 10px; - .codex-editor .hide { + .hide { display: none; } +} + + /** * Working zone - redactor @@ -27,6 +20,7 @@ position: relative; padding-bottom: 120px; min-height: 350px; + border: 1px dotted #ccc; } .ce-block__content a { @@ -69,7 +63,6 @@ */ .ce-toolbar__plus{ position: absolute; - background-image: url('fonts/codex_editor/icon-plus.svg'); background-position: center center; background-repeat: no-repeat; text-align: center; @@ -327,7 +320,6 @@ .ce-settings__anchor-hash { display: inline-block; - background: url('fonts/codex_editor/icon-hash.svg') no-repeat center center; background-size: contain; height: 11px; width: 10px; diff --git a/src/styles/animations.css b/src/styles/animations.css new file mode 100644 index 00000000..0e32012b --- /dev/null +++ b/src/styles/animations.css @@ -0,0 +1,73 @@ +.wobble { + animation-name: wobble; + animation-duration: 400ms; +} + +/** + * @author Nick Pettit - https://github.com/nickpettit/glide + */ +@keyframes wobble { + from { + transform: translate3d(0, 0, 0); + } + + 15% { + transform: translate3d(-5%, 0, 0) rotate3d(0, 0, 1, -5deg); + } + + 30% { + transform: translate3d(2%, 0, 0) rotate3d(0, 0, 1, 3deg); + } + + 45% { + transform: translate3d(-3%, 0, 0) rotate3d(0, 0, 1, -3deg); + } + + 60% { + transform: translate3d(2%, 0, 0) rotate3d(0, 0, 1, 2deg); + } + + 75% { + transform: translate3d(-1%, 0, 0) rotate3d(0, 0, 1, -1deg); + } + + to { + transform: translate3d(0, 0, 0); + } +} + +@keyframes bounceIn { + from, + 20%, + 40%, + 60%, + 80%, + to { + animation-timing-function: cubic-bezier(0.215, 0.61, 0.355, 1); + } + + 0% { + transform: scale3d(0.86, 0.86, 0.86); + } + + 20% { + transform: scale3d(1.1, 1.1, 1.1); + } + + 40% { + transform: scale3d(0.9, 0.9, 0.9); + } + + 60% { + transform: scale3d(1.03, 1.03, 1.03); + } + + 80% { + transform: scale3d(0.97, 0.97, 0.97); + } + + to { + transform: scale3d(1, 1, 1); + } +} + diff --git a/src/styles/block.css b/src/styles/block.css new file mode 100644 index 00000000..4df2359c --- /dev/null +++ b/src/styles/block.css @@ -0,0 +1,19 @@ +.ce-block { + &:first-of-type { + margin-top: 0; + } + + &--selected { + background-image: linear-gradient(17deg, rgba(243, 248, 255, 0.03) 63.45%, rgba(207, 214, 229, 0.27) 98%); + border-radius: 3px; + } + + &--stretched &__content { + max-width: none; + } + + &__content { + max-width: var(--content-width); + margin: 0 auto; + } +} diff --git a/src/styles/export.css b/src/styles/export.css new file mode 100644 index 00000000..25d0b731 --- /dev/null +++ b/src/styles/export.css @@ -0,0 +1,61 @@ +/** + * Block Tool wrapper + */ +.cdx-block { + padding: 0.7em 0; +} + +/** + * Input + */ +.cdx-input { + border: 1px solid var(--color-gray-border); + box-shadow: inset 0 1px 2px 0 rgba(35, 44, 72, 0.06); + border-radius: 3px; + padding: 10px 12px; + outline: none; + width: 100%; + box-sizing: border-box; +} + +/** + * Settings + */ +.cdx-settings-button { + @apply --toolbar-button; + + &--active { + color: var(--color-active-icon); + } +} + +/** + * Loader + */ +.cdx-loader { + position: relative; + border: 1px solid var(--color-gray-border); + + &::before { + content: ''; + position: absolute; + left: 50%; + top: 50%; + width: 18px; + height: 18px; + margin: -11px 0 0 -11px; + border: 2px solid var(--color-gray-border); + border-left-color: var(--color-active-icon); + border-radius: 50%; + animation: cdxRotation 1.2s infinite linear; + } +} + +@keyframes cdxRotation { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } +} diff --git a/src/styles/inline-toolbar.css b/src/styles/inline-toolbar.css new file mode 100644 index 00000000..2cc0859e --- /dev/null +++ b/src/styles/inline-toolbar.css @@ -0,0 +1,66 @@ +.ce-inline-toolbar { + @apply --overlay-pane; + padding: 6px; + transform: translateX(-50%); + display: none; + box-shadow: 0 6px 12px -6px rgba(131, 147, 173, 0.46), + 5px -12px 34px -13px rgba(97, 105, 134, 0.6), + 0 26px 52px 3px rgba(147, 165, 186, 0.24); + + &--showed { + display: block; + } + + [hidden] { + display: none !important; + } +} + +.ce-inline-tool { + @apply --toolbar-button; + line-height: normal; + + &--last { + margin-right: 0 !important; + } + + &--link { + .icon { + margin-top: -2px; + } + + .icon--unlink { + display: none; + } + } + + &--unlink { + .icon--link { + display: none; + } + .icon--unlink { + display: inline-block; + } + } + + &-input { + background-color: var(--bg-light); + outline: none; + border: 0; + border-radius: 3px; + margin: 6px 0 0; + font-size: 13px; + padding: 8px; + width: 100%; + box-sizing: border-box; + display: none; + + &::placeholder { + color: var(--grayText); + } + + &--showed { + display: block; + } + } +} diff --git a/src/styles/main.css b/src/styles/main.css new file mode 100644 index 00000000..6d2e1da2 --- /dev/null +++ b/src/styles/main.css @@ -0,0 +1,9 @@ +@import url('variables.css'); +@import url('ui.css'); +@import url('toolbar.css'); +@import url('toolbox.css'); +@import url('inline-toolbar.css'); +@import url('settings.css'); +@import url('block.css'); +@import url('animations.css'); +@import url('export.css'); diff --git a/src/styles/settings.css b/src/styles/settings.css new file mode 100644 index 00000000..49de0684 --- /dev/null +++ b/src/styles/settings.css @@ -0,0 +1,66 @@ +.ce-settings { + @apply --overlay-pane; + right: 5px; + top: 35px; + min-width: 124px; + + &::before{ + left: auto; + right: 12px; + } + + display: none; + + &--opened { + display: block; + } + + &__plugin-zone { + &:not(:empty){ + padding: 6px 6px 0; + } + } + + &__default-zone { + &:not(:empty){ + padding: 6px; + } + } + + &__button { + @apply --toolbar-button; + line-height: 32px; + + &--disabled { + cursor: not-allowed !important; + opacity: .3; + } + + &--selected { + color: var(--color-active-icon); + } + + &--delete { + transition: background-color 300ms ease; + will-change: background-color; + + .icon { + transition: transform 200ms ease-out; + will-change: transform; + } + } + + &--confirm { + background-color: var(--color-confirm); + color: #fff; + + &:hover { + background-color: color-mod(var(--color-confirm) blackness(+5%)) !important; + } + + .icon { + transform: rotate(90deg); + } + } + } +} diff --git a/src/styles/toolbar.css b/src/styles/toolbar.css new file mode 100644 index 00000000..953090ce --- /dev/null +++ b/src/styles/toolbar.css @@ -0,0 +1,57 @@ +.ce-toolbar { + position: absolute; + left: 0; + right: 0; + top: 0; + /*opacity: 0;*/ + /*visibility: hidden;*/ + transition: opacity 100ms ease; + will-change: opacity, transform; + display: none; + + &--opened { + display: block; + /*opacity: 1;*/ + /*visibility: visible;*/ + } + + &__content { + max-width: var(--content-width); + margin: 0 auto; + position: relative; + } + + &__plus { + @apply --toolbox-button; + + position: absolute; + left: calc(calc(var(--toolbox-buttons-size) + 10px) * -1); + + &--hidden { + display: none; + } + } + + /** + * Block actions Zone + * ------------------------- + */ + &__actions { + position: absolute; + right: 0; + top: 0; + padding-right: 16px; + + &-buttons { + text-align: right; + } + } + + &__settings-btn { + display: inline-block; + width: 24px; + height: 24px; + color: var(--grayText); + cursor: pointer; + } +} diff --git a/src/styles/toolbox.css b/src/styles/toolbox.css new file mode 100644 index 00000000..16e292bd --- /dev/null +++ b/src/styles/toolbox.css @@ -0,0 +1,17 @@ +.ce-toolbox { + position: absolute; + visibility: hidden; + transition: opacity 100ms ease; + will-change: opacity; + display: flex; + flex-direction: row; + + &--opened { + opacity: 1; + visibility: visible; + } + + &__button { + @apply --toolbox-button; + } +} diff --git a/src/styles/ui.css b/src/styles/ui.css new file mode 100644 index 00000000..9e3c62ab --- /dev/null +++ b/src/styles/ui.css @@ -0,0 +1,42 @@ +/** +* Editor wrapper +*/ +.codex-editor { + position: relative; + box-sizing: border-box; + + .hide { + display: none; + } + + &__redactor { + padding-bottom: 300px; + } + + svg { + fill: currentColor; + vertical-align: middle; + max-height: 100%; + } +} + +/** + * Set color for native selection + */ +::selection{ + background-color: var(--selectionColor); +} + +/** + * Add placeholder to content editable elements with data attribute + * data-placeholder="Hello world!" + */ +[contentEditable=true][data-placeholder]:empty::before{ + content: attr(data-placeholder); + color: var(--grayText); + font-weight: normal; +} + +[contentEditable=true][data-placeholder]:empty:focus::before { + opacity: 0.3; +} diff --git a/src/styles/variables.css b/src/styles/variables.css new file mode 100644 index 00000000..e4f6da50 --- /dev/null +++ b/src/styles/variables.css @@ -0,0 +1,125 @@ +:root { + /** + * Selection color + */ + --selectionColor: rgba(61,166,239,0.63); + + /** + * Toolbar buttons + */ + --bg-light: #eff2f5; + + /** + * All gray texts: placeholders, settings + */ + --grayText: #707684; + + /** + * Blue icons + */ + --color-active-icon: #388AE5; + + /** + * Gray border, loaders + */ + --color-gray-border: #E8E8EB; + + /** + * Block content width + */ + --content-width: 650px; + + /** + * Toolbar buttons height and width + */ + --toolbar-buttons-size: 34px; + + /** + * Toolbar Plus Button and Toolbox buttons height and width + */ + --toolbox-buttons-size: 24px; + + /** + * Confirm deletion bg + */ + --color-confirm: #E24A4A; + + --overlay-pane: { + position: absolute; + background-color: #FFFFFF; + box-shadow: 0 8px 23px -6px rgba(21,40,54,0.31), 22px -14px 34px -18px rgba(33,48,73,0.26); + border-radius: 4px; + z-index: 2; + + &::before { + content: ''; + width: 15px; + height: 15px; + position: absolute; + top: -7px; + left: 50%; + margin-left: -7px; + transform: rotate(-45deg); + background-color: #fff; + z-index: -1; + } + }; + + /** + * Styles for Toolbox Buttons and Plus Button + */ + --toolbox-button: { + color: var(--grayText); + cursor: pointer; + width: var(--toolbox-buttons-size); + height: var(--toolbox-buttons-size); + display: inline-flex; + justify-content: center; + align-items: center; + + &:not(:last-of-type){ + margin-right: 10px; + } + + &:hover, + &--active { + color: var(--color-active-icon); + } + + &--active{ + animation: bounceIn 0.75s 1; + animation-fill-mode: forwards; + } + + }; + + /** + * Styles for Settings Button in Toolbar + */ + --toolbar-button: { + display: inline-block; + width: 34px; + height: 34px; + line-height: 34px; + text-align: center; + border-radius: 3px; + cursor: pointer; + border: 0; + outline: none; + background-color: transparent; + vertical-align: bottom; + color: var(--grayText); + + &:not(:last-of-type){ + margin-right: 5px; + } + + &:hover { + background-color: var(--bg-light); + } + + &--active { + color: var(--color-active-icon); + } + }; +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..ad52c423 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions" : { + "sourceMap": true, + "target": "es2017", + "declaration": false, + "moduleResolution": "node", // This resolution strategy attempts to mimic the Node.js module resolution mechanism at runtime + "lib": ["es2017", "dom"] + } +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 00000000..7833928e --- /dev/null +++ b/tslint.json @@ -0,0 +1,17 @@ +{ + "extends": "tslint:recommended", + "rules": { + "indent": [true, "spaces", 2], + "interface-name": false, + "quotemark": [true, "single"], + "no-console": false, + "no-empty-interface": false, + "one-variable-per-declaration": false, + "object-literal-sort-keys": false, + "ordered-imports": [true, { + "import-sources-order": "any", + "named-imports-order": "case-insensitive" + }], + "no-empty": false + } +} diff --git a/webpack.config.js b/webpack.config.js index 31562936..2030a3ed 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -20,79 +20,195 @@ const VERSION = process.env.VERSION || pkg.version; * Plugins for bundle * @type {webpack} */ -var webpack = require('webpack'); -var ExtractTextWebpackPlugin = require('extract-text-webpack-plugin'); +var webpack = require('webpack'); + +/** + * File system + */ +var fs = require('fs'); + +/** + * Available CodeX Editor modules placed in components/modules folder + * They will required automatically. + * Folders and files starting with '_' will be skipped + * @type {Array} + */ +var editorModules = fs.readdirSync('./src/components/modules').filter( name => /.(j|t)s$/.test(name) && name.substring(0,1) !== '_' ); + +editorModules.forEach( name => { + console.log('Require modules/' + name); +}); + +/** + * Options for the Babel + */ +var babelLoader = { + loader: 'babel-loader', + options: { + cacheDirectory: true, + presets: [ + "env" + ], + plugins: [ + /** + * Dont need to use «.default» after «export default Class Ui {}» + * @see {@link https://github.com/59naga/babel-plugin-add-module-exports} + */ + 'add-module-exports', + /** + * Babel transforms some awesome ES6 features to ES5 with extra code, such as Class, JSX. + * This plugin makes all generated extra codes to one module which significantly reduces the bundle code size. + * + * {@link https://github.com/brianZeng/babel-plugin-transform-helper} + * @since 11 dec 2017 - removed due to plugin does not supports class inheritance + */ + // ['babel-plugin-transform-helper', { + // helperFilename:'build/__tmp_babel_helpers.js' + // }], + 'class-display-name', + ] + } +}; + + module.exports = { - entry: { - "codex-editor": "./codex" - }, - output: { - filename: "[name].js", - library: ["codex","editor"] - }, + entry: { + 'codex-editor': ['babel-polyfill', './src/codex'] + }, + output: { + path: path.resolve(__dirname, 'build'), + filename: '[name].js', + library: [ 'CodexEditor' ], + libraryTarget: 'umd' + }, - watch: true, + watch: true, + watchOptions: { + aggregateTimeout: 50 + }, - watchOptions: { - aggregateTimeOut: 50 - }, + devtool: NODE_ENV == 'development' ? 'source-map' : null, - devtool: NODE_ENV == 'development' ? "source-map" : null, - - resolve : { - fallback: path.join(__dirname, "node_modules"), - modulesDirectories : ['./node_modules', './modules'], - extensions : ['', '.js', '.json'] - }, - - resolveLoader : { - modulesDirectories: ['./node_modules'], - moduleTemplates: ["*-webpack-loader", "*-web-loader", "*-loader", "*"], - extensions: ['', '.js'] - }, - - plugins: [ - - /** Pass variables into modules */ - new webpack.DefinePlugin({ - NODE_ENV: JSON.stringify(NODE_ENV), - VERSION: JSON.stringify(VERSION) - }), - - /** Минифицируем CSS и JS */ - new webpack.optimize.UglifyJsPlugin({ - /** Disable warning messages. Cant disable uglify for 3rd party libs such as html-janitor */ - compress: { - warnings: false - } - }), - - /** Block biuld if errors found */ - new webpack.NoErrorsPlugin(), - - ], - - module : { - - loaders : [{ - test : /\.js$/, - exclude: /(node_modules)/, - loader : 'babel', - query: { - presets: [__dirname + '/node_modules/babel-preset-es2015'] - } - }, - { - test : /\.js$/, - loader: 'eslint-loader?fix=true', - exclude: /(node_modules)/ - }, - { - test : /\.css$/, - exclude: /(node_modules)/, - loader: ExtractTextWebpackPlugin.extract('style-loader', 'css-loader') - }] + /** + * Tell webpack what directories should be searched when resolving modules. + */ + resolve : { + // fallback: path.join(__dirname, 'node_modules'), + modules : [ path.join(__dirname, "src"), "node_modules"], + extensions: ['.js', '.ts'], + alias: { + 'utils': path.resolve(__dirname + '/src/components/', './utils'), + 'dom': path.resolve(__dirname + '/src/components/', './dom'), } -}; \ No newline at end of file + }, + // + + // resolveLoader : { + // modules: [ path.resolve(__dirname, "src"), "node_modules" ], + // moduleTemplates: ['*-webpack-loader', '*-web-loader', '*-loader', '*'], + // extensions: ['.js'] + // }, + + plugins: [ + + /** Pass variables into modules */ + new webpack.DefinePlugin({ + NODE_ENV: JSON.stringify(NODE_ENV), + VERSION: JSON.stringify(VERSION), + editorModules: JSON.stringify(editorModules) + }), + + /** + * Setting up a dynamic requires that we use to autoload Editor Modules from 'components/modules' dir + * {@link https://webpack.js.org/plugins/context-replacement-plugin/} + */ + new webpack.ContextReplacementPlugin( + /src\/components\/modules/, + false, // newContentRecursive=false because we dont need to include folders + new RegExp( + '[^_]' + // dont match names started with '_' + `(${editorModules.join('|')})` + // module names pattern: (events.js|ui.js|...) + '$' // at the end of path + ) + ), + + /** + * Automatically load global visible modules + * instead of having to import/require them everywhere. + */ + new webpack.ProvidePlugin({ + '_': 'utils', + '$': 'dom', + 'Module': './../__module.ts', + }), + + /** Минифицируем CSS и JS */ + // new webpack.optimize.UglifyJsPlugin({ + /** Disable warning messages. Cant disable uglify for 3rd party libs such as html-janitor */ + // compress: { + // warnings: false + // } + // }), + + /** Block biuld if errors found */ + // new webpack.NoErrorsPlugin(), + + ], + + module : { + rules : [ + { + test: /\.ts$/, + use: [ + babelLoader, + { + loader: 'ts-loader' + }, + { + loader: 'tslint-loader', + } + ] + }, + { + test : /\.js$/, + use: [ + babelLoader, + { + loader: 'eslint-loader?fix=true&esModules=true', + } + ], + exclude: [ + /(node_modules|build)/, // dont need to look in '/build' to prevent analyse __tmp_babel_helper.js + /src[\\\/]components[\\\/]tools/ + ] + }, + { + test: /\.css$/, + exclude: /node_modules/, + use: [ + { + loader: 'css-loader', + options: { + // minimize: 1, + importLoaders: 1 + } + }, + 'postcss-loader' + ] + }, + { + test: /\.(svg)$/, + use: [ + { + loader: 'raw-loader', + } + ] + } + ] + }, + optimization: { + minimize: true + }, +}; diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..4d196324 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,6048 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@codexteam/shortcuts@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@codexteam/shortcuts/-/shortcuts-1.0.0.tgz#9d66a7c00c93be05b7940d46d2a82af442e8b46d" + +"@csstools/convert-colors@^1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@csstools/convert-colors/-/convert-colors-1.4.0.tgz#ad495dc41b12e75d588c6db8b9834f08fa131eb7" + +"@mrmlnc/readdir-enhanced@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" + dependencies: + call-me-maybe "^1.0.1" + glob-to-regexp "^0.3.0" + +"@nodelib/fs.stat@^1.0.1": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.0.tgz#50c1e2260ac0ed9439a181de3725a0168d59c48a" + +"@webassemblyjs/ast@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.5.13.tgz#81155a570bd5803a30ec31436bc2c9c0ede38f25" + dependencies: + "@webassemblyjs/helper-module-context" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/wast-parser" "1.5.13" + debug "^3.1.0" + mamacro "^0.0.3" + +"@webassemblyjs/floating-point-hex-parser@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.5.13.tgz#29ce0baa97411f70e8cce68ce9c0f9d819a4e298" + +"@webassemblyjs/helper-api-error@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.5.13.tgz#e49b051d67ee19a56e29b9aa8bd949b5b4442a59" + +"@webassemblyjs/helper-buffer@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.5.13.tgz#873bb0a1b46449231137c1262ddfd05695195a1e" + dependencies: + debug "^3.1.0" + +"@webassemblyjs/helper-code-frame@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.5.13.tgz#1bd2181b6a0be14e004f0fe9f5a660d265362b58" + dependencies: + "@webassemblyjs/wast-printer" "1.5.13" + +"@webassemblyjs/helper-fsm@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.5.13.tgz#cdf3d9d33005d543a5c5e5adaabf679ffa8db924" + +"@webassemblyjs/helper-module-context@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.5.13.tgz#dc29ddfb51ed657655286f94a5d72d8a489147c5" + dependencies: + debug "^3.1.0" + mamacro "^0.0.3" + +"@webassemblyjs/helper-wasm-bytecode@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.5.13.tgz#03245817f0a762382e61733146f5773def15a747" + +"@webassemblyjs/helper-wasm-section@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.5.13.tgz#efc76f44a10d3073b584b43c38a179df173d5c7d" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-buffer" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/wasm-gen" "1.5.13" + debug "^3.1.0" + +"@webassemblyjs/ieee754@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.5.13.tgz#573e97c8c12e4eebb316ca5fde0203ddd90b0364" + dependencies: + ieee754 "^1.1.11" + +"@webassemblyjs/leb128@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.5.13.tgz#ab52ebab9cec283c1c1897ac1da833a04a3f4cee" + dependencies: + long "4.0.0" + +"@webassemblyjs/utf8@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.5.13.tgz#6b53d2cd861cf94fa99c1f12779dde692fbc2469" + +"@webassemblyjs/wasm-edit@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.5.13.tgz#c9cef5664c245cf11b3b3a73110c9155831724a8" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-buffer" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/helper-wasm-section" "1.5.13" + "@webassemblyjs/wasm-gen" "1.5.13" + "@webassemblyjs/wasm-opt" "1.5.13" + "@webassemblyjs/wasm-parser" "1.5.13" + "@webassemblyjs/wast-printer" "1.5.13" + debug "^3.1.0" + +"@webassemblyjs/wasm-gen@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.5.13.tgz#8e6ea113c4b432fa66540189e79b16d7a140700e" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/ieee754" "1.5.13" + "@webassemblyjs/leb128" "1.5.13" + "@webassemblyjs/utf8" "1.5.13" + +"@webassemblyjs/wasm-opt@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.5.13.tgz#147aad7717a7ee4211c36b21a5f4c30dddf33138" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-buffer" "1.5.13" + "@webassemblyjs/wasm-gen" "1.5.13" + "@webassemblyjs/wasm-parser" "1.5.13" + debug "^3.1.0" + +"@webassemblyjs/wasm-parser@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.5.13.tgz#6f46516c5bb23904fbdf58009233c2dd8a54c72f" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-api-error" "1.5.13" + "@webassemblyjs/helper-wasm-bytecode" "1.5.13" + "@webassemblyjs/ieee754" "1.5.13" + "@webassemblyjs/leb128" "1.5.13" + "@webassemblyjs/utf8" "1.5.13" + +"@webassemblyjs/wast-parser@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.5.13.tgz#5727a705d397ae6a3ae99d7f5460acf2ec646eea" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/floating-point-hex-parser" "1.5.13" + "@webassemblyjs/helper-api-error" "1.5.13" + "@webassemblyjs/helper-code-frame" "1.5.13" + "@webassemblyjs/helper-fsm" "1.5.13" + long "^3.2.0" + mamacro "^0.0.3" + +"@webassemblyjs/wast-printer@1.5.13": + version "1.5.13" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.5.13.tgz#bb34d528c14b4f579e7ec11e793ec50ad7cd7c95" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/wast-parser" "1.5.13" + long "^3.2.0" + +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + +acorn-dynamic-import@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz#901ceee4c7faaef7e07ad2a47e890675da50a278" + dependencies: + acorn "^5.0.0" + +acorn-jsx@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" + dependencies: + acorn "^3.0.4" + +acorn@^3.0.4: + version "3.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" + +acorn@^5.0.0, acorn@^5.5.0, acorn@^5.6.2: + version "5.7.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.1.tgz#f095829297706a7c9776958c0afc8930a9b9d9d8" + +ajv-keywords@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" + +ajv-keywords@^3.0.0, ajv-keywords@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.2.0.tgz#e86b819c602cf8821ad637413698f1dec021847a" + +ajv@^5.0.0, ajv@^5.2.3, ajv@^5.3.0: + version "5.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" + dependencies: + co "^4.6.0" + fast-deep-equal "^1.0.0" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.3.0" + +ajv@^6.0.1, ajv@^6.1.0: + version "6.5.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.5.2.tgz#678495f9b82f7cca6be248dd92f59bff5e1f4360" + dependencies: + fast-deep-equal "^2.0.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.1" + +alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" + +ansi-escapes@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + dependencies: + color-convert "^1.9.0" + +any-promise@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-0.1.0.tgz#830b680aa7e56f33451d4b049f3bd8044498ee27" + +anymatch@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" + dependencies: + micromatch "^3.1.4" + normalize-path "^2.1.1" + +aproba@^1.0.3, aproba@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" + +are-we-there-yet@~1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.6" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-diff@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" + +arr-flatten@^1.0.1, arr-flatten@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" + +arr-union@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" + +array-find-index@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" + +array-iterate@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/array-iterate/-/array-iterate-1.1.2.tgz#f66a57e84426f8097f4197fbb6c051b8e5cdf7d8" + +array-union@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" + dependencies: + array-uniq "^1.0.1" + +array-uniq@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +array-unique@^0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" + +arrify@^1.0.0, arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asn1.js@^4.0.0: + version "4.10.1" + resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" + dependencies: + bn.js "^4.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +assert@^1.1.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + dependencies: + util "0.10.3" + +assign-symbols@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async@^1.3.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +async@^2.4.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" + dependencies: + lodash "^4.17.10" + +atob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.1.tgz#ae2d5a729477f289d60dd7f96a6314a22dd6c22a" + +autoprefixer@^6.3.1: + version "6.7.7" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" + dependencies: + browserslist "^1.7.6" + caniuse-db "^1.0.30000634" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^5.2.16" + postcss-value-parser "^3.2.3" + +autoprefixer@^7.1.1: + version "7.2.6" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-7.2.6.tgz#256672f86f7c735da849c4f07d008abb056067dc" + dependencies: + browserslist "^2.11.3" + caniuse-lite "^1.0.30000805" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^6.0.17" + postcss-value-parser "^3.2.3" + +autoprefixer@^9.0.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.1.0.tgz#566a70d1148046b96b31efa08090f1999ffb6d8c" + dependencies: + browserslist "^4.0.1" + caniuse-lite "^1.0.30000872" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.2" + postcss-value-parser "^3.2.3" + +babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" + dependencies: + chalk "^1.1.3" + esutils "^2.0.2" + js-tokens "^3.0.2" + +babel-core@^6.26.0, babel-core@^6.26.3: + version "6.26.3" + resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" + dependencies: + babel-code-frame "^6.26.0" + babel-generator "^6.26.0" + babel-helpers "^6.24.1" + babel-messages "^6.23.0" + babel-register "^6.26.0" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + convert-source-map "^1.5.1" + debug "^2.6.9" + json5 "^0.5.1" + lodash "^4.17.4" + minimatch "^3.0.4" + path-is-absolute "^1.0.1" + private "^0.1.8" + slash "^1.0.0" + source-map "^0.5.7" + +babel-generator@^6.26.0: + version "6.26.1" + resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" + dependencies: + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + detect-indent "^4.0.0" + jsesc "^1.3.0" + lodash "^4.17.4" + source-map "^0.5.7" + trim-right "^1.0.1" + +babel-helper-bindify-decorators@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz#14c19e5f142d7b47f19a52431e52b1ccbc40a330" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" + dependencies: + babel-helper-explode-assignable-expression "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-call-delegate@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-define-map@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-explode-assignable-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" + dependencies: + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-explode-class@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz#7dc2a3910dee007056e1e31d640ced3d54eaa9eb" + dependencies: + babel-helper-bindify-decorators "^6.24.1" + babel-runtime "^6.22.0" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-function-name@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" + dependencies: + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-get-function-arity@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-hoist-variables@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-optimise-call-expression@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-helper-regex@^6.24.1: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" + dependencies: + babel-runtime "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-helper-remap-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helper-replace-supers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" + dependencies: + babel-helper-optimise-call-expression "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-helpers@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-loader@^7.1.5: + version "7.1.5" + resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.5.tgz#e3ee0cd7394aa557e013b02d3e492bfd07aa6d68" + dependencies: + find-cache-dir "^1.0.0" + loader-utils "^1.0.2" + mkdirp "^0.5.1" + +babel-messages@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-add-module-exports@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz#9ae9a1f4a8dc67f0cdec4f4aeda1e43a5ff65e25" + +babel-plugin-check-es2015-constants@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-class-display-name@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/babel-plugin-class-display-name/-/babel-plugin-class-display-name-2.1.0.tgz#198ff12b9eabd33e011ee13f2f9898985608b4d1" + +babel-plugin-syntax-async-functions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" + +babel-plugin-syntax-async-generators@^6.5.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" + +babel-plugin-syntax-class-constructor-call@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz#9cb9d39fe43c8600bec8146456ddcbd4e1a76416" + +babel-plugin-syntax-class-properties@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" + +babel-plugin-syntax-decorators@^6.13.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" + +babel-plugin-syntax-do-expressions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz#5747756139aa26d390d09410b03744ba07e4796d" + +babel-plugin-syntax-dynamic-import@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" + +babel-plugin-syntax-exponentiation-operator@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" + +babel-plugin-syntax-export-extensions@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz#70a1484f0f9089a4e84ad44bac353c95b9b12721" + +babel-plugin-syntax-function-bind@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz#48c495f177bdf31a981e732f55adc0bdd2601f46" + +babel-plugin-syntax-object-rest-spread@^6.8.0: + version "6.13.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" + +babel-plugin-syntax-trailing-function-commas@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" + +babel-plugin-transform-async-generator-functions@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz#f058900145fd3e9907a6ddf28da59f215258a5db" + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-generators "^6.5.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-async-to-generator@^6.22.0, babel-plugin-transform-async-to-generator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" + dependencies: + babel-helper-remap-async-to-generator "^6.24.1" + babel-plugin-syntax-async-functions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-class-constructor-call@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz#80dc285505ac067dcb8d6c65e2f6f11ab7765ef9" + dependencies: + babel-plugin-syntax-class-constructor-call "^6.18.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-class-properties@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" + dependencies: + babel-helper-function-name "^6.24.1" + babel-plugin-syntax-class-properties "^6.8.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-decorators@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz#788013d8f8c6b5222bdf7b344390dfd77569e24d" + dependencies: + babel-helper-explode-class "^6.24.1" + babel-plugin-syntax-decorators "^6.13.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-do-expressions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-do-expressions/-/babel-plugin-transform-do-expressions-6.22.0.tgz#28ccaf92812d949c2cd1281f690c8fdc468ae9bb" + dependencies: + babel-plugin-syntax-do-expressions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-arrow-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-block-scoping@^6.23.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" + dependencies: + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + lodash "^4.17.4" + +babel-plugin-transform-es2015-classes@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" + dependencies: + babel-helper-define-map "^6.24.1" + babel-helper-function-name "^6.24.1" + babel-helper-optimise-call-expression "^6.24.1" + babel-helper-replace-supers "^6.24.1" + babel-messages "^6.23.0" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-computed-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" + dependencies: + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-destructuring@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-duplicate-keys@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-for-of@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-function-name@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" + dependencies: + babel-helper-function-name "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" + dependencies: + babel-plugin-transform-es2015-modules-commonjs "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: + version "6.26.2" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz#58a793863a9e7ca870bdc5a881117ffac27db6f3" + dependencies: + babel-plugin-transform-strict-mode "^6.24.1" + babel-runtime "^6.26.0" + babel-template "^6.26.0" + babel-types "^6.26.0" + +babel-plugin-transform-es2015-modules-systemjs@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" + dependencies: + babel-helper-hoist-variables "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-modules-umd@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" + dependencies: + babel-plugin-transform-es2015-modules-amd "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + +babel-plugin-transform-es2015-object-super@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" + dependencies: + babel-helper-replace-supers "^6.24.1" + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-parameters@^6.23.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" + dependencies: + babel-helper-call-delegate "^6.24.1" + babel-helper-get-function-arity "^6.24.1" + babel-runtime "^6.22.0" + babel-template "^6.24.1" + babel-traverse "^6.24.1" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-shorthand-properties@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-spread@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-sticky-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-plugin-transform-es2015-template-literals@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-typeof-symbol@^6.23.0: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" + dependencies: + babel-runtime "^6.22.0" + +babel-plugin-transform-es2015-unicode-regex@^6.22.0: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" + dependencies: + babel-helper-regex "^6.24.1" + babel-runtime "^6.22.0" + regexpu-core "^2.0.0" + +babel-plugin-transform-exponentiation-operator@^6.22.0, babel-plugin-transform-exponentiation-operator@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" + dependencies: + babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" + babel-plugin-syntax-exponentiation-operator "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-export-extensions@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz#53738b47e75e8218589eea946cbbd39109bbe653" + dependencies: + babel-plugin-syntax-export-extensions "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-function-bind@^6.22.0: + version "6.22.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-function-bind/-/babel-plugin-transform-function-bind-6.22.0.tgz#c6fb8e96ac296a310b8cf8ea401462407ddf6a97" + dependencies: + babel-plugin-syntax-function-bind "^6.8.0" + babel-runtime "^6.22.0" + +babel-plugin-transform-object-rest-spread@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06" + dependencies: + babel-plugin-syntax-object-rest-spread "^6.8.0" + babel-runtime "^6.26.0" + +babel-plugin-transform-regenerator@^6.22.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" + dependencies: + regenerator-transform "^0.10.0" + +babel-plugin-transform-strict-mode@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" + dependencies: + babel-runtime "^6.22.0" + babel-types "^6.24.1" + +babel-polyfill@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" + dependencies: + babel-runtime "^6.26.0" + core-js "^2.5.0" + regenerator-runtime "^0.10.5" + +babel-preset-env@^1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.7.0.tgz#dea79fa4ebeb883cd35dab07e260c1c9c04df77a" + dependencies: + babel-plugin-check-es2015-constants "^6.22.0" + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-to-generator "^6.22.0" + babel-plugin-transform-es2015-arrow-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" + babel-plugin-transform-es2015-block-scoping "^6.23.0" + babel-plugin-transform-es2015-classes "^6.23.0" + babel-plugin-transform-es2015-computed-properties "^6.22.0" + babel-plugin-transform-es2015-destructuring "^6.23.0" + babel-plugin-transform-es2015-duplicate-keys "^6.22.0" + babel-plugin-transform-es2015-for-of "^6.23.0" + babel-plugin-transform-es2015-function-name "^6.22.0" + babel-plugin-transform-es2015-literals "^6.22.0" + babel-plugin-transform-es2015-modules-amd "^6.22.0" + babel-plugin-transform-es2015-modules-commonjs "^6.23.0" + babel-plugin-transform-es2015-modules-systemjs "^6.23.0" + babel-plugin-transform-es2015-modules-umd "^6.23.0" + babel-plugin-transform-es2015-object-super "^6.22.0" + babel-plugin-transform-es2015-parameters "^6.23.0" + babel-plugin-transform-es2015-shorthand-properties "^6.22.0" + babel-plugin-transform-es2015-spread "^6.22.0" + babel-plugin-transform-es2015-sticky-regex "^6.22.0" + babel-plugin-transform-es2015-template-literals "^6.22.0" + babel-plugin-transform-es2015-typeof-symbol "^6.23.0" + babel-plugin-transform-es2015-unicode-regex "^6.22.0" + babel-plugin-transform-exponentiation-operator "^6.22.0" + babel-plugin-transform-regenerator "^6.22.0" + browserslist "^3.2.6" + invariant "^2.2.2" + semver "^5.3.0" + +babel-preset-stage-0@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-stage-0/-/babel-preset-stage-0-6.24.1.tgz#5642d15042f91384d7e5af8bc88b1db95b039e6a" + dependencies: + babel-plugin-transform-do-expressions "^6.22.0" + babel-plugin-transform-function-bind "^6.22.0" + babel-preset-stage-1 "^6.24.1" + +babel-preset-stage-1@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz#7692cd7dcd6849907e6ae4a0a85589cfb9e2bfb0" + dependencies: + babel-plugin-transform-class-constructor-call "^6.24.1" + babel-plugin-transform-export-extensions "^6.22.0" + babel-preset-stage-2 "^6.24.1" + +babel-preset-stage-2@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz#d9e2960fb3d71187f0e64eec62bc07767219bdc1" + dependencies: + babel-plugin-syntax-dynamic-import "^6.18.0" + babel-plugin-transform-class-properties "^6.24.1" + babel-plugin-transform-decorators "^6.24.1" + babel-preset-stage-3 "^6.24.1" + +babel-preset-stage-3@^6.24.1: + version "6.24.1" + resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz#836ada0a9e7a7fa37cb138fb9326f87934a48395" + dependencies: + babel-plugin-syntax-trailing-function-commas "^6.22.0" + babel-plugin-transform-async-generator-functions "^6.24.1" + babel-plugin-transform-async-to-generator "^6.24.1" + babel-plugin-transform-exponentiation-operator "^6.24.1" + babel-plugin-transform-object-rest-spread "^6.22.0" + +babel-register@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" + dependencies: + babel-core "^6.26.0" + babel-runtime "^6.26.0" + core-js "^2.5.0" + home-or-tmp "^2.0.0" + lodash "^4.17.4" + mkdirp "^0.5.1" + source-map-support "^0.4.15" + +babel-runtime@^6.18.0, babel-runtime@^6.22.0, babel-runtime@^6.23.0, babel-runtime@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.11.0" + +babel-template@^6.24.1, babel-template@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" + dependencies: + babel-runtime "^6.26.0" + babel-traverse "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + lodash "^4.17.4" + +babel-traverse@^6.24.1, babel-traverse@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" + dependencies: + babel-code-frame "^6.26.0" + babel-messages "^6.23.0" + babel-runtime "^6.26.0" + babel-types "^6.26.0" + babylon "^6.18.0" + debug "^2.6.8" + globals "^9.18.0" + invariant "^2.2.2" + lodash "^4.17.4" + +babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: + version "6.26.0" + resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" + dependencies: + babel-runtime "^6.26.0" + esutils "^2.0.2" + lodash "^4.17.4" + to-fast-properties "^1.0.3" + +babylon@^6.18.0: + version "6.18.0" + resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" + +bail@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.3.tgz#63cfb9ddbac829b02a3128cd53224be78e6c21a3" + +balanced-match@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a" + +balanced-match@^0.4.2: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + +balanced-match@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" + +base64-js@^1.0.2: + version "1.3.0" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3" + +base@^0.11.1: + version "0.11.2" + resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" + dependencies: + cache-base "^1.0.1" + class-utils "^0.3.5" + component-emitter "^1.2.1" + define-property "^1.0.0" + isobject "^3.0.1" + mixin-deep "^1.2.0" + pascalcase "^0.1.1" + +big.js@^3.1.3: + version "3.2.0" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" + +binary-extensions@^1.0.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" + +bluebird@^3.5.1: + version "3.5.1" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" + +bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: + version "4.11.8" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" + +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +braces@^2.3.0, braces@^2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" + dependencies: + arr-flatten "^1.1.0" + array-unique "^0.3.2" + extend-shallow "^2.0.1" + fill-range "^4.0.0" + isobject "^3.0.1" + repeat-element "^1.1.2" + snapdragon "^0.8.1" + snapdragon-node "^2.0.1" + split-string "^3.0.2" + to-regex "^3.0.1" + +brorand@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" + +browserify-aes@^1.0.0, browserify-aes@^1.0.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" + dependencies: + buffer-xor "^1.0.3" + cipher-base "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.3" + inherits "^2.0.1" + safe-buffer "^5.0.1" + +browserify-cipher@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" + dependencies: + browserify-aes "^1.0.4" + browserify-des "^1.0.0" + evp_bytestokey "^1.0.0" + +browserify-des@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" + dependencies: + cipher-base "^1.0.1" + des.js "^1.0.0" + inherits "^2.0.1" + safe-buffer "^5.1.2" + +browserify-rsa@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" + dependencies: + bn.js "^4.1.0" + randombytes "^2.0.1" + +browserify-sign@^4.0.0: + version "4.0.4" + resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" + dependencies: + bn.js "^4.1.1" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.2" + elliptic "^6.0.0" + inherits "^2.0.1" + parse-asn1 "^5.0.0" + +browserify-zlib@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" + dependencies: + pako "~1.0.5" + +browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: + version "1.7.7" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" + dependencies: + caniuse-db "^1.0.30000639" + electron-to-chromium "^1.2.7" + +browserslist@^2.0.0, browserslist@^2.11.3: + version "2.11.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.11.3.tgz#fe36167aed1bbcde4827ebfe71347a2cc70b99b2" + dependencies: + caniuse-lite "^1.0.30000792" + electron-to-chromium "^1.3.30" + +browserslist@^3.2.6: + version "3.2.8" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-3.2.8.tgz#b0005361d6471f0f5952797a76fc985f1f978fc6" + dependencies: + caniuse-lite "^1.0.30000844" + electron-to-chromium "^1.3.47" + +browserslist@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.0.1.tgz#61c05ce2a5843c7d96166408bc23d58b5416e818" + dependencies: + caniuse-lite "^1.0.30000865" + electron-to-chromium "^1.3.52" + node-releases "^1.0.0-alpha.10" + +buffer-from@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" + +buffer-xor@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" + +buffer@^4.3.0: + version "4.9.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + +builtin-modules@^1.0.0, builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + +builtin-status-codes@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" + +cacache@^10.0.4: + version "10.0.4" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-10.0.4.tgz#6452367999eff9d4188aefd9a14e9d7c6a263460" + dependencies: + bluebird "^3.5.1" + chownr "^1.0.1" + glob "^7.1.2" + graceful-fs "^4.1.11" + lru-cache "^4.1.1" + mississippi "^2.0.0" + mkdirp "^0.5.1" + move-concurrently "^1.0.1" + promise-inflight "^1.0.1" + rimraf "^2.6.2" + ssri "^5.2.4" + unique-filename "^1.1.0" + y18n "^4.0.0" + +cache-base@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" + dependencies: + collection-visit "^1.0.0" + component-emitter "^1.2.1" + get-value "^2.0.6" + has-value "^1.0.0" + isobject "^3.0.1" + set-value "^2.0.0" + to-object-path "^0.3.0" + union-value "^1.0.0" + unset-value "^1.0.0" + +call-me-maybe@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b" + +caller-path@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" + dependencies: + callsites "^0.2.0" + +callsites@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" + +camelcase-keys@^4.0.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-4.2.0.tgz#a2aa5fb1af688758259c32c141426d78923b9b77" + dependencies: + camelcase "^4.1.0" + map-obj "^2.0.0" + quick-lru "^1.0.0" + +camelcase@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" + +caniuse-api@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" + dependencies: + browserslist "^1.3.6" + caniuse-db "^1.0.30000529" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-api@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-2.0.0.tgz#b1ddb5a5966b16f48dc4998444d4bbc6c7d9d834" + dependencies: + browserslist "^2.0.0" + caniuse-lite "^1.0.0" + lodash.memoize "^4.1.2" + lodash.uniq "^4.5.0" + +caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: + version "1.0.30000874" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000874.tgz#49edc0262efdc6c49d4d962bb16d1f0c790fa44e" + +caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000792, caniuse-lite@^1.0.30000805, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30000865, caniuse-lite@^1.0.30000872: + version "1.0.30000874" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000874.tgz#a641b1f1c420d58d9b132920ef6ba87bbdcd2223" + +ccount@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/ccount/-/ccount-1.0.3.tgz#f1cec43f332e2ea5a569fd46f9f5bde4e6102aff" + +chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e" + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +character-entities-html4@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-1.1.2.tgz#c44fdde3ce66b52e8d321d6c1bf46101f0150610" + +character-entities-legacy@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz#7c6defb81648498222c9855309953d05f4d63a9c" + +character-entities@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.2.tgz#58c8f371c0774ef0ba9b2aca5f00d8f100e6e363" + +character-reference-invalid@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz#21e421ad3d84055952dab4a43a04e73cd425d3ed" + +chardet@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" + +chardet@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.5.0.tgz#fe3ac73c00c3d865ffcc02a0682e2c20b6a06029" + +cheerio@^0.19.0: + version "0.19.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.19.0.tgz#772e7015f2ee29965096d71ea4175b75ab354925" + dependencies: + css-select "~1.0.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "~3.8.1" + lodash "^3.2.0" + +chokidar@^2.0.2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.4.tgz#356ff4e2b0e8e43e322d18a372460bbcf3accd26" + dependencies: + anymatch "^2.0.0" + async-each "^1.0.0" + braces "^2.3.0" + glob-parent "^3.1.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^4.0.0" + lodash.debounce "^4.0.8" + normalize-path "^2.1.1" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + upath "^1.0.5" + optionalDependencies: + fsevents "^1.2.2" + +chownr@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" + +chrome-trace-event@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.0.tgz#45a91bd2c20c9411f0963b5aaeb9a1b95e09cc48" + dependencies: + tslib "^1.9.0" + +cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +circular-json@^0.3.1: + version "0.3.3" + resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" + +clap@^1.0.9: + version "1.2.3" + resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz#4f36745b32008492557f46412d66d50cb99bce51" + dependencies: + chalk "^1.1.3" + +class-utils@^0.3.5: + version "0.3.6" + resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" + dependencies: + arr-union "^3.1.0" + define-property "^0.2.5" + isobject "^3.0.0" + static-extend "^0.1.1" + +cli-cursor@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" + dependencies: + restore-cursor "^2.0.0" + +cli-width@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" + +cliui@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.1.0.tgz#348422dbe82d800b3022eef4f6ac10bf2e4d1b49" + dependencies: + string-width "^2.1.1" + strip-ansi "^4.0.0" + wrap-ansi "^2.0.0" + +clone-regexp@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/clone-regexp/-/clone-regexp-1.0.1.tgz#051805cd33173375d82118fc0918606da39fd60f" + dependencies: + is-regexp "^1.0.0" + is-supported-regexp-flag "^1.0.0" + +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +coa@~1.0.1: + version "1.0.4" + resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" + dependencies: + q "^1.1.2" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +collapse-white-space@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.4.tgz#ce05cf49e54c3277ae573036a26851ba430a0091" + +collection-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" + dependencies: + map-visit "^1.0.0" + object-visit "^1.0.0" + +color-convert@^1.3.0, color-convert@^1.8.2, color-convert@^1.9.0, color-convert@^1.9.1: + version "1.9.2" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.2.tgz#49881b8fba67df12a96bdf3f56c0aab9e7913147" + dependencies: + color-name "1.1.1" + +color-name@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689" + +color-name@^1.0.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + +color-string@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" + dependencies: + color-name "^1.0.0" + +color-string@^1.4.0, color-string@^1.5.2: + version "1.5.3" + resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^0.11.0: + version "0.11.4" + resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" + dependencies: + clone "^1.0.2" + color-convert "^1.3.0" + color-string "^0.3.0" + +color@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/color/-/color-1.0.3.tgz#e48e832d85f14ef694fb468811c2d5cfe729b55d" + dependencies: + color-convert "^1.8.2" + color-string "^1.4.0" + +color@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color/-/color-2.0.1.tgz#e4ed78a3c4603d0891eba5430b04b86314f4c839" + dependencies: + color-convert "^1.9.1" + color-string "^1.5.2" + +colormin@^1.0.5: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" + dependencies: + color "^0.11.0" + css-color-names "0.0.4" + has "^1.0.1" + +colors@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" + +commander@^2.12.1, commander@^2.8.1: + version "2.17.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.0.tgz#9d07b25e2a6f198b76d8b756a0e8a9604a6a1a60" + +commander@~2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.13.0.tgz#6964bca67685df7c1f1430c584f07d7597885b9c" + +commondir@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" + +component-emitter@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +concat-stream@^1.5.0, concat-stream@^1.6.0: + version "1.6.2" + resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" + dependencies: + buffer-from "^1.0.0" + inherits "^2.0.3" + readable-stream "^2.2.2" + typedarray "^0.0.6" + +console-browserify@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" + dependencies: + date-now "^0.1.4" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +constants-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" + +convert-source-map@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" + +copy-concurrently@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" + dependencies: + aproba "^1.1.1" + fs-write-stream-atomic "^1.0.8" + iferr "^0.1.5" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.0" + +copy-descriptor@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" + +core-js@^2.4.0, core-js@^2.5.0: + version "2.5.7" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +cosmiconfig@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-4.0.0.tgz#760391549580bbd2df1e562bc177b13c290972dc" + dependencies: + is-directory "^0.3.1" + js-yaml "^3.9.0" + parse-json "^4.0.0" + require-from-string "^2.0.1" + +cosmiconfig@^5.0.0: + version "5.0.5" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.0.5.tgz#a809e3c2306891ce17ab70359dc8bdf661fe2cd0" + dependencies: + is-directory "^0.3.1" + js-yaml "^3.9.0" + parse-json "^4.0.0" + +create-ecdh@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.3.tgz#c9111b6f33045c4697f144787f9254cdc77c45ff" + dependencies: + bn.js "^4.1.0" + elliptic "^6.0.0" + +create-hash@^1.1.0, create-hash@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" + dependencies: + cipher-base "^1.0.1" + inherits "^2.0.1" + md5.js "^1.3.4" + ripemd160 "^2.0.1" + sha.js "^2.4.0" + +create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: + version "1.1.7" + resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" + dependencies: + cipher-base "^1.0.3" + create-hash "^1.1.0" + inherits "^2.0.1" + ripemd160 "^2.0.0" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +cross-spawn@^5.0.1, cross-spawn@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + +cross-spawn@^6.0.5: + version "6.0.5" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + +crypto-browserify@^3.11.0: + version "3.12.0" + resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" + dependencies: + browserify-cipher "^1.0.0" + browserify-sign "^4.0.0" + create-ecdh "^4.0.0" + create-hash "^1.1.0" + create-hmac "^1.1.0" + diffie-hellman "^5.0.0" + inherits "^2.0.1" + pbkdf2 "^3.0.3" + public-encrypt "^4.0.0" + randombytes "^2.0.0" + randomfill "^1.0.3" + +css-color-function@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/css-color-function/-/css-color-function-1.3.3.tgz#8ed24c2c0205073339fafa004bc8c141fccb282e" + dependencies: + balanced-match "0.1.0" + color "^0.11.0" + debug "^3.1.0" + rgb "~0.1.0" + +css-color-names@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" + +css-loader@^0.28.11: + version "0.28.11" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.11.tgz#c3f9864a700be2711bb5a2462b2389b1a392dab7" + dependencies: + babel-code-frame "^6.26.0" + css-selector-tokenizer "^0.7.0" + cssnano "^3.10.0" + icss-utils "^2.1.0" + loader-utils "^1.0.2" + lodash.camelcase "^4.3.0" + object-assign "^4.1.1" + postcss "^5.0.6" + postcss-modules-extract-imports "^1.2.0" + postcss-modules-local-by-default "^1.2.0" + postcss-modules-scope "^1.1.0" + postcss-modules-values "^1.3.0" + postcss-value-parser "^3.3.0" + source-list-map "^2.0.0" + +css-select@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.0.0.tgz#b1121ca51848dd264e2244d058cee254deeb44b0" + dependencies: + boolbase "~1.0.0" + css-what "1.0" + domutils "1.4" + nth-check "~1.0.0" + +css-selector-tokenizer@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86" + dependencies: + cssesc "^0.1.0" + fastparse "^1.1.1" + regexpu-core "^1.0.0" + +css-unit-converter@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/css-unit-converter/-/css-unit-converter-1.1.1.tgz#d9b9281adcfd8ced935bdbaba83786897f64e996" + +css-what@1.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-1.0.0.tgz#d7cc2df45180666f99d2b14462639469e00f736c" + +cssesc@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" + +cssnano@^3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" + dependencies: + autoprefixer "^6.3.1" + decamelize "^1.1.2" + defined "^1.0.0" + has "^1.0.1" + object-assign "^4.0.1" + postcss "^5.0.14" + postcss-calc "^5.2.0" + postcss-colormin "^2.1.8" + postcss-convert-values "^2.3.4" + postcss-discard-comments "^2.0.4" + postcss-discard-duplicates "^2.0.1" + postcss-discard-empty "^2.0.1" + postcss-discard-overridden "^0.1.1" + postcss-discard-unused "^2.2.1" + postcss-filter-plugins "^2.0.0" + postcss-merge-idents "^2.1.5" + postcss-merge-longhand "^2.0.1" + postcss-merge-rules "^2.0.3" + postcss-minify-font-values "^1.0.2" + postcss-minify-gradients "^1.0.1" + postcss-minify-params "^1.0.4" + postcss-minify-selectors "^2.0.4" + postcss-normalize-charset "^1.1.0" + postcss-normalize-url "^3.0.7" + postcss-ordered-values "^2.1.0" + postcss-reduce-idents "^2.2.2" + postcss-reduce-initial "^1.0.0" + postcss-reduce-transforms "^1.0.3" + postcss-svgo "^2.1.1" + postcss-unique-selectors "^2.0.2" + postcss-value-parser "^3.2.3" + postcss-zindex "^2.0.1" + +csso@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85" + dependencies: + clap "^1.0.9" + source-map "^0.5.3" + +currently-unhandled@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" + dependencies: + array-find-index "^1.0.1" + +cyclist@~0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" + +date-now@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" + +debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + dependencies: + ms "2.0.0" + +debug@^3.0.0, debug@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" + dependencies: + ms "2.0.0" + +decamelize-keys@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +decamelize@^1.1.0, decamelize@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +decamelize@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-2.0.0.tgz#656d7bbc8094c4c788ea53c5840908c9c7d063c7" + dependencies: + xregexp "4.0.0" + +decode-uri-component@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" + +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +define-property@^0.2.5: + version "0.2.5" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" + dependencies: + is-descriptor "^0.1.0" + +define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" + dependencies: + is-descriptor "^1.0.0" + +define-property@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" + dependencies: + is-descriptor "^1.0.2" + isobject "^3.0.1" + +defined@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" + +del@^2.0.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" + dependencies: + globby "^5.0.0" + is-path-cwd "^1.0.0" + is-path-in-cwd "^1.0.0" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + rimraf "^2.2.8" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +des.js@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" + dependencies: + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + +detect-indent@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" + dependencies: + repeating "^2.0.0" + +detect-libc@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" + +diff@^3.2.0: + version "3.5.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" + +diffie-hellman@^5.0.0: + version "5.0.3" + resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" + dependencies: + bn.js "^4.1.0" + miller-rabin "^4.0.0" + randombytes "^2.0.0" + +dir-glob@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-2.0.0.tgz#0b205d2b6aef98238ca286598a8204d29d0a0034" + dependencies: + arrify "^1.0.1" + path-type "^3.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" + dependencies: + esutils "^2.0.2" + +dom-serializer@0, dom-serializer@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domain-browser@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" + +domelementtype@1, domelementtype@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@2.3: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" + dependencies: + domelementtype "1" + +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + dependencies: + domelementtype "1" + +domutils@1.4: + version "1.4.3" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.4.3.tgz#0865513796c6b306031850e175516baf80b72a6f" + dependencies: + domelementtype "1" + +domutils@1.5: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + dependencies: + dom-serializer "0" + domelementtype "1" + +dot-prop@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57" + dependencies: + is-obj "^1.0.0" + +duplexify@^3.4.2, duplexify@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.6.0.tgz#592903f5d80b38d037220541264d69a198fb3410" + dependencies: + end-of-stream "^1.0.0" + inherits "^2.0.1" + readable-stream "^2.0.0" + stream-shift "^1.0.0" + +electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30, electron-to-chromium@^1.3.47, electron-to-chromium@^1.3.52: + version "1.3.55" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.55.tgz#f150e10b20b77d9d41afcca312efe0c3b1a7fdce" + +elliptic@^6.0.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" + dependencies: + bn.js "^4.4.0" + brorand "^1.0.1" + hash.js "^1.0.0" + hmac-drbg "^1.0.0" + inherits "^2.0.1" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.0" + +emojis-list@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" + +end-of-stream@^1.0.0, end-of-stream@^1.1.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" + dependencies: + once "^1.4.0" + +enhanced-resolve@^4.0.0, enhanced-resolve@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.1.0.tgz#41c7e0bfdfe74ac1ffe1e57ad6a5c6c9f3742a7f" + dependencies: + graceful-fs "^4.1.2" + memory-fs "^0.4.0" + tapable "^1.0.0" + +entities@1.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.0.0.tgz#b2987aa3821347fcde642b24fdfc9e4fb712bf26" + +entities@^1.1.1, entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + +errno@^0.1.3, errno@~0.1.7: + version "0.1.7" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + dependencies: + prr "~1.0.1" + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" + dependencies: + is-arrayish "^0.2.1" + +es6-promise@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-2.3.0.tgz#96edb9f2fdb01995822b263dd8aadab6748181bc" + +escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +eslint-loader@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.1.0.tgz#61334c548aeb0b8e20ec3a552fb7a88c47261c6a" + dependencies: + loader-fs-cache "^1.0.0" + loader-utils "^1.0.2" + object-assign "^4.0.1" + object-hash "^1.1.4" + rimraf "^2.6.1" + +eslint-scope@^3.7.1: + version "3.7.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-scope@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.0.tgz#50bf3071e9338bcdc43331794a0cb533f0136172" + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-visitor-keys@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" + +eslint@^4.19.1: + version "4.19.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" + dependencies: + ajv "^5.3.0" + babel-code-frame "^6.22.0" + chalk "^2.1.0" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.1.0" + doctrine "^2.1.0" + eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" + espree "^3.5.4" + esquery "^1.0.0" + esutils "^2.0.2" + file-entry-cache "^2.0.0" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.0.1" + ignore "^3.3.3" + imurmurhash "^0.1.4" + inquirer "^3.0.6" + is-resolvable "^1.0.0" + js-yaml "^3.9.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.3.0" + lodash "^4.17.4" + minimatch "^3.0.2" + mkdirp "^0.5.1" + natural-compare "^1.4.0" + optionator "^0.8.2" + path-is-inside "^1.0.2" + pluralize "^7.0.0" + progress "^2.0.0" + regexpp "^1.0.1" + require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" + strip-json-comments "~2.0.1" + table "4.0.2" + text-table "~0.2.0" + +espree@^3.5.4: + version "3.5.4" + resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" + dependencies: + acorn "^5.5.0" + acorn-jsx "^3.0.0" + +esprima@^2.6.0: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + +esquery@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" + dependencies: + estraverse "^4.0.0" + +esrecurse@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" + dependencies: + estraverse "^4.1.0" + +estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +events@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + +evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" + dependencies: + md5.js "^1.3.4" + safe-buffer "^5.1.1" + +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + +execall@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/execall/-/execall-1.0.0.tgz#73d0904e395b3cab0658b08d09ec25307f29bb73" + dependencies: + clone-regexp "^1.0.0" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-brackets@^2.1.4: + version "2.1.4" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" + dependencies: + debug "^2.3.3" + define-property "^0.2.5" + extend-shallow "^2.0.1" + posix-character-classes "^0.1.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +extend-shallow@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" + dependencies: + is-extendable "^0.1.0" + +extend-shallow@^3.0.0, extend-shallow@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" + dependencies: + assign-symbols "^1.0.0" + is-extendable "^1.0.1" + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + +external-editor@^2.0.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" + dependencies: + chardet "^0.4.0" + iconv-lite "^0.4.17" + tmp "^0.0.33" + +external-editor@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.0.tgz#dc35c48c6f98a30ca27a20e9687d7f3c77704bb6" + dependencies: + chardet "^0.5.0" + iconv-lite "^0.4.22" + tmp "^0.0.33" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extglob@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" + dependencies: + array-unique "^0.3.2" + define-property "^1.0.0" + expand-brackets "^2.1.4" + extend-shallow "^2.0.1" + fragment-cache "^0.2.1" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +extract-text-webpack-plugin@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extract-text-webpack-plugin/-/extract-text-webpack-plugin-3.0.2.tgz#5f043eaa02f9750a9258b78c0a6e0dc1408fb2f7" + dependencies: + async "^2.4.1" + loader-utils "^1.1.0" + schema-utils "^0.3.0" + webpack-sources "^1.0.1" + +fast-deep-equal@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" + +fast-deep-equal@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" + +fast-glob@^2.0.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.2.tgz#71723338ac9b4e0e2fff1d6748a2a13d5ed352bf" + dependencies: + "@mrmlnc/readdir-enhanced" "^2.2.1" + "@nodelib/fs.stat" "^1.0.1" + glob-parent "^3.1.0" + is-glob "^4.0.0" + merge2 "^1.2.1" + micromatch "^3.1.10" + +fast-json-stable-stringify@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +fastparse@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" + +figures@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" + dependencies: + flat-cache "^1.2.1" + object-assign "^4.0.1" + +filename-regex@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" + +fill-range@^2.1.0: + version "2.2.4" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^3.0.0" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +fill-range@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" + dependencies: + extend-shallow "^2.0.1" + is-number "^3.0.0" + repeat-string "^1.6.1" + to-regex-range "^2.1.0" + +find-cache-dir@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" + dependencies: + commondir "^1.0.1" + mkdirp "^0.5.1" + pkg-dir "^1.0.0" + +find-cache-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" + dependencies: + commondir "^1.0.1" + make-dir "^1.0.0" + pkg-dir "^2.0.0" + +find-up@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" + dependencies: + path-exists "^2.0.0" + pinkie-promise "^2.0.0" + +find-up@^2.0.0, find-up@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" + dependencies: + locate-path "^2.0.0" + +find-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" + dependencies: + locate-path "^3.0.0" + +flat-cache@^1.2.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" + dependencies: + circular-json "^0.3.1" + del "^2.0.2" + graceful-fs "^4.1.2" + write "^0.2.1" + +flatten@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" + +flush-write-stream@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.0.3.tgz#c5d586ef38af6097650b49bc41b55fabb19f35bd" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.4" + +for-in@^1.0.1, for-in@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +fragment-cache@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" + dependencies: + map-cache "^0.2.2" + +from2@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" + dependencies: + inherits "^2.0.1" + readable-stream "^2.0.0" + +fs-minipass@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.5.tgz#06c277218454ec288df77ada54a03b8702aacb9d" + dependencies: + minipass "^2.2.1" + +fs-write-stream-atomic@^1.0.8: + version "1.0.10" + resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" + dependencies: + graceful-fs "^4.1.2" + iferr "^0.1.5" + imurmurhash "^0.1.4" + readable-stream "1 || 2" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.2.2: + version "1.2.4" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.4.tgz#f41dcb1af2582af3692da36fc55cbd8e1041c426" + dependencies: + nan "^2.9.2" + node-pre-gyp "^0.10.0" + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + +gauge@~2.7.3: + version "2.7.4" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +get-caller-file@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" + +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + +get-value@^2.0.3, get-value@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob-parent@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" + dependencies: + is-glob "^3.1.0" + path-dirname "^1.0.0" + +glob-to-regexp@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" + +glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +global-modules-path@^2.1.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/global-modules-path/-/global-modules-path-2.3.0.tgz#b0e2bac6beac39745f7db5c59d26a36a0b94f7dc" + +globals@^11.0.1: + version "11.7.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.7.0.tgz#a583faa43055b1aca771914bf68258e2fc125673" + +globals@^9.18.0: + version "9.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" + +globby@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" + dependencies: + array-union "^1.0.1" + arrify "^1.0.0" + glob "^7.0.3" + object-assign "^4.0.1" + pify "^2.0.0" + pinkie-promise "^2.0.0" + +globby@^8.0.0: + version "8.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-8.0.1.tgz#b5ad48b8aa80b35b814fc1281ecc851f1d2b5b50" + dependencies: + array-union "^1.0.1" + dir-glob "^2.0.0" + fast-glob "^2.0.2" + glob "^7.1.2" + ignore "^3.3.5" + pify "^3.0.0" + slash "^1.0.0" + +globjoin@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/globjoin/-/globjoin-0.1.4.tgz#2f4494ac8919e3767c5cbb691e9f463324285d43" + +gonzales-pe@4.2.3, gonzales-pe@^4.0.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.2.3.tgz#41091703625433285e0aee3aa47829fc1fbeb6f2" + dependencies: + minimist "1.1.x" + +graceful-fs@^4.1.11, graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +has-value@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" + dependencies: + get-value "^2.0.3" + has-values "^0.1.4" + isobject "^2.0.0" + +has-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" + dependencies: + get-value "^2.0.6" + has-values "^1.0.0" + isobject "^3.0.0" + +has-values@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" + +has-values@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" + dependencies: + is-number "^3.0.0" + kind-of "^4.0.0" + +has@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + dependencies: + function-bind "^1.1.1" + +hash-base@^3.0.0: + version "3.0.4" + resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +hash.js@^1.0.0, hash.js@^1.0.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.5.tgz#e38ab4b85dfb1e0c40fe9265c0e9b54854c23812" + dependencies: + inherits "^2.0.3" + minimalistic-assert "^1.0.1" + +hmac-drbg@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" + dependencies: + hash.js "^1.0.3" + minimalistic-assert "^1.0.0" + minimalistic-crypto-utils "^1.0.1" + +home-or-tmp@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.1" + +hosted-git-info@^2.1.4: + version "2.7.1" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.7.1.tgz#97f236977bd6e125408930ff6de3eec6281ec047" + +html-comment-regex@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" + +html-janitor@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-janitor/-/html-janitor-2.0.2.tgz#3f4551d23d1be8554e273f9eada2b617c2b3ab70" + +html-tags@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" + +htmlparser2@^3.9.2: + version "3.9.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^2.0.2" + +htmlparser2@~3.8.1: + version "3.8.3" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.8.3.tgz#996c28b191516a8be86501a7d79757e5c70c1068" + dependencies: + domelementtype "1" + domhandler "2.3" + domutils "1.5" + entities "1.0" + readable-stream "1.1" + +https-browserify@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" + +iconv-lite@^0.4.17, iconv-lite@^0.4.22, iconv-lite@^0.4.4: + version "0.4.23" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.23.tgz#297871f63be507adcfbfca715d0cd0eed84e9a63" + dependencies: + safer-buffer ">= 2.1.2 < 3" + +icss-replace-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" + +icss-utils@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962" + dependencies: + postcss "^6.0.1" + +ieee754@^1.1.11, ieee754@^1.1.4: + version "1.1.12" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.12.tgz#50bf24e5b9c8bb98af4964c941cdb0918da7b60b" + +iferr@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" + +ignore-walk@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.1.tgz#a83e62e7d272ac0e3b551aaa82831a19b69f82f8" + dependencies: + minimatch "^3.0.4" + +ignore@^3.3.3, ignore@^3.3.5: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + +ignore@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.3.tgz#e2d58c9654d75b542529fa28d80ac95b29e4f467" + +import-cwd@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9" + dependencies: + import-from "^2.1.0" + +import-from@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/import-from/-/import-from-2.1.0.tgz#335db7f2a7affd53aaa471d4b8021dee36b7f3b1" + dependencies: + resolve-from "^3.0.0" + +import-lazy@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-3.1.0.tgz#891279202c8a2280fdbd6674dbd8da1a1dfc67cc" + +import-local@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" + dependencies: + pkg-dir "^2.0.0" + resolve-cwd "^2.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + +indent-string@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" + +indexes-of@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" + +indexof@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + +ini@~1.3.0: + version "1.3.5" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" + +inquirer@^3.0.6: + version "3.3.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.0.4" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx-lite "^4.0.8" + rx-lite-aggregates "^4.0.8" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + +inquirer@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.0.0.tgz#e8c20303ddc15bbfc2c12a6213710ccd9e1413d8" + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^3.0.0" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rxjs "^6.1.0" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + +interpret@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" + +invariant@^2.2.2: + version "2.2.4" + resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" + dependencies: + loose-envify "^1.0.0" + +invert-kv@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" + +is-absolute-url@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" + +is-accessor-descriptor@^0.1.6: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" + dependencies: + kind-of "^3.0.2" + +is-accessor-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" + dependencies: + kind-of "^6.0.0" + +is-alphabetical@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.2.tgz#1fa6e49213cb7885b75d15862fb3f3d96c884f41" + +is-alphanumeric@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-alphanumeric/-/is-alphanumeric-1.0.0.tgz#4a9cef71daf4c001c1d81d63d140cf53fd6889f4" + +is-alphanumerical@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz#1138e9ae5040158dc6ff76b820acd6b7a181fd40" + dependencies: + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" + +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.1.4, is-buffer@^1.1.5: + version "1.1.6" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" + +is-builtin-module@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" + dependencies: + builtin-modules "^1.0.0" + +is-data-descriptor@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" + dependencies: + kind-of "^3.0.2" + +is-data-descriptor@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" + dependencies: + kind-of "^6.0.0" + +is-decimal@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.2.tgz#894662d6a8709d307f3a276ca4339c8fa5dff0ff" + +is-descriptor@^0.1.0: + version "0.1.6" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" + dependencies: + is-accessor-descriptor "^0.1.6" + is-data-descriptor "^0.1.4" + kind-of "^5.0.0" + +is-descriptor@^1.0.0, is-descriptor@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" + dependencies: + is-accessor-descriptor "^1.0.0" + is-data-descriptor "^1.0.0" + kind-of "^6.0.2" + +is-directory@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" + +is-dotfile@^1.0.0: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.0, is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extendable@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + dependencies: + is-plain-object "^2.0.4" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-extglob@^2.1.0, is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + +is-finite@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-fullwidth-code-point@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-glob@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" + dependencies: + is-extglob "^2.1.0" + +is-glob@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" + dependencies: + is-extglob "^2.1.1" + +is-hexadecimal@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz#b6e710d7d07bb66b98cb8cece5c9b4921deeb835" + +is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-number@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" + dependencies: + kind-of "^3.0.2" + +is-number@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" + +is-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f" + +is-path-cwd@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" + +is-path-in-cwd@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" + dependencies: + is-path-inside "^1.0.0" + +is-path-inside@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" + dependencies: + path-is-inside "^1.0.1" + +is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" + +is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + dependencies: + isobject "^3.0.1" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-promise@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" + +is-regexp@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" + +is-resolvable@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" + +is-stream@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" + +is-supported-regexp-flag@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-supported-regexp-flag/-/is-supported-regexp-flag-1.0.1.tgz#21ee16518d2c1dd3edd3e9a0d57e50207ac364ca" + +is-svg@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9" + dependencies: + html-comment-regex "^1.1.0" + +is-whitespace-character@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz#ede53b4c6f6fb3874533751ec9280d01928d03ed" + +is-windows@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" + +is-word-character@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.2.tgz#46a5dac3f2a1840898b91e576cd40d493f3ae553" + +isarray@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" + +isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isnumeric@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/isnumeric/-/isnumeric-0.2.0.tgz#a2347ba360de19e33d0ffd590fddf7755cbf2e64" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isobject@^3.0.0, isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + +js-base64@^2.1.9: + version "2.4.8" + resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.8.tgz#57a9b130888f956834aa40c5b165ba59c758f033" + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + +js-tokens@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" + +js-yaml@^3.7.0, js-yaml@^3.9.0, js-yaml@^3.9.1: + version "3.12.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.12.0.tgz#eaed656ec8344f10f527c6bfa1b6e2244de167d1" + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +js-yaml@~3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" + dependencies: + argparse "^1.0.7" + esprima "^2.6.0" + +jsesc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" + +jsesc@~0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" + +json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" + +json-schema-traverse@^0.3.0: + version "0.3.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + +json5@^0.5.0, json5@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" + +kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" + dependencies: + is-buffer "^1.1.5" + +kind-of@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" + dependencies: + is-buffer "^1.1.5" + +kind-of@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" + +kind-of@^6.0.0, kind-of@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" + +known-css-properties@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/known-css-properties/-/known-css-properties-0.6.1.tgz#31b5123ad03d8d1a3f36bd4155459c981173478b" + +lcid@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" + dependencies: + invert-kv "^1.0.0" + +levn@^0.3.0, levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +load-json-file@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-4.0.0.tgz#2f5f45ab91e33216234fd53adab668eb4ec0993b" + dependencies: + graceful-fs "^4.1.2" + parse-json "^4.0.0" + pify "^3.0.0" + strip-bom "^3.0.0" + +loader-fs-cache@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz#56e0bf08bd9708b26a765b68509840c8dec9fdbc" + dependencies: + find-cache-dir "^0.1.1" + mkdirp "0.5.1" + +loader-runner@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" + +loader-utils@^1.0.2, loader-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" + dependencies: + big.js "^3.1.3" + emojis-list "^2.0.0" + json5 "^0.5.0" + +locate-path@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" + dependencies: + p-locate "^2.0.0" + path-exists "^3.0.0" + +locate-path@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" + dependencies: + p-locate "^3.0.0" + path-exists "^3.0.0" + +lodash._reinterpolate@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" + +lodash.debounce@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" + +lodash.memoize@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" + +lodash.template@^4.2.4: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.4.0.tgz#e73a0385c8355591746e020b99679c690e68fba0" + dependencies: + lodash._reinterpolate "~3.0.0" + lodash.templatesettings "^4.0.0" + +lodash.templatesettings@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.1.0.tgz#2b4d4e95ba440d915ff08bc899e4553666713316" + dependencies: + lodash._reinterpolate "~3.0.0" + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" + +lodash@^3.2.0: + version "3.10.1" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" + +lodash@^4.17.10, lodash@^4.17.4, lodash@^4.3.0: + version "4.17.10" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7" + +log-symbols@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-2.2.0.tgz#5740e1c5d6f0dfda4ad9323b5332107ef6b4c40a" + dependencies: + chalk "^2.0.1" + +long@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" + +long@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" + +longest-streak@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-2.0.2.tgz#2421b6ba939a443bb9ffebf596585a50b4c38e2e" + +loose-envify@^1.0.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +loud-rejection@^1.0.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" + dependencies: + currently-unhandled "^0.4.1" + signal-exit "^3.0.0" + +lru-cache@^4.0.1, lru-cache@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.3.tgz#a1175cf3496dfc8436c156c334b4955992bce69c" + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + +make-dir@^1.0.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.3.0.tgz#79c1033b80515bd6d24ec9933e860ca75ee27f0c" + dependencies: + pify "^3.0.0" + +mamacro@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" + +map-cache@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" + +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" + +map-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" + +map-visit@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" + dependencies: + object-visit "^1.0.0" + +markdown-escapes@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.2.tgz#e639cbde7b99c841c0bacc8a07982873b46d2122" + +markdown-table@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-1.1.2.tgz#c78db948fa879903a41bce522e3b96f801c63786" + +math-expression-evaluator@^1.2.14: + version "1.2.17" + resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" + +math-random@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac" + +mathml-tag-names@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.0.tgz#490b70e062ee24636536e3d9481e333733d00f2c" + +md5.js@^1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +mdast-util-compact@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/mdast-util-compact/-/mdast-util-compact-1.0.1.tgz#cdb5f84e2b6a2d3114df33bd05d9cb32e3c4083a" + dependencies: + unist-util-modify-children "^1.0.0" + unist-util-visit "^1.1.0" + +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + dependencies: + mimic-fn "^1.0.0" + +memory-fs@^0.4.0, memory-fs@~0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" + dependencies: + errno "^0.1.3" + readable-stream "^2.0.1" + +meow@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-5.0.0.tgz#dfc73d63a9afc714a5e371760eb5c88b91078aa4" + dependencies: + camelcase-keys "^4.0.0" + decamelize-keys "^1.0.0" + loud-rejection "^1.0.0" + minimist-options "^3.0.1" + normalize-package-data "^2.3.4" + read-pkg-up "^3.0.0" + redent "^2.0.0" + trim-newlines "^2.0.0" + yargs-parser "^10.0.0" + +merge2@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.2.2.tgz#03212e3da8d86c4d8523cebd6318193414f94e34" + +micromatch@^2.3.11: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +micromatch@^3.1.10, micromatch@^3.1.4, micromatch@^3.1.8: + version "3.1.10" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + braces "^2.3.1" + define-property "^2.0.2" + extend-shallow "^3.0.2" + extglob "^2.0.4" + fragment-cache "^0.2.1" + kind-of "^6.0.2" + nanomatch "^1.2.9" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.2" + +miller-rabin@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" + dependencies: + bn.js "^4.0.0" + brorand "^1.0.1" + +mimic-fn@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" + +minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + +minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" + +minimatch@^3.0.2, minimatch@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" + dependencies: + brace-expansion "^1.1.7" + +minimist-options@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + +minimist@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@1.1.x: + version "1.1.3" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8" + +minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +minipass@^2.2.1, minipass@^2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.3.3.tgz#a7dcc8b7b833f5d368759cce544dccb55f50f233" + dependencies: + safe-buffer "^5.1.2" + yallist "^3.0.0" + +minizlib@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.1.0.tgz#11e13658ce46bc3a70a267aac58359d1e0c29ceb" + dependencies: + minipass "^2.2.1" + +mississippi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-2.0.0.tgz#3442a508fafc28500486feea99409676e4ee5a6f" + dependencies: + concat-stream "^1.5.0" + duplexify "^3.4.2" + end-of-stream "^1.1.0" + flush-write-stream "^1.0.0" + from2 "^2.1.0" + parallel-transform "^1.1.0" + pump "^2.0.1" + pumpify "^1.3.3" + stream-each "^1.1.0" + through2 "^2.0.0" + +mixin-deep@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + dependencies: + for-in "^1.0.2" + is-extendable "^1.0.1" + +mkdirp@0.5.1, mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +move-concurrently@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" + dependencies: + aproba "^1.1.1" + copy-concurrently "^1.0.0" + fs-write-stream-atomic "^1.0.8" + mkdirp "^0.5.1" + rimraf "^2.5.4" + run-queue "^1.0.3" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + +nan@^2.9.2: + version "2.10.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" + +nanomatch@^1.2.9: + version "1.2.13" + resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" + dependencies: + arr-diff "^4.0.0" + array-unique "^0.3.2" + define-property "^2.0.2" + extend-shallow "^3.0.2" + fragment-cache "^0.2.1" + is-windows "^1.0.2" + kind-of "^6.0.2" + object.pick "^1.3.0" + regex-not "^1.0.0" + snapdragon "^0.8.1" + to-regex "^3.0.1" + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + +needle@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.2.1.tgz#b5e325bd3aae8c2678902fa296f729455d1d3a7d" + dependencies: + debug "^2.1.2" + iconv-lite "^0.4.4" + sax "^1.2.4" + +neo-async@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.1.tgz#acb909e327b1e87ec9ef15f41b8a269512ad41ee" + +nice-try@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4" + +node-libs-browser@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" + dependencies: + assert "^1.1.1" + browserify-zlib "^0.2.0" + buffer "^4.3.0" + console-browserify "^1.1.0" + constants-browserify "^1.0.0" + crypto-browserify "^3.11.0" + domain-browser "^1.1.1" + events "^1.0.0" + https-browserify "^1.0.0" + os-browserify "^0.3.0" + path-browserify "0.0.0" + process "^0.11.10" + punycode "^1.2.4" + querystring-es3 "^0.2.0" + readable-stream "^2.3.3" + stream-browserify "^2.0.1" + stream-http "^2.7.2" + string_decoder "^1.0.0" + timers-browserify "^2.0.4" + tty-browserify "0.0.0" + url "^0.11.0" + util "^0.10.3" + vm-browserify "0.0.4" + +node-pre-gyp@^0.10.0: + version "0.10.3" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.10.3.tgz#3070040716afdc778747b61b6887bf78880b80fc" + dependencies: + detect-libc "^1.0.2" + mkdirp "^0.5.1" + needle "^2.2.1" + nopt "^4.0.1" + npm-packlist "^1.1.6" + npmlog "^4.0.2" + rc "^1.2.7" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^4" + +node-releases@^1.0.0-alpha.10: + version "1.0.0-alpha.10" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.0.0-alpha.10.tgz#61c8d5f9b5b2e05d84eba941d05b6f5202f68a2a" + dependencies: + semver "^5.3.0" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" + dependencies: + hosted-git-info "^2.1.4" + is-builtin-module "^1.0.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-path@^2.0.1, normalize-path@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +normalize-range@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" + +normalize-selector@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/normalize-selector/-/normalize-selector-0.2.0.tgz#d0b145eb691189c63a78d201dc4fdb1293ef0c03" + +normalize-url@^1.4.0: + version "1.9.1" + resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" + dependencies: + object-assign "^4.0.1" + prepend-http "^1.0.0" + query-string "^4.1.0" + sort-keys "^1.0.0" + +npm-bundled@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.0.3.tgz#7e71703d973af3370a9591bafe3a63aca0be2308" + +npm-packlist@^1.1.6: + version "1.1.11" + resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.1.11.tgz#84e8c683cbe7867d34b1d357d893ce29e28a02de" + dependencies: + ignore-walk "^3.0.1" + npm-bundled "^1.0.1" + +npm-run-path@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" + dependencies: + path-key "^2.0.0" + +npmlog@^4.0.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.3" + set-blocking "~2.0.0" + +nth-check@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" + dependencies: + boolbase "~1.0.0" + +num2fraction@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object-copy@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" + dependencies: + copy-descriptor "^0.1.0" + define-property "^0.2.5" + kind-of "^3.0.3" + +object-hash@^1.1.4: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.0.tgz#76d9ba6ff113cf8efc0d996102851fe6723963e2" + +object-visit@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" + dependencies: + isobject "^3.0.0" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +object.pick@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" + dependencies: + isobject "^3.0.1" + +once@^1.3.0, once@^1.3.1, once@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +onecolor@^3.0.4: + version "3.0.5" + resolved "https://registry.yarnpkg.com/onecolor/-/onecolor-3.0.5.tgz#36eff32201379efdf1180fb445e51a8e2425f9f6" + +onetime@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" + dependencies: + mimic-fn "^1.0.0" + +optionator@^0.8.2: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +os-browserify@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-locale@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" + dependencies: + execa "^0.7.0" + lcid "^1.0.0" + mem "^1.1.0" + +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + +p-limit@^1.1.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" + dependencies: + p-try "^1.0.0" + +p-limit@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.0.0.tgz#e624ed54ee8c460a778b3c9f3670496ff8a57aec" + dependencies: + p-try "^2.0.0" + +p-locate@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" + dependencies: + p-limit "^1.1.0" + +p-locate@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" + dependencies: + p-limit "^2.0.0" + +p-try@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" + +p-try@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.0.0.tgz#85080bb87c64688fa47996fe8f7dfbe8211760b1" + +pako@~1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" + +parallel-transform@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.1.0.tgz#d410f065b05da23081fcd10f28854c29bda33b06" + dependencies: + cyclist "~0.2.2" + inherits "^2.0.3" + readable-stream "^2.1.5" + +parse-asn1@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.1.tgz#f6bf293818332bd0dab54efb16087724745e6ca8" + dependencies: + asn1.js "^4.0.0" + browserify-aes "^1.0.0" + create-hash "^1.1.0" + evp_bytestokey "^1.0.0" + pbkdf2 "^3.0.3" + +parse-entities@^1.0.2, parse-entities@^1.1.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.1.2.tgz#9eaf719b29dc3bd62246b4332009072e01527777" + dependencies: + character-entities "^1.0.0" + character-entities-legacy "^1.0.0" + character-reference-invalid "^1.0.0" + is-alphanumerical "^1.0.0" + is-decimal "^1.0.0" + is-hexadecimal "^1.0.0" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +parse-json@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" + dependencies: + error-ex "^1.3.1" + json-parse-better-errors "^1.0.1" + +pascalcase@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" + +path-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" + +path-dirname@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" + +path-exists@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" + dependencies: + pinkie-promise "^2.0.0" + +path-exists@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" + +path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-is-inside@^1.0.1, path-is-inside@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" + +path-key@^2.0.0, path-key@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +path-type@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" + dependencies: + pify "^3.0.0" + +path@^0.12.7: + version "0.12.7" + resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" + dependencies: + process "^0.11.1" + util "^0.10.3" + +pbkdf2@^3.0.3: + version "3.0.16" + resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.16.tgz#7404208ec6b01b62d85bf83853a8064f8d9c2a5c" + dependencies: + create-hash "^1.1.2" + create-hmac "^1.1.4" + ripemd160 "^2.0.1" + safe-buffer "^5.0.1" + sha.js "^2.4.8" + +pify@^2.0.0, pify@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" + +pify@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +pixrem@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/pixrem/-/pixrem-4.0.1.tgz#2da4a1de6ec4423c5fc3794e930b81d4490ec686" + dependencies: + browserslist "^2.0.0" + postcss "^6.0.0" + reduce-css-calc "^1.2.7" + +pkg-dir@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" + dependencies: + find-up "^1.0.0" + +pkg-dir@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" + dependencies: + find-up "^2.1.0" + +pleeease-filters@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pleeease-filters/-/pleeease-filters-4.0.0.tgz#6632b2fb05648d2758d865384fbced79e1ccaec7" + dependencies: + onecolor "^3.0.4" + postcss "^6.0.1" + +pluralize@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" + +posix-character-classes@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" + +postcss-apply@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/postcss-apply/-/postcss-apply-0.10.0.tgz#50cc982b7a58a335f9be96a277fc2d8792e760dc" + dependencies: + babel-runtime "^6.26.0" + balanced-match "^1.0.0" + postcss "^6.0.21" + +postcss-apply@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/postcss-apply/-/postcss-apply-0.8.0.tgz#14e544bbb5cb6f1c1e048857965d79ae066b1343" + dependencies: + babel-runtime "^6.23.0" + balanced-match "^0.4.2" + postcss "^6.0.0" + +postcss-attribute-case-insensitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-2.0.0.tgz#94dc422c8f90997f16bd33a3654bbbec084963b4" + dependencies: + postcss "^6.0.0" + postcss-selector-parser "^2.2.3" + +postcss-calc@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" + dependencies: + postcss "^5.0.2" + postcss-message-helpers "^2.0.0" + reduce-css-calc "^1.2.6" + +postcss-calc@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-6.0.1.tgz#3d24171bbf6e7629d422a436ebfe6dd9511f4330" + dependencies: + css-unit-converter "^1.1.1" + postcss "^6.0.0" + postcss-selector-parser "^2.2.2" + reduce-css-calc "^2.0.0" + +postcss-color-function@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-color-function/-/postcss-color-function-4.0.1.tgz#402b3f2cebc3f6947e618fb6be3654fbecef6444" + dependencies: + css-color-function "~1.3.3" + postcss "^6.0.1" + postcss-message-helpers "^2.0.0" + postcss-value-parser "^3.3.0" + +postcss-color-gray@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/postcss-color-gray/-/postcss-color-gray-4.1.0.tgz#e5581ed57eaa826fb652ca11b1e2b7b136a9f9df" + dependencies: + color "^2.0.1" + postcss "^6.0.14" + postcss-message-helpers "^2.0.0" + reduce-function-call "^1.0.2" + +postcss-color-hex-alpha@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-hex-alpha/-/postcss-color-hex-alpha-3.0.0.tgz#1e53e6c8acb237955e8fd08b7ecdb1b8b8309f95" + dependencies: + color "^1.0.3" + postcss "^6.0.1" + postcss-message-helpers "^2.0.0" + +postcss-color-hsl@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-hsl/-/postcss-color-hsl-2.0.0.tgz#12703666fa310430e3f30a454dac1386317d5844" + dependencies: + postcss "^6.0.1" + postcss-value-parser "^3.3.0" + units-css "^0.4.0" + +postcss-color-hwb@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-hwb/-/postcss-color-hwb-3.0.0.tgz#3402b19ef4d8497540c1fb5072be9863ca95571e" + dependencies: + color "^1.0.3" + postcss "^6.0.1" + postcss-message-helpers "^2.0.0" + reduce-function-call "^1.0.2" + +postcss-color-mod-function@^2.4.3: + version "2.4.3" + resolved "https://registry.yarnpkg.com/postcss-color-mod-function/-/postcss-color-mod-function-2.4.3.tgz#14a97f5b17a5f19396e9dea7ffcb5be732592baf" + dependencies: + "@csstools/convert-colors" "^1.4.0" + postcss "^6.0.23" + postcss-values-parser "^1.5.0" + +postcss-color-rebeccapurple@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-3.1.0.tgz#ce1269ecc2d0d8bf92aab44bd884e633124c33ec" + dependencies: + postcss "^6.0.22" + postcss-values-parser "^1.5.0" + +postcss-color-rgb@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-rgb/-/postcss-color-rgb-2.0.0.tgz#14539c8a7131494b482e0dd1cc265ff6514b5263" + dependencies: + postcss "^6.0.1" + postcss-value-parser "^3.3.0" + +postcss-color-rgba-fallback@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-color-rgba-fallback/-/postcss-color-rgba-fallback-3.0.0.tgz#37d5c9353a07a09270912a82606bb42a0d702c04" + dependencies: + postcss "^6.0.6" + postcss-value-parser "^3.3.0" + rgb-hex "^2.1.0" + +postcss-colormin@^2.1.8: + version "2.2.2" + resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b" + dependencies: + colormin "^1.0.5" + postcss "^5.0.13" + postcss-value-parser "^3.2.3" + +postcss-convert-values@^2.3.4: + version "2.6.1" + resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d" + dependencies: + postcss "^5.0.11" + postcss-value-parser "^3.1.2" + +postcss-cssnext@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/postcss-cssnext/-/postcss-cssnext-3.1.0.tgz#927dc29341a938254cde38ea60a923b9dfedead9" + dependencies: + autoprefixer "^7.1.1" + caniuse-api "^2.0.0" + chalk "^2.0.1" + pixrem "^4.0.0" + pleeease-filters "^4.0.0" + postcss "^6.0.5" + postcss-apply "^0.8.0" + postcss-attribute-case-insensitive "^2.0.0" + postcss-calc "^6.0.0" + postcss-color-function "^4.0.0" + postcss-color-gray "^4.0.0" + postcss-color-hex-alpha "^3.0.0" + postcss-color-hsl "^2.0.0" + postcss-color-hwb "^3.0.0" + postcss-color-rebeccapurple "^3.0.0" + postcss-color-rgb "^2.0.0" + postcss-color-rgba-fallback "^3.0.0" + postcss-custom-media "^6.0.0" + postcss-custom-properties "^6.1.0" + postcss-custom-selectors "^4.0.1" + postcss-font-family-system-ui "^3.0.0" + postcss-font-variant "^3.0.0" + postcss-image-set-polyfill "^0.3.5" + postcss-initial "^2.0.0" + postcss-media-minmax "^3.0.0" + postcss-nesting "^4.0.1" + postcss-pseudo-class-any-link "^4.0.0" + postcss-pseudoelements "^5.0.0" + postcss-replace-overflow-wrap "^2.0.0" + postcss-selector-matches "^3.0.1" + postcss-selector-not "^3.0.1" + +postcss-custom-media@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-custom-media/-/postcss-custom-media-6.0.0.tgz#be532784110ecb295044fb5395a18006eb21a737" + dependencies: + postcss "^6.0.1" + +postcss-custom-properties@^6.1.0: + version "6.3.1" + resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-6.3.1.tgz#5c52abde313d7ec9368c4abf67d27a656cba8b39" + dependencies: + balanced-match "^1.0.0" + postcss "^6.0.18" + +postcss-custom-properties@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/postcss-custom-properties/-/postcss-custom-properties-7.0.0.tgz#24dc4fbe6d6ed550ea4fd3b11204660e9ffa3b33" + dependencies: + balanced-match "^1.0.0" + postcss "^6.0.18" + +postcss-custom-selectors@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-custom-selectors/-/postcss-custom-selectors-4.0.1.tgz#781382f94c52e727ef5ca4776ea2adf49a611382" + dependencies: + postcss "^6.0.1" + postcss-selector-matches "^3.0.0" + +postcss-discard-comments@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" + dependencies: + postcss "^5.0.14" + +postcss-discard-duplicates@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932" + dependencies: + postcss "^5.0.4" + +postcss-discard-empty@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5" + dependencies: + postcss "^5.0.14" + +postcss-discard-overridden@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58" + dependencies: + postcss "^5.0.16" + +postcss-discard-unused@^2.2.1: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433" + dependencies: + postcss "^5.0.14" + uniqs "^2.0.0" + +postcss-filter-plugins@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.3.tgz#82245fdf82337041645e477114d8e593aa18b8ec" + dependencies: + postcss "^5.0.4" + +postcss-font-family-system-ui@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-font-family-system-ui/-/postcss-font-family-system-ui-3.0.0.tgz#675fe7a9e029669f05f8dba2e44c2225ede80623" + dependencies: + postcss "^6.0" + +postcss-font-variant@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-font-variant/-/postcss-font-variant-3.0.0.tgz#08ccc88f6050ba82ed8ef2cc76c0c6a6b41f183e" + dependencies: + postcss "^6.0.1" + +postcss-html@^0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/postcss-html/-/postcss-html-0.31.0.tgz#ea6ae2e95df60a03032e9ab5aba72143d8ca0325" + dependencies: + htmlparser2 "^3.9.2" + +postcss-image-set-polyfill@^0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/postcss-image-set-polyfill/-/postcss-image-set-polyfill-0.3.5.tgz#0f193413700cf1f82bd39066ef016d65a4a18181" + dependencies: + postcss "^6.0.1" + postcss-media-query-parser "^0.2.3" + +postcss-initial@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-initial/-/postcss-initial-2.0.0.tgz#72715f7336e0bb79351d99ee65c4a253a8441ba4" + dependencies: + lodash.template "^4.2.4" + postcss "^6.0.1" + +postcss-less@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-less/-/postcss-less-2.0.0.tgz#5d190b8e057ca446d60fe2e2587ad791c9029fb8" + dependencies: + postcss "^5.2.16" + +postcss-load-config@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-2.0.0.tgz#f1312ddbf5912cd747177083c5ef7a19d62ee484" + dependencies: + cosmiconfig "^4.0.0" + import-cwd "^2.0.0" + +postcss-loader@^2.1.6: + version "2.1.6" + resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-2.1.6.tgz#1d7dd7b17c6ba234b9bed5af13e0bea40a42d740" + dependencies: + loader-utils "^1.1.0" + postcss "^6.0.0" + postcss-load-config "^2.0.0" + schema-utils "^0.4.0" + +postcss-markdown@^0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/postcss-markdown/-/postcss-markdown-0.31.0.tgz#e4c699ad34b14a29ad5d47132bb1b3100b60ef75" + dependencies: + remark "^9.0.0" + unist-util-find-all-after "^1.0.2" + +postcss-media-minmax@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-media-minmax/-/postcss-media-minmax-3.0.0.tgz#675256037a43ef40bc4f0760bfd06d4dc69d48d2" + dependencies: + postcss "^6.0.1" + +postcss-media-query-parser@^0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/postcss-media-query-parser/-/postcss-media-query-parser-0.2.3.tgz#27b39c6f4d94f81b1a73b8f76351c609e5cef244" + +postcss-merge-idents@^2.1.5: + version "2.1.7" + resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270" + dependencies: + has "^1.0.1" + postcss "^5.0.10" + postcss-value-parser "^3.1.1" + +postcss-merge-longhand@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658" + dependencies: + postcss "^5.0.4" + +postcss-merge-rules@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721" + dependencies: + browserslist "^1.5.2" + caniuse-api "^1.5.2" + postcss "^5.0.4" + postcss-selector-parser "^2.2.2" + vendors "^1.0.0" + +postcss-message-helpers@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e" + +postcss-minify-font-values@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69" + dependencies: + object-assign "^4.0.1" + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-minify-gradients@^1.0.1: + version "1.0.5" + resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1" + dependencies: + postcss "^5.0.12" + postcss-value-parser "^3.3.0" + +postcss-minify-params@^1.0.4: + version "1.2.2" + resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.2" + postcss-value-parser "^3.0.2" + uniqs "^2.0.0" + +postcss-minify-selectors@^2.0.4: + version "2.1.1" + resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf" + dependencies: + alphanum-sort "^1.0.2" + has "^1.0.1" + postcss "^5.0.14" + postcss-selector-parser "^2.0.0" + +postcss-modules-extract-imports@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85" + dependencies: + postcss "^6.0.1" + +postcss-modules-local-by-default@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + +postcss-modules-scope@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" + dependencies: + css-selector-tokenizer "^0.7.0" + postcss "^6.0.1" + +postcss-modules-values@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" + dependencies: + icss-replace-symbols "^1.1.0" + postcss "^6.0.1" + +postcss-nested-ancestors@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-nested-ancestors/-/postcss-nested-ancestors-2.0.0.tgz#957ef27fb9e37cb082786d95b5e310d4b47470fe" + dependencies: + escape-string-regexp "^1.0.5" + postcss "^6.0.0" + postcss-resolve-nested-selector "^0.1.1" + +postcss-nested@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-3.0.0.tgz#cde40bd07a078565f3df72e2dc2665871c724852" + dependencies: + postcss "^6.0.14" + postcss-selector-parser "^3.1.1" + +postcss-nesting@^4.0.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-4.2.1.tgz#0483bce338b3f0828ced90ff530b29b98b00300d" + dependencies: + postcss "^6.0.11" + +postcss-nesting@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/postcss-nesting/-/postcss-nesting-6.0.0.tgz#4c45276a065765ec063efe1e4daf75c131518991" + dependencies: + postcss "^6.0.22" + +postcss-normalize-charset@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1" + dependencies: + postcss "^5.0.5" + +postcss-normalize-url@^3.0.7: + version "3.0.8" + resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222" + dependencies: + is-absolute-url "^2.0.0" + normalize-url "^1.4.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + +postcss-ordered-values@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.1" + +postcss-pseudo-class-any-link@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-4.0.0.tgz#9152a0613d3450720513e8892854bae42d0ee68e" + dependencies: + postcss "^6.0.1" + postcss-selector-parser "^2.2.3" + +postcss-pseudoelements@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-pseudoelements/-/postcss-pseudoelements-5.0.0.tgz#eef194e8d524645ca520a949e95e518e812402cb" + dependencies: + postcss "^6.0.0" + +postcss-reduce-idents@^2.2.2: + version "2.4.0" + resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3" + dependencies: + postcss "^5.0.4" + postcss-value-parser "^3.0.2" + +postcss-reduce-initial@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea" + dependencies: + postcss "^5.0.4" + +postcss-reduce-transforms@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1" + dependencies: + has "^1.0.1" + postcss "^5.0.8" + postcss-value-parser "^3.0.1" + +postcss-replace-overflow-wrap@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-2.0.0.tgz#794db6faa54f8db100854392a93af45768b4e25b" + dependencies: + postcss "^6.0.1" + +postcss-reporter@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/postcss-reporter/-/postcss-reporter-5.0.0.tgz#a14177fd1342829d291653f2786efd67110332c3" + dependencies: + chalk "^2.0.1" + lodash "^4.17.4" + log-symbols "^2.0.0" + postcss "^6.0.8" + +postcss-resolve-nested-selector@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/postcss-resolve-nested-selector/-/postcss-resolve-nested-selector-0.1.1.tgz#29ccbc7c37dedfac304e9fff0bf1596b3f6a0e4e" + +postcss-safe-parser@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/postcss-safe-parser/-/postcss-safe-parser-4.0.1.tgz#8756d9e4c36fdce2c72b091bbc8ca176ab1fcdea" + dependencies: + postcss "^7.0.0" + +postcss-sass@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/postcss-sass/-/postcss-sass-0.2.0.tgz#e55516441e9526ba4b380a730d3a02e9eaa78c7a" + dependencies: + gonzales-pe "^4.0.3" + postcss "^6.0.6" + +postcss-sass@^0.3.0: + version "0.3.2" + resolved "https://registry.yarnpkg.com/postcss-sass/-/postcss-sass-0.3.2.tgz#17f3074cecb28128b156f1a4407c6ad075d7e00c" + dependencies: + gonzales-pe "4.2.3" + postcss "6.0.22" + +postcss-scss@^1.0.2: + version "1.0.6" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-1.0.6.tgz#ab903f3bb20161bc177896462293a53d4bff5f7a" + dependencies: + postcss "^6.0.23" + +postcss-scss@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/postcss-scss/-/postcss-scss-2.0.0.tgz#248b0a28af77ea7b32b1011aba0f738bda27dea1" + dependencies: + postcss "^7.0.0" + +postcss-selector-matches@^3.0.0, postcss-selector-matches@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/postcss-selector-matches/-/postcss-selector-matches-3.0.1.tgz#e5634011e13950881861bbdd58c2d0111ffc96ab" + dependencies: + balanced-match "^0.4.2" + postcss "^6.0.1" + +postcss-selector-not@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-3.0.1.tgz#2e4db2f0965336c01e7cec7db6c60dff767335d9" + dependencies: + balanced-match "^0.4.2" + postcss "^6.0.1" + +postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2, postcss-selector-parser@^2.2.3: + version "2.2.3" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90" + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-selector-parser@^3.1.0, postcss-selector-parser@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.1.tgz#4f875f4afb0c96573d5cf4d74011aee250a7e865" + dependencies: + dot-prop "^4.1.1" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-smart-import@^0.7.6: + version "0.7.6" + resolved "https://registry.yarnpkg.com/postcss-smart-import/-/postcss-smart-import-0.7.6.tgz#259deb84aa28f138458218ecc0e9a84c61ada6a4" + dependencies: + babel-runtime "^6.26.0" + lodash "^4.17.4" + object-assign "^4.1.1" + postcss "^6.0.14" + postcss-sass "^0.2.0" + postcss-scss "^1.0.2" + postcss-value-parser "^3.3.0" + promise-each "^2.2.0" + read-cache "^1.0.0" + resolve "^1.5.0" + sugarss "^1.0.1" + +postcss-styled@^0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/postcss-styled/-/postcss-styled-0.31.0.tgz#ab532a2b3c469dfcca306a7623c4d4a98bb077d5" + +postcss-svgo@^2.1.1: + version "2.1.6" + resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" + dependencies: + is-svg "^2.0.0" + postcss "^5.0.14" + postcss-value-parser "^3.2.3" + svgo "^0.7.0" + +postcss-syntax@^0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/postcss-syntax/-/postcss-syntax-0.31.0.tgz#13d955c705d339595d10a19efa4a1bee82dfb78f" + +postcss-unique-selectors@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d" + dependencies: + alphanum-sort "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" + +postcss-values-parser@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/postcss-values-parser/-/postcss-values-parser-1.5.0.tgz#5d9fa63e2bcb0179ce48f3235303765eb89f3047" + dependencies: + flatten "^1.0.2" + indexes-of "^1.0.1" + uniq "^1.0.1" + +postcss-zindex@^2.0.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22" + dependencies: + has "^1.0.1" + postcss "^5.0.4" + uniqs "^2.0.0" + +postcss@6.0.22: + version "6.0.22" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.22.tgz#e23b78314905c3b90cbd61702121e7a78848f2a3" + dependencies: + chalk "^2.4.1" + source-map "^0.6.1" + supports-color "^5.4.0" + +postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16: + version "5.2.18" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" + dependencies: + chalk "^1.1.3" + js-base64 "^2.1.9" + source-map "^0.5.6" + supports-color "^3.2.3" + +postcss@^6.0, postcss@^6.0.0, postcss@^6.0.1, postcss@^6.0.11, postcss@^6.0.14, postcss@^6.0.17, postcss@^6.0.18, postcss@^6.0.21, postcss@^6.0.22, postcss@^6.0.23, postcss@^6.0.5, postcss@^6.0.6, postcss@^6.0.8: + version "6.0.23" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.23.tgz#61c82cc328ac60e677645f979054eb98bc0e3324" + dependencies: + chalk "^2.4.1" + source-map "^0.6.1" + supports-color "^5.4.0" + +postcss@^7.0.0, postcss@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.2.tgz#7b5a109de356804e27f95a960bef0e4d5bc9bb18" + dependencies: + chalk "^2.4.1" + source-map "^0.6.1" + supports-color "^5.4.0" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +prepend-http@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +private@^0.1.6, private@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" + +process-nextick-args@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" + +process@^0.11.1, process@^0.11.10: + version "0.11.10" + resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" + +progress@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" + +promise-each@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/promise-each/-/promise-each-2.2.0.tgz#3353174eff2694481037e04e01f77aa0fb6d1b60" + dependencies: + any-promise "^0.1.0" + +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + +public-encrypt@^4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.2.tgz#46eb9107206bf73489f8b85b69d91334c6610994" + dependencies: + bn.js "^4.1.0" + browserify-rsa "^4.0.0" + create-hash "^1.1.0" + parse-asn1 "^5.0.0" + randombytes "^2.0.1" + +pump@^2.0.0, pump@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" + dependencies: + end-of-stream "^1.1.0" + once "^1.3.1" + +pumpify@^1.3.3: + version "1.5.1" + resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" + dependencies: + duplexify "^3.6.0" + inherits "^2.0.3" + pump "^2.0.0" + +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + +punycode@^1.2.4: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + +q@^1.1.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" + +query-string@^4.1.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" + dependencies: + object-assign "^4.1.0" + strict-uri-encode "^1.0.0" + +querystring-es3@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" + +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + +quick-lru@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" + +randomatic@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.0.0.tgz#d35490030eb4f7578de292ce6dfb04a91a128923" + dependencies: + is-number "^4.0.0" + kind-of "^6.0.0" + math-random "^1.0.1" + +randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: + version "2.0.6" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" + dependencies: + safe-buffer "^5.1.0" + +randomfill@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" + dependencies: + randombytes "^2.0.5" + safe-buffer "^5.1.0" + +raw-loader@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-0.5.1.tgz#0c3d0beaed8a01c966d9787bf778281252a979aa" + +rc@^1.2.7: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +read-cache@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" + dependencies: + pify "^2.3.0" + +read-pkg-up@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-3.0.0.tgz#3ed496685dba0f8fe118d0691dc51f4a1ff96f07" + dependencies: + find-up "^2.0.0" + read-pkg "^3.0.0" + +read-pkg@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-3.0.0.tgz#9cbc686978fee65d16c00e2b19c237fcf6e38389" + dependencies: + load-json-file "^4.0.0" + normalize-package-data "^2.3.2" + path-type "^3.0.0" + +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.4, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6: + version "2.3.6" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@1.1: + version "1.1.13" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +redent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/redent/-/redent-2.0.0.tgz#c1b2007b42d57eb1389079b3c8333639d5e1ccaa" + dependencies: + indent-string "^3.0.0" + strip-indent "^2.0.0" + +reduce-css-calc@^1.2.6, reduce-css-calc@^1.2.7: + version "1.3.0" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" + dependencies: + balanced-match "^0.4.2" + math-expression-evaluator "^1.2.14" + reduce-function-call "^1.0.1" + +reduce-css-calc@^2.0.0: + version "2.1.4" + resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-2.1.4.tgz#c20e9cda8445ad73d4ff4bea960c6f8353791708" + dependencies: + css-unit-converter "^1.1.1" + postcss-value-parser "^3.3.0" + +reduce-function-call@^1.0.1, reduce-function-call@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" + dependencies: + balanced-match "^0.4.2" + +regenerate@^1.2.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" + +regenerator-runtime@^0.10.5: + version "0.10.5" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" + +regenerator-runtime@^0.11.0: + version "0.11.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" + +regenerator-transform@^0.10.0: + version "0.10.1" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" + dependencies: + babel-runtime "^6.18.0" + babel-types "^6.19.0" + private "^0.1.6" + +regex-cache@^0.4.2: + version "0.4.4" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" + dependencies: + is-equal-shallow "^0.1.3" + +regex-not@^1.0.0, regex-not@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" + dependencies: + extend-shallow "^3.0.2" + safe-regex "^1.1.0" + +regexpp@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" + +regexpu-core@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regexpu-core@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" + dependencies: + regenerate "^1.2.1" + regjsgen "^0.2.0" + regjsparser "^0.1.4" + +regjsgen@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" + +regjsparser@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" + dependencies: + jsesc "~0.5.0" + +remark-parse@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-5.0.0.tgz#4c077f9e499044d1d5c13f80d7a98cf7b9285d95" + dependencies: + collapse-white-space "^1.0.2" + is-alphabetical "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + is-word-character "^1.0.0" + markdown-escapes "^1.0.0" + parse-entities "^1.1.0" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + trim "0.0.1" + trim-trailing-lines "^1.0.0" + unherit "^1.0.4" + unist-util-remove-position "^1.0.0" + vfile-location "^2.0.0" + xtend "^4.0.1" + +remark-stringify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/remark-stringify/-/remark-stringify-5.0.0.tgz#336d3a4d4a6a3390d933eeba62e8de4bd280afba" + dependencies: + ccount "^1.0.0" + is-alphanumeric "^1.0.0" + is-decimal "^1.0.0" + is-whitespace-character "^1.0.0" + longest-streak "^2.0.1" + markdown-escapes "^1.0.0" + markdown-table "^1.1.0" + mdast-util-compact "^1.0.0" + parse-entities "^1.0.2" + repeat-string "^1.5.4" + state-toggle "^1.0.0" + stringify-entities "^1.0.1" + unherit "^1.0.4" + xtend "^4.0.1" + +remark@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/remark/-/remark-9.0.0.tgz#c5cfa8ec535c73a67c4b0f12bfdbd3a67d8b2f60" + dependencies: + remark-parse "^5.0.0" + remark-stringify "^5.0.0" + unified "^6.0.0" + +remove-trailing-separator@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2, repeat-string@^1.5.4, repeat-string@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +repeating@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" + dependencies: + is-finite "^1.0.0" + +replace-ext@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + +require-from-string@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + +require-main-filename@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" + +require-uncached@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" + dependencies: + caller-path "^0.1.0" + resolve-from "^1.0.0" + +resolve-cwd@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" + dependencies: + resolve-from "^3.0.0" + +resolve-from@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" + +resolve-from@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + +resolve-url@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" + +resolve@^1.3.2, resolve@^1.5.0: + version "1.8.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.8.1.tgz#82f1ec19a423ac1fbd080b0bab06ba36e84a7a26" + dependencies: + path-parse "^1.0.5" + +restore-cursor@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" + dependencies: + onetime "^2.0.0" + signal-exit "^3.0.2" + +ret@~0.1.10: + version "0.1.15" + resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" + +rgb-hex@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/rgb-hex/-/rgb-hex-2.1.0.tgz#c773c5fe2268a25578d92539a82a7a5ce53beda6" + +rgb@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/rgb/-/rgb-0.1.0.tgz#be27b291e8feffeac1bd99729721bfa40fc037b5" + +rimraf@^2.2.8, rimraf@^2.4.4, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" + dependencies: + glob "^7.0.5" + +ripemd160@^2.0.0, ripemd160@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" + dependencies: + hash-base "^3.0.0" + inherits "^2.0.1" + +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + dependencies: + is-promise "^2.1.0" + +run-queue@^1.0.0, run-queue@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" + dependencies: + aproba "^1.1.1" + +rx-lite-aggregates@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" + dependencies: + rx-lite "*" + +rx-lite@*, rx-lite@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + +rxjs@^6.1.0: + version "6.2.2" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.2.2.tgz#eb75fa3c186ff5289907d06483a77884586e1cf9" + dependencies: + tslib "^1.9.0" + +safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + +safe-regex@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" + dependencies: + ret "~0.1.10" + +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + +sax@^1.2.4, sax@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + +schema-utils@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf" + dependencies: + ajv "^5.0.0" + +schema-utils@^0.4.0, schema-utils@^0.4.4, schema-utils@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e" + dependencies: + ajv "^6.1.0" + ajv-keywords "^3.1.0" + +"semver@2 || 3 || 4 || 5", semver@^5.0.1, semver@^5.3.0, semver@^5.5.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" + +serialize-javascript@^1.4.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-1.5.0.tgz#1aa336162c88a890ddad5384baebc93a655161fe" + +set-blocking@^2.0.0, set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +set-value@^0.4.3: + version "0.4.3" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.1" + to-object-path "^0.3.0" + +set-value@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" + dependencies: + extend-shallow "^2.0.1" + is-extendable "^0.1.1" + is-plain-object "^2.0.3" + split-string "^3.0.1" + +setimmediate@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" + +sha.js@^2.4.0, sha.js@^2.4.8: + version "2.4.11" + resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" + dependencies: + inherits "^2.0.1" + safe-buffer "^5.0.1" + +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + +signal-exit@^3.0.0, signal-exit@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" + dependencies: + is-arrayish "^0.3.1" + +slash@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" + +slice-ansi@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" + dependencies: + is-fullwidth-code-point "^2.0.0" + +snapdragon-node@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" + dependencies: + define-property "^1.0.0" + isobject "^3.0.0" + snapdragon-util "^3.0.1" + +snapdragon-util@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" + dependencies: + kind-of "^3.2.0" + +snapdragon@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" + dependencies: + base "^0.11.1" + debug "^2.2.0" + define-property "^0.2.5" + extend-shallow "^2.0.1" + map-cache "^0.2.2" + source-map "^0.5.6" + source-map-resolve "^0.5.0" + use "^3.1.0" + +sort-keys@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" + dependencies: + is-plain-obj "^1.0.0" + +source-list-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" + +source-map-resolve@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" + dependencies: + atob "^2.1.1" + decode-uri-component "^0.2.0" + resolve-url "^0.2.1" + source-map-url "^0.4.0" + urix "^0.1.0" + +source-map-support@^0.4.15: + version "0.4.18" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" + dependencies: + source-map "^0.5.6" + +source-map-url@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" + +source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7: + version "0.5.7" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" + +source-map@^0.6.1, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + +spdx-correct@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" + +spdx-expression-parse@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" + +specificity@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/specificity/-/specificity-0.4.0.tgz#301b1ab5455987c37d6d94f8c956ef9d9fb48c1d" + +split-string@^3.0.1, split-string@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" + dependencies: + extend-shallow "^3.0.0" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +ssri@^5.2.4: + version "5.3.0" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-5.3.0.tgz#ba3872c9c6d33a0704a7d71ff045e5ec48999d06" + dependencies: + safe-buffer "^5.1.1" + +state-toggle@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.1.tgz#c3cb0974f40a6a0f8e905b96789eb41afa1cde3a" + +static-extend@^0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" + dependencies: + define-property "^0.2.5" + object-copy "^0.1.0" + +stream-browserify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" + dependencies: + inherits "~2.0.1" + readable-stream "^2.0.2" + +stream-each@^1.1.0: + version "1.2.3" + resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" + dependencies: + end-of-stream "^1.1.0" + stream-shift "^1.0.0" + +stream-http@^2.7.2: + version "2.8.3" + resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" + dependencies: + builtin-status-codes "^3.0.0" + inherits "^2.0.1" + readable-stream "^2.3.6" + to-arraybuffer "^1.0.0" + xtend "^4.0.0" + +stream-shift@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" + +strict-uri-encode@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" + dependencies: + is-fullwidth-code-point "^2.0.0" + strip-ansi "^4.0.0" + +string_decoder@^1.0.0, string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + dependencies: + safe-buffer "~5.1.0" + +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + +stringify-entities@^1.0.1: + version "1.3.2" + resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-1.3.2.tgz#a98417e5471fd227b3e45d3db1861c11caf668f7" + dependencies: + character-entities-html4 "^1.0.0" + character-entities-legacy "^1.0.0" + is-alphanumerical "^1.0.0" + is-hexadecimal "^1.0.0" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" + +strip-eof@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" + +strip-indent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-2.0.0.tgz#5ef8db295d01e6ed6cbf7aab96998d7822527b68" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +style-search@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/style-search/-/style-search-0.1.0.tgz#7958c793e47e32e07d2b5cafe5c0bf8e12e77902" + +stylelint@^9.3.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-9.4.0.tgz#2f2b82ae9db53a06735ae0724f41b134fdb84a10" + dependencies: + autoprefixer "^9.0.0" + balanced-match "^1.0.0" + chalk "^2.4.1" + cosmiconfig "^5.0.0" + debug "^3.0.0" + execall "^1.0.0" + file-entry-cache "^2.0.0" + get-stdin "^6.0.0" + globby "^8.0.0" + globjoin "^0.1.4" + html-tags "^2.0.0" + ignore "^4.0.0" + import-lazy "^3.1.0" + imurmurhash "^0.1.4" + known-css-properties "^0.6.0" + lodash "^4.17.4" + log-symbols "^2.0.0" + mathml-tag-names "^2.0.1" + meow "^5.0.0" + micromatch "^2.3.11" + normalize-selector "^0.2.0" + pify "^3.0.0" + postcss "^7.0.0" + postcss-html "^0.31.0" + postcss-less "^2.0.0" + postcss-markdown "^0.31.0" + postcss-media-query-parser "^0.2.3" + postcss-reporter "^5.0.0" + postcss-resolve-nested-selector "^0.1.1" + postcss-safe-parser "^4.0.0" + postcss-sass "^0.3.0" + postcss-scss "^2.0.0" + postcss-selector-parser "^3.1.0" + postcss-styled "^0.31.0" + postcss-syntax "^0.31.0" + postcss-value-parser "^3.3.0" + resolve-from "^4.0.0" + signal-exit "^3.0.2" + specificity "^0.4.0" + string-width "^2.1.0" + style-search "^0.1.0" + sugarss "^1.0.0" + svg-tags "^1.0.0" + table "^4.0.1" + +sugarss@^1.0.0, sugarss@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sugarss/-/sugarss-1.0.1.tgz#be826d9003e0f247735f92365dc3fd7f1bae9e44" + dependencies: + postcss "^6.0.14" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + dependencies: + has-flag "^1.0.0" + +supports-color@^5.3.0, supports-color@^5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54" + dependencies: + has-flag "^3.0.0" + +svg-sprite-generator@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/svg-sprite-generator/-/svg-sprite-generator-0.0.7.tgz#159777ce3e83e800f255cedd311da761492163e9" + dependencies: + async "^1.3.0" + cheerio "^0.19.0" + commander "^2.8.1" + es6-promise "^2.3.0" + +svg-tags@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764" + +svgo@^0.7.0: + version "0.7.2" + resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" + dependencies: + coa "~1.0.1" + colors "~1.1.2" + csso "~2.3.1" + js-yaml "~3.7.0" + mkdirp "~0.5.1" + sax "~1.2.1" + whet.extend "~0.9.9" + +table@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" + dependencies: + ajv "^5.2.3" + ajv-keywords "^2.1.0" + chalk "^2.1.0" + lodash "^4.17.4" + slice-ansi "1.0.0" + string-width "^2.1.1" + +table@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.3.tgz#00b5e2b602f1794b9acaf9ca908a76386a7813bc" + dependencies: + ajv "^6.0.1" + ajv-keywords "^3.0.0" + chalk "^2.1.0" + lodash "^4.17.4" + slice-ansi "1.0.0" + string-width "^2.1.1" + +tapable@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.0.0.tgz#cbb639d9002eed9c6b5975eb20598d7936f1f9f2" + +tar@^4: + version "4.4.6" + resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.6.tgz#63110f09c00b4e60ac8bcfe1bf3c8660235fbc9b" + dependencies: + chownr "^1.0.1" + fs-minipass "^1.2.5" + minipass "^2.3.3" + minizlib "^1.1.0" + mkdirp "^0.5.0" + safe-buffer "^5.1.2" + yallist "^3.0.2" + +text-table@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + +through2@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.3.tgz#0004569b37c7c74ba39c43f3ced78d1ad94140be" + dependencies: + readable-stream "^2.1.5" + xtend "~4.0.1" + +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + +timers-browserify@^2.0.4: + version "2.0.10" + resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.10.tgz#1d28e3d2aadf1d5a5996c4e9f95601cd053480ae" + dependencies: + setimmediate "^1.0.4" + +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + dependencies: + os-tmpdir "~1.0.2" + +to-arraybuffer@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" + +to-fast-properties@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" + +to-object-path@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" + dependencies: + kind-of "^3.0.2" + +to-regex-range@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" + dependencies: + is-number "^3.0.0" + repeat-string "^1.6.1" + +to-regex@^3.0.1, to-regex@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" + dependencies: + define-property "^2.0.2" + extend-shallow "^3.0.2" + regex-not "^1.0.2" + safe-regex "^1.1.0" + +trim-newlines@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-2.0.0.tgz#b403d0b91be50c331dfc4b82eeceb22c3de16d20" + +trim-right@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" + +trim-trailing-lines@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz#e0ec0810fd3c3f1730516b45f49083caaf2774d9" + +trim@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" + +trough@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.2.tgz#7f1663ec55c480139e2de5e486c6aef6cc24a535" + +ts-loader@^4.4.2: + version "4.4.2" + resolved "https://registry.yarnpkg.com/ts-loader/-/ts-loader-4.4.2.tgz#778d4464b24436873c78f7f9e914d88194c2a248" + dependencies: + chalk "^2.3.0" + enhanced-resolve "^4.0.0" + loader-utils "^1.0.2" + micromatch "^3.1.4" + semver "^5.0.1" + +tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" + +tslint-loader@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/tslint-loader/-/tslint-loader-3.6.0.tgz#12ed4d5ef57d68be25cd12692fb2108b66469d76" + dependencies: + loader-utils "^1.0.2" + mkdirp "^0.5.1" + object-assign "^4.1.1" + rimraf "^2.4.4" + semver "^5.3.0" + +tslint@^5.11.0: + version "5.11.0" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.11.0.tgz#98f30c02eae3cde7006201e4c33cb08b48581eed" + dependencies: + babel-code-frame "^6.22.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^3.2.0" + glob "^7.1.1" + js-yaml "^3.7.0" + minimatch "^3.0.4" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.27.2" + +tsutils@^2.27.2: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + dependencies: + tslib "^1.8.1" + +tty-browserify@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +typedarray@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" + +typescript@^2.9.2: + version "2.9.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c" + +uglify-es@^3.3.4: + version "3.3.9" + resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677" + dependencies: + commander "~2.13.0" + source-map "~0.6.1" + +uglifyjs-webpack-plugin@^1.2.4: + version "1.2.7" + resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-1.2.7.tgz#57638dd99c853a1ebfe9d97b42160a8a507f9d00" + dependencies: + cacache "^10.0.4" + find-cache-dir "^1.0.0" + schema-utils "^0.4.5" + serialize-javascript "^1.4.0" + source-map "^0.6.1" + uglify-es "^3.3.4" + webpack-sources "^1.1.0" + worker-farm "^1.5.2" + +unherit@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.1.tgz#132748da3e88eab767e08fabfbb89c5e9d28628c" + dependencies: + inherits "^2.0.1" + xtend "^4.0.1" + +unified@^6.0.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/unified/-/unified-6.2.0.tgz#7fbd630f719126d67d40c644b7e3f617035f6dba" + dependencies: + bail "^1.0.0" + extend "^3.0.0" + is-plain-obj "^1.1.0" + trough "^1.0.0" + vfile "^2.0.0" + x-is-string "^0.1.0" + +union-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" + dependencies: + arr-union "^3.1.0" + get-value "^2.0.6" + is-extendable "^0.1.1" + set-value "^0.4.3" + +uniq@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" + +uniqs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" + +unique-filename@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.0.tgz#d05f2fe4032560871f30e93cbe735eea201514f3" + dependencies: + unique-slug "^2.0.0" + +unique-slug@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.0.tgz#db6676e7c7cc0629878ff196097c78855ae9f4ab" + dependencies: + imurmurhash "^0.1.4" + +unist-util-find-all-after@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/unist-util-find-all-after/-/unist-util-find-all-after-1.0.2.tgz#9be49cfbae5ca1566b27536670a92836bf2f8d6d" + dependencies: + unist-util-is "^2.0.0" + +unist-util-is@^2.0.0, unist-util-is@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-2.1.2.tgz#1193fa8f2bfbbb82150633f3a8d2eb9a1c1d55db" + +unist-util-modify-children@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-modify-children/-/unist-util-modify-children-1.1.2.tgz#c7f1b91712554ee59c47a05b551ed3e052a4e2d1" + dependencies: + array-iterate "^1.0.0" + +unist-util-remove-position@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz#86b5dad104d0bbfbeb1db5f5c92f3570575c12cb" + dependencies: + unist-util-visit "^1.1.0" + +unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6" + +unist-util-visit-parents@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-2.0.1.tgz#63fffc8929027bee04bfef7d2cce474f71cb6217" + dependencies: + unist-util-is "^2.1.2" + +unist-util-visit@^1.1.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.4.0.tgz#1cb763647186dc26f5e1df5db6bd1e48b3cc2fb1" + dependencies: + unist-util-visit-parents "^2.0.0" + +units-css@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/units-css/-/units-css-0.4.0.tgz#d6228653a51983d7c16ff28f8b9dc3b1ffed3a07" + dependencies: + isnumeric "^0.2.0" + viewport-dimensions "^0.2.0" + +unset-value@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" + dependencies: + has-value "^0.3.1" + isobject "^3.0.0" + +upath@^1.0.5: + version "1.1.0" + resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" + +uri-js@^4.2.1: + version "4.2.2" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" + dependencies: + punycode "^2.1.0" + +urix@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" + +url@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" + dependencies: + punycode "1.3.2" + querystring "0.2.0" + +use@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + dependencies: + inherits "2.0.1" + +util@^0.10.3: + version "0.10.4" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" + dependencies: + inherits "2.0.3" + +v8-compile-cache@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.0.tgz#526492e35fc616864284700b7043e01baee09f0a" + +validate-npm-package-license@^3.0.1: + version "3.0.3" + resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +vendors@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.2.tgz#7fcb5eef9f5623b156bcea89ec37d63676f21801" + +vfile-location@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.3.tgz#083ba80e50968e8d420be49dd1ea9a992131df77" + +vfile-message@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-1.0.1.tgz#51a2ccd8a6b97a7980bb34efb9ebde9632e93677" + dependencies: + unist-util-stringify-position "^1.1.1" + +vfile@^2.0.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/vfile/-/vfile-2.3.0.tgz#e62d8e72b20e83c324bc6c67278ee272488bf84a" + dependencies: + is-buffer "^1.1.4" + replace-ext "1.0.0" + unist-util-stringify-position "^1.0.0" + vfile-message "^1.0.0" + +viewport-dimensions@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/viewport-dimensions/-/viewport-dimensions-0.2.0.tgz#de740747db5387fd1725f5175e91bac76afdf36c" + +vm-browserify@0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" + dependencies: + indexof "0.0.1" + +watchpack@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.6.0.tgz#4bc12c2ebe8aa277a71f1d3f14d685c7b446cd00" + dependencies: + chokidar "^2.0.2" + graceful-fs "^4.1.2" + neo-async "^2.5.0" + +webpack-cli@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.1.0.tgz#d71a83687dcfeb758fdceeb0fe042f96bcf62994" + dependencies: + chalk "^2.4.1" + cross-spawn "^6.0.5" + enhanced-resolve "^4.0.0" + global-modules-path "^2.1.0" + import-local "^1.0.0" + inquirer "^6.0.0" + interpret "^1.1.0" + loader-utils "^1.1.0" + supports-color "^5.4.0" + v8-compile-cache "^2.0.0" + yargs "^12.0.1" + +webpack-sources@^1.0.1, webpack-sources@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" + dependencies: + source-list-map "^2.0.0" + source-map "~0.6.1" + +webpack@^4.16.2: + version "4.16.4" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.16.4.tgz#6b020f76483bc66339164c296d89978aa100d37a" + dependencies: + "@webassemblyjs/ast" "1.5.13" + "@webassemblyjs/helper-module-context" "1.5.13" + "@webassemblyjs/wasm-edit" "1.5.13" + "@webassemblyjs/wasm-opt" "1.5.13" + "@webassemblyjs/wasm-parser" "1.5.13" + acorn "^5.6.2" + acorn-dynamic-import "^3.0.0" + ajv "^6.1.0" + ajv-keywords "^3.1.0" + chrome-trace-event "^1.0.0" + enhanced-resolve "^4.1.0" + eslint-scope "^4.0.0" + json-parse-better-errors "^1.0.2" + loader-runner "^2.3.0" + loader-utils "^1.1.0" + memory-fs "~0.4.1" + micromatch "^3.1.8" + mkdirp "~0.5.0" + neo-async "^2.5.0" + node-libs-browser "^2.0.0" + schema-utils "^0.4.4" + tapable "^1.0.0" + uglifyjs-webpack-plugin "^1.2.4" + watchpack "^1.5.0" + webpack-sources "^1.0.1" + +whet.extend@~0.9.9: + version "0.9.9" + resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" + +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + +which@^1.2.9: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.3" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" + dependencies: + string-width "^1.0.2 || 2" + +wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +worker-farm@^1.5.2: + version "1.6.0" + resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.6.0.tgz#aecc405976fab5a95526180846f0dba288f3a4a0" + dependencies: + errno "~0.1.7" + +wrap-ansi@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" + dependencies: + string-width "^1.0.1" + strip-ansi "^3.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +write@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" + dependencies: + mkdirp "^0.5.1" + +x-is-string@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" + +xregexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020" + +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +"y18n@^3.2.1 || ^4.0.0", y18n@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" + +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + +yallist@^3.0.0, yallist@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" + +yargs-parser@^10.0.0, yargs-parser@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" + dependencies: + camelcase "^4.1.0" + +yargs@^12.0.1: + version "12.0.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.1.tgz#6432e56123bb4e7c3562115401e98374060261c2" + dependencies: + cliui "^4.0.0" + decamelize "^2.0.0" + find-up "^3.0.0" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1 || ^4.0.0" + yargs-parser "^10.1.0"