From 43ab0f193854efb816705159dee4c97d483a2f8c Mon Sep 17 00:00:00 2001 From: Zahra Jabini Date: Fri, 21 Feb 2020 17:20:00 -0500 Subject: [PATCH] =?UTF-8?q?Cleanup=20=F0=9F=9A=B4=E2=80=8D=E2=99=80?= =?UTF-8?q?=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 12 +- Brocfile.js | 51 - README.md | 51 +- assets/demo/debug.html | 2 +- assets/demo/debug.js | 3 +- assets/demo/demo.js | 2 +- assets/demo/index.html | 4 +- assets/demo/mobiledoc.css | 151 + assets/demo/mobiledoc.js | 12131 ++++++++++++++++++++++++++++++++++++ broccoli/demo.js | 8 - broccoli/jquery.js | 21 - package.json | 25 +- rollup.config.js | 36 +- src/js/editor/ui.js | 4 + src/js/index.js | 3 +- testem-ci.js | 2 +- testem.js | 2 +- yarn.lock | 3596 +---------- 18 files changed, 12604 insertions(+), 3500 deletions(-) delete mode 100644 Brocfile.js create mode 100644 assets/demo/mobiledoc.css create mode 100644 assets/demo/mobiledoc.js delete mode 100644 broccoli/demo.js delete mode 100644 broccoli/jquery.js diff --git a/.eslintrc.js b/.eslintrc.js index d9b1df5e1..b4508e22f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,8 +1,15 @@ module.exports = { "env": { - "browser": true, - "jquery": true + "browser": true }, + "overrides": [ + { + "files": ["rollup.config.js"], + "env": { + "node": true + } + } + ], "extends": "eslint:recommended", "parserOptions": { "ecmaVersion": 2017, @@ -160,7 +167,6 @@ module.exports = { "no-param-reassign": "off", "no-path-concat": "error", "no-plusplus": "off", - "no-process-env": "error", "no-process-exit": "error", "no-proto": "error", "no-prototype-builtins": "off", diff --git a/Brocfile.js b/Brocfile.js deleted file mode 100644 index 806f44fea..000000000 --- a/Brocfile.js +++ /dev/null @@ -1,51 +0,0 @@ -/* jshint node:true */ - -var broccoli = require("broccoli"); -var Watcher = require("broccoli-sane-watcher"); -var Funnel = require("broccoli-funnel"); -var builder = require("broccoli-multi-builder"); -var mergeTrees = require("broccoli-merge-trees"); -var testTreeBuilder = require("broccoli-test-builder"); -var jquery = require("./broccoli/jquery"); -var BroccoliLiveReload = require("broccoli-livereload"); -var replace = require("broccoli-string-replace"); -var demoTree = require("./broccoli/demo"); - -var vendoredModules = [ - { name: "mobiledoc-dom-renderer" }, - { name: "mobiledoc-text-renderer" } -]; - -var cssFiles = new Funnel("src/css", { destDir: "css" }); - -var packageName = require("./package.json").name; - -var buildOptions = { - libDirName: "src/js", - vendoredModules: vendoredModules, - packageName: packageName -}; - -var testTree = testTreeBuilder.build({ libDirName: "src" }); -testTree = jquery.build(testTree, "/tests/jquery"); -testTree = new BroccoliLiveReload(testTree, { target: "index.html" }); - -var testBuilder = new broccoli.Builder(testTree); - -function replaceVersion(tree) { - var version = require("./package.json").version; - return replace(tree, { - files: [ "**/*.js" ], - pattern: { match: /##VERSION##/g, replacement: version } - }); -} - -module.exports = mergeTrees([ - replaceVersion(builder.build("amd", buildOptions)), - replaceVersion(builder.build("global", buildOptions)), - replaceVersion(builder.build("commonjs", buildOptions)), - cssFiles, - testTree, - demoTree() -]); - diff --git a/README.md b/README.md index e42cd001f..00519f322 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,12 @@ ![Mobiledoc Logo](https://bustle.github.io/mobiledoc-kit/demo/mobiledoc-logo-color-small.png) -Mobiledoc Kit (beta) is a framework-agnostic library for building WYSIWYG editors +Mobiledoc Kit is a framework-agnostic library for building WYSIWYG editors supporting rich content via cards. ## Libraries -This repository hosts the core Mobiledoc-Kit library. If you want to use Mobiledoc-Kit to *create a WYSIWYG editor* you have the following options: +This repository hosts the core Mobiledoc Kit library. If you want to use Mobiledoc Kit to *create a WYSIWYG editor* you have the following options: | Environment | Library | | ----------- | ------- | @@ -32,6 +32,7 @@ If you only want to use the Mobiledoc-Kit runtime, for *rendering mobiledoc post | Server-Side Rendering (Text-only, e.g. SEO) | [mobiledoc-text-renderer](https://github.com/bustle/mobiledoc-text-renderer) | | In-Browser (DOM) Rendering, with Ember | [ember-mobiledoc-dom-renderer](https://github.com/bustle/ember-mobiledoc-dom-renderer) | | React Server and Browser Renderer | [mobiledoc-react-renderer](https://github.com/dailybeast/mobiledoc-react-renderer) | +| 🔮 Render Mobiledoc as VDOM by passing React or React-like `createElement` function | [mobiledoc-vdom-renderer](https://github.com/bustle/mobiledoc-vdom-renderer) | Mobiledoc is a deliberately simple and terse format, and you are encouraged to write your own renderer if you have other target output formats (e.g., a PDF renderer, an iOS Native Views Renderer, etc.). @@ -75,7 +76,7 @@ The `Mobiledoc.Editor` class is invoked with an element to render into and optionally a Mobiledoc to load. For example: ```js -var simpleMobiledoc = { +const simpleMobiledoc = { version: "0.3.1", markups: [], atoms: [], @@ -86,9 +87,9 @@ var simpleMobiledoc = { ]] ] }; -var element = document.querySelector('#editor'); -var options = { mobiledoc: simpleMobiledoc }; -var editor = new Mobiledoc.Editor(options); +const element = document.querySelector('#editor'); +const options = { mobiledoc: simpleMobiledoc }; +const editor = new Mobiledoc.Editor(options); editor.render(element); ``` @@ -253,7 +254,7 @@ and better understand changes being made to the post. ```js editor.run(postEditor => { - const mention = postEditor.builder.createAtom("mention", "John Doe", { id: 42 }); + const mention = postEditor.builder.createAtom("mention", "Jane Doe", { id: 42 }); // insert at current cursor position: // or should the user have to grab the current position from the editor first? postEditor.insertMarkers(editor.range.head, [mention]); @@ -271,7 +272,7 @@ The Mobiledoc editor allows the configuration of hot keys and text expansions. For instance, the hot-key command-B to make selected text bold, is registered internally as: -```javascript +```js const boldKeyCommand = { str: 'META+B', run(editor) { @@ -296,7 +297,7 @@ The key can be any of the alphanumeric characters on the keyboard, or one of the You can override built-in behavior by simply registering a hot key with the same name. For example, to submit a form instead of entering a new line when `enter` is pressed you could do the following: -```javascript +```js const enterKeyCommand = { str: 'enter', run(editor) { @@ -318,7 +319,7 @@ The callback is called after the matching text has been inserted. It is passed the `editor` instance and an array of matches (either the result of `match.exec` on the matching user-entered text, or an array containing only the `text`). -```javascript +```js editor.onTextInput({ text: 'X', run(editor) { @@ -339,7 +340,7 @@ The editor has several default text input handlers that are defined in To remove default text input handlers call the unregister function. -```javascript +```js editor.unregisterAllTextInputHandlers(); ``` @@ -379,16 +380,16 @@ function imageToCardParser(node, builder, {addSection, addMarkerable, nodeFinish if (node.nodeType !== 1 || node.tagName !== 'IMG') { return; } - var payload = { src: node.src }; - var cardSection = builder.createCardSection('my-image', payload); + const payload = { src: node.src }; + const cardSection = builder.createCardSection('my-image', payload); addSection(cardSection); nodeFinished(); } -var options = { +const options = { parserPlugins: [imageToCardParser] }; -var editor = new Mobiledoc.Editor(options); -var element = document.querySelector('#editor'); +const editor = new Mobiledoc.Editor(options); +const element = document.querySelector('#editor'); editor.render(element); ``` @@ -406,7 +407,7 @@ parsed by the next plugin or the default parser. ## Caveats -### Mobiledoc-kit and the Grammarly extension +### Mobiledoc Kit and the Grammarly extension `mobiledoc-kit` and the [Grammarly extension](https://www.grammarly.com/) do not play well together (see [issue 422](https://github.com/bustle/mobiledoc-kit/issues/422)). Until this is resolved, you can avoid any such problems by disabling Grammarly for the `mobiledoc-kit` instances on your page. To do this, add the `data-gramm="false"` attribute to the `mobiledoc-kit` main DOM element. ## Contributing @@ -423,20 +424,20 @@ Install dependencies via yarn: Run tests via the built-in broccoli server: - * `npm start` + * `yarn start` * `open http://localhost:4200/tests` Or run headless tests via testem: - * `npm test` + * `yarn test` -Tests in CI are run at Travis via Saucelabs (see the `test:ci` npm script). +Tests in CI are run at Travis via Saucelabs (see the `test:ci` yarn script). ### Demo To run the demo site locally: - * `npm start` + * `yarn start` * `open http://localhost:4200/demo` The assets for the demo are in `assets/demo`. @@ -448,7 +449,7 @@ If you have a question about usage you can post in the [slack channel](https://m ### Releasing (Implementer notes) -* Use `np` (`npm install -g np`) +* Use `np` (`yarn install -g np`) * `np ` (e.g. `np 0.12.0`) * `git push --tags` @@ -464,8 +465,8 @@ does not rebuild jsdoc. To publish a new version: - * `npm run build:website` - This builds the website into `website/` and commits it - * `npm run deploy:website` - Pushes the `website/` subtree to the `gh-pages` + * `yarn run build:website` - This builds the website into `website/` and commits it + * `yarn run deploy:website` - Pushes the `website/` subtree to the `gh-pages` branch of your `origin` at github -*Development of Mobiledoc and the supporting libraries was generously funded by [Bustle Labs](http://www.bustle.com/labs). Bustle Labs is the tech team behind the editorial staff at [Bustle](http://www.bustle.com), a fantastic and successful feminist and women’s interest site based in NYC.* +*Development of Mobiledoc and the supporting libraries was generously funded by [Bustle Digital Group](https://bustle.company/).* diff --git a/assets/demo/debug.html b/assets/demo/debug.html index e91ec16e6..7e699aa59 100644 --- a/assets/demo/debug.html +++ b/assets/demo/debug.html @@ -4,7 +4,7 @@ Mobiledoc Kit Debug - + diff --git a/assets/demo/debug.js b/assets/demo/debug.js index acd98092b..96cc73831 100644 --- a/assets/demo/debug.js +++ b/assets/demo/debug.js @@ -1,4 +1,4 @@ -import { Editor } from '../mobiledoc.js'; +import { Editor } from './mobiledoc.js'; let editor; @@ -264,6 +264,7 @@ const movableCard = { }; function speakingPlugin(node) { + // eslint-disable-next-line no-console console.log('got node!',node); } diff --git a/assets/demo/demo.js b/assets/demo/demo.js index 3e4a8b806..ffb355948 100644 --- a/assets/demo/demo.js +++ b/assets/demo/demo.js @@ -1,4 +1,4 @@ -import { Editor } from '../mobiledoc.js'; +import { Editor } from './mobiledoc.js'; function bootstrapSimpleDemo() { const el = document.querySelector('#editor-basic'); diff --git a/assets/demo/index.html b/assets/demo/index.html index 280c3697f..3949681b9 100644 --- a/assets/demo/index.html +++ b/assets/demo/index.html @@ -7,7 +7,7 @@ - + @@ -28,7 +28,7 @@

- Publishers use the Mobiledoc-Kit library to + Publishers use the Mobiledoc Kit library to build rich-content editors customized for their content. Posts are serialized to the JSON-based Mobiledoc format, a structure designed to unambiguously represent the content and be simple to render diff --git a/assets/demo/mobiledoc.css b/assets/demo/mobiledoc.css new file mode 100644 index 000000000..18af81e69 --- /dev/null +++ b/assets/demo/mobiledoc.css @@ -0,0 +1,151 @@ +/** + * Editor + */ + +.__mobiledoc-editor { + font-family: 'Lora', Georgia, serif; + margin: 1em 0; + color: #454545; + font-size: 1.2em; + line-height: 1.6em; + position: relative; + min-height: 1em; +} + +.__mobiledoc-editor:focus { + outline: none; +} + +.__mobiledoc-editor > * { + position: relative; +} + +.__mobiledoc-editor.__has-no-content:after { + content: attr(data-placeholder); + color: #bbb; + cursor: text; + position: absolute; + top: 0; +} + +.__mobiledoc-editor a { + color: #0b8bff; + white-space: nowrap; +} + +.__mobiledoc-editor h1, +.__mobiledoc-editor h2, +.__mobiledoc-editor h3, +.__mobiledoc-editor h4, +.__mobiledoc-editor h5, +.__mobiledoc-editor h6 { + font-family: 'Merriweather Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; + font-weight: 800; + letter-spacing: -0.02em; +} + +.__mobiledoc-editor blockquote { + border-left: 4px solid #0b8bff; + margin: 1em 0 1em -1.2em; + padding-left: 1.05em; + color: #a0a0a0; +} + +.__mobiledoc-editor img { + display: block; + max-width: 100%; + margin: 0 auto; +} + +.__mobiledoc-editor div, +.__mobiledoc-editor iframe { + max-width: 100%; +} + +.__mobiledoc-editor [data-md-text-align='left'] { + text-align: left; +} + +.__mobiledoc-editor [data-md-text-align='center'] { + text-align: center; +} + +.__mobiledoc-editor [data-md-text-align='right'] { + text-align: right; +} + +.__mobiledoc-editor [data-md-text-align='justify'] { + text-align: justify; +} + +.__mobiledoc-editor ol, +.__mobiledoc-editor ul { + list-style-position: inside; +} + +/** + * Cards + */ + +.__mobiledoc-card { + display: inline-block; +} + +/** + * Tooltips + */ + +@-webkit-keyframes fade-in { + 0% { opacity: 0; } + 100% { opacity: 1; } +} +@keyframes fade-in { + 0% { opacity: 0; } + 100% { opacity: 1; } +} + +.__mobiledoc-tooltip { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 0.7em; + white-space: nowrap; + position: absolute; + background-color: rgba(43,43,43,0.9); + border-radius: 3px; + line-height: 1em; + padding: 0.7em 0.9em; + color: #FFF; + -webkit-animation: fade-in 0.2s; + animation: fade-in 0.2s; +} + +.__mobiledoc-tooltip:before { + content: ''; + position: absolute; + left: 50%; + width: 0; + height: 0; + border-left: 0.4em solid transparent; + border-right: 0.4em solid transparent; + border-bottom: 0.4em solid rgba(43,43,43,0.9); + top: -0.4em; + margin-left: -0.4em; +} + +/* help keeps mouseover state when moving from link to tooltip */ +.__mobiledoc-tooltip:after { + content: ''; + position: absolute; + left: 0; + right: 0; + top: -0.4em; + height: 0.4em; +} + +.__mobiledoc-tooltip a { + color: #FFF; + text-decoration: none; +} + +.__mobiledoc-tooltip a:hover { + text-decoration: underline; +} diff --git a/assets/demo/mobiledoc.js b/assets/demo/mobiledoc.js new file mode 100644 index 000000000..f6c51e0d8 --- /dev/null +++ b/assets/demo/mobiledoc.js @@ -0,0 +1,12131 @@ +function detect(enumerable, callback) { + if (enumerable.detect) { + return enumerable.detect(callback); + } else { + for (let i=0; i { + if (conditionFn(i)) { filtered.push(i); } + }); + return filtered; +} + +/** + * @return {Integer} the number of items that are the same, starting from the 0th index, in a and b + * @private + */ +function commonItemLength(listA, listB) { + let offset = 0; + while (offset < listA.length && offset < listB.length) { + if (listA[offset] !== listB[offset]) { + break; + } + offset++; + } + return offset; +} + +/** + * @return {Array} the items that are the same, starting from the 0th index, in a and b + * @private + */ +function commonItems(listA, listB) { + let offset = 0; + while (offset < listA.length && offset < listB.length) { + if (listA[offset] !== listB[offset]) { + break; + } + offset++; + } + return listA.slice(0, offset); +} + +function reduce(enumerable, callback, initialValue) { + let previousValue = initialValue; + forEach(enumerable, (val, index) => { + previousValue = callback(previousValue, val, index); + }); + return previousValue; +} + +/** + * @param {Array} array of key1,value1,key2,value2,... + * @return {Object} {key1:value1, key2:value2, ...} + * @private + */ +function kvArrayToObject(array) { + const obj = {}; + for (let i = 0; i < array.length; i+=2) { + let [key, value] = [array[i], array[i+1]]; + obj[key] = value; + } + return obj; +} + +function objectToSortedKVArray(obj) { + const keys = Object.keys(obj).sort(); + const result = []; + keys.forEach(k => { + result.push(k); + result.push(obj[k]); + }); + return result; +} + +// check shallow equality of two non-nested arrays +function isArrayEqual(arr1, arr2) { + let l1 = arr1.length, l2 = arr2.length; + if (l1 !== l2) { return false; } + + for (let i=0; i < l1; i++) { + if (arr1[i] !== arr2[i]) { return false; } + } + return true; +} + +// return an object with only the valid keys +function filterObject(object, validKeys=[]) { + let result = {}; + forEach( + filter(Object.keys(object), key => validKeys.indexOf(key) !== -1), + key => result[key] = object[key] + ); + return result; +} + +function contains(array, item) { + return array.indexOf(item) !== -1; +} + +function values(object) { + return Object.keys(object).map(key => object[key]); +} + +const NODE_TYPES = { + ELEMENT: 1, + TEXT: 3, + COMMENT: 8 +}; + +function isTextNode(node) { + return node.nodeType === NODE_TYPES.TEXT; +} + +function isCommentNode(node) { + return node.nodeType === NODE_TYPES.COMMENT; +} + +function isElementNode(node) { + return node.nodeType === NODE_TYPES.ELEMENT; +} + + +function clearChildNodes(element) { + while (element.childNodes.length) { + element.removeChild(element.childNodes[0]); + } +} + +/** + * @return {Boolean} true when the child node is contained or the same as + * (e.g., inclusive containment) the parent node + * see https://github.com/webmodules/node-contains/blob/master/index.js + * Mimics the behavior of `Node.contains`, which is broken in IE 10 + * @private + */ +function containsNode(parentNode, childNode) { + if (parentNode === childNode) { + return true; + } + const position = parentNode.compareDocumentPosition(childNode); + return !!(position & Node.DOCUMENT_POSITION_CONTAINED_BY); +} + +/** + * converts the element's NamedNodeMap of attrs into + * an object with key-value pairs + * @param {DOMNode} element + * @return {Object} key-value pairs + * @private + */ +function getAttributes(element) { + const result = {}; + if (element.hasAttributes()) { + forEach(element.attributes, ({name,value}) => { + result[name] = value; + }); + } + return result; +} + +function addClassName(element, className) { + element.classList.add(className); +} + +function removeClassName(element, className) { + element.classList.remove(className); +} + +function normalizeTagName(tagName) { + return tagName.toLowerCase(); +} + +function parseHTML(html) { + const div = document.createElement('div'); + div.innerHTML = html; + return div; +} + +function serializeHTML(node) { + const div = document.createElement('div'); + div.appendChild(node); + return div.innerHTML; +} + +class View { + constructor(options={}) { + options.tagName = options.tagName || 'div'; + options.container = options.container || document.body; + + this.element = document.createElement(options.tagName); + this.container = options.container; + this.isShowing = false; + + let classNames = options.classNames || []; + classNames.forEach(name => addClassName(this.element, name)); + this._eventListeners = []; + } + + addEventListener(element, type, listener) { + element.addEventListener(type, listener); + this._eventListeners.push([element, type, listener]); + } + + removeAllEventListeners() { + this._eventListeners.forEach(([element, type, listener]) => { + element.removeEventListener(type, listener); + }); + } + + show() { + if(!this.isShowing) { + this.container.appendChild(this.element); + this.isShowing = true; + return true; + } + } + + hide() { + if (this.isShowing) { + this.container.removeChild(this.element); + this.isShowing = false; + return true; + } + } + + destroy() { + this.removeAllEventListeners(); + this.hide(); + this.isDestroyed = true; + } +} + +/* + * @param {String} string + * @return {String} a dasherized string. 'modelIndex' -> 'model-index', etc + */ +function dasherize(string) { + return string.replace(/[A-Z]/g, (match, offset) => { + const lower = match.toLowerCase(); + + return (offset === 0 ? lower : '-' + lower); + }); +} + +function startsWith(string, character) { + return string.charAt(0) === character; +} + +function endsWith(string, endString) { + let index = string.lastIndexOf(endString); + return index !== -1 && index === string.length - endString.length; +} + +function getEventTargetMatchingTag(tagName, target, container) { + tagName = normalizeTagName(tagName); + // Traverses up DOM from an event target to find the node matching specifed tag + while (target && target !== container) { + if (normalizeTagName(target.tagName) === tagName) { + return target; + } + target = target.parentNode; + } +} + +function getElementRelativeOffset(element) { + var offset = { left: 0, top: -window.pageYOffset }; + var offsetParent = element.offsetParent; + var offsetParentPosition = window.getComputedStyle(offsetParent).position; + var offsetParentRect; + + if (offsetParentPosition === 'relative') { + offsetParentRect = offsetParent.getBoundingClientRect(); + offset.left = offsetParentRect.left; + offset.top = offsetParentRect.top; + } + return offset; +} + +function getElementComputedStyleNumericProp(element, prop) { + return parseFloat(window.getComputedStyle(element)[prop]); +} + +function positionElementToRect(element, rect, topOffset, leftOffset) { + var relativeOffset = getElementRelativeOffset(element); + var style = element.style; + var round = Math.round; + var left, top; + + topOffset = topOffset || 0; + leftOffset = leftOffset || 0; + left = round(rect.left - relativeOffset.left - leftOffset); + top = round(rect.top - relativeOffset.top - topOffset); + style.left = left + 'px'; + style.top = top + 'px'; + return { left: left, top: top }; +} + +function positionElementHorizontallyCenteredToRect(element, rect, topOffset) { + var horizontalCenter = (element.offsetWidth / 2) - (rect.width / 2); + return positionElementToRect(element, rect, topOffset, horizontalCenter); +} + +function positionElementCenteredBelow(element, belowElement) { + var elementMargin = getElementComputedStyleNumericProp(element, 'marginTop'); + return positionElementHorizontallyCenteredToRect(element, belowElement.getBoundingClientRect(), -element.offsetHeight - elementMargin); +} + +function setData(element, name, value) { + if (element.dataset) { + element.dataset[name] = value; + } else { + const dataName = dasherize(name); + return element.setAttribute(dataName, value); + } +} + +function whenElementIsNotInDOM(element, callback) { + let isCanceled = false; + const observerFn = () => { + if (isCanceled) { return; } + if (!element.parentNode) { + callback(); + } else { + window.requestAnimationFrame(observerFn); + } + }; + observerFn(); + return { cancel: () => isCanceled = true }; +} + +const DELAY = 200; + +class Tooltip extends View { + constructor(options) { + let { rootElement } = options; + let timeout; + options.classNames = ['__mobiledoc-tooltip']; + super(options); + + this.addEventListener(rootElement, 'mouseover', (e) => { + let target = getEventTargetMatchingTag(options.showForTag, e.target, rootElement); + if (target && target.isContentEditable) { + timeout = setTimeout(() => { + this.showLink(target.href, target); + }, DELAY); + } + }); + + this.addEventListener(rootElement, 'mouseout', (e) => { + clearTimeout(timeout); + if (this.elementObserver) { this.elementObserver.cancel(); } + let toElement = e.toElement || e.relatedTarget; + if (toElement && toElement.className !== this.element.className) { + this.hide(); + } + }); + } + + showMessage(message, element) { + let tooltipElement = this.element; + tooltipElement.innerHTML = message; + this.show(); + positionElementCenteredBelow(tooltipElement, element); + } + + showLink(link, element) { + let message = `${link}`; + this.showMessage(message, element); + this.elementObserver = whenElementIsNotInDOM(element, () => this.hide()); + } +} + +var errorProps = [ + 'description', + 'fileName', + 'lineNumber', + 'message', + 'name', + 'number', + 'stack' +]; + +function MobiledocError() { + let tmp = Error.apply(this, arguments); + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. + for (let idx = 0; idx < errorProps.length; idx++) { + this[errorProps[idx]] = tmp[errorProps[idx]]; + } +} + +MobiledocError.prototype = Object.create(Error.prototype); + +function assert(message, conditional) { + if (!conditional) { + throw new MobiledocError(message); + } +} + +const MARKUP_SECTION_TYPE = 'markup-section'; +const LIST_SECTION_TYPE = 'list-section'; +const MARKUP_TYPE = 'markup'; +const MARKER_TYPE = 'marker'; +const POST_TYPE = 'post'; +const LIST_ITEM_TYPE = 'list-item'; +const CARD_TYPE = 'card-section'; +const IMAGE_SECTION_TYPE = 'image-section'; +const ATOM_TYPE = 'atom'; + +const CONSTRUCTOR_FN_NAME = 'constructor'; + +function mixin(target, source) { + target = target.prototype; + // Fallback to just `source` to allow mixing in a plain object (pojo) + source = source.prototype || source; + + Object.getOwnPropertyNames(source).forEach((name) => { + if (name !== CONSTRUCTOR_FN_NAME) { + const descriptor = Object.getOwnPropertyDescriptor(source, name); + + Object.defineProperty(target, name, descriptor); + } + }); +} + +class Markerupable { + + clearMarkups() { + this.markups = []; + } + + addMarkup(markup) { + this.markups.push(markup); + } + + addMarkupAtIndex(markup, index) { + this.markups.splice(index, 0, markup); + } + + removeMarkup(markupOrMarkupCallback) { + let callback; + if (typeof markupOrMarkupCallback === 'function') { + callback = markupOrMarkupCallback; + } else { + let markup = markupOrMarkupCallback; + callback = (_markup) => _markup === markup; + } + + forEach( + filter(this.markups, callback), + m => this._removeMarkup(m) + ); + } + + _removeMarkup(markup) { + const index = this.markups.indexOf(markup); + if (index !== -1) { + this.markups.splice(index, 1); + } + } + + hasMarkup(tagNameOrMarkup) { + return !!this.getMarkup(tagNameOrMarkup); + } + + getMarkup(tagNameOrMarkup) { + if (typeof tagNameOrMarkup === 'string') { + let tagName = normalizeTagName(tagNameOrMarkup); + return detect(this.markups, markup => markup.tagName === tagName); + } else { + let targetMarkup = tagNameOrMarkup; + return detect(this.markups, markup => markup === targetMarkup); + } + } + + get openedMarkups() { + let count = 0; + if (this.prev) { + count = commonItemLength(this.markups, this.prev.markups); + } + + return this.markups.slice(count); + } + + get closedMarkups() { + let count = 0; + if (this.next) { + count = commonItemLength(this.markups, this.next.markups); + } + + return this.markups.slice(count); + } +} + +class LinkedItem { + constructor() { + this.next = null; + this.prev = null; + } +} + +// Unicode uses a pair of "surrogate" characters" (a high- and low-surrogate) +// to encode characters outside the basic multilingual plane (like emoji and +// some languages). +// These values are the unicode code points for the start and end of the +// high- and low-surrogate characters. +// See "high surrogate" and "low surrogate" on +// https://en.wikipedia.org/wiki/Unicode_block +const HIGH_SURROGATE_RANGE = [0xD800, 0xDBFF]; +const LOW_SURROGATE_RANGE = [0xDC00, 0xDFFF]; + +const Marker = class Marker extends LinkedItem { + constructor(value='', markups=[]) { + super(); + this.value = value; + assert('Marker must have value', value !== undefined && value !== null); + this.markups = []; + this.type = MARKER_TYPE; + this.isMarker = true; + this.isAtom = false; + markups.forEach(m => this.addMarkup(m)); + } + + clone() { + const clonedMarkups = this.markups.slice(); + return this.builder.createMarker(this.value, clonedMarkups); + } + + get isEmpty() { + return this.isBlank; + } + + get isBlank() { + return this.length === 0; + } + + charAt(offset) { + return this.value.slice(offset, offset+1); + } + + /** + * A marker's text is equal to its value. + * Compare with an Atom which distinguishes between text and value + */ + get text() { + return this.value; + } + + get length() { + return this.value.length; + } + + // delete the character at this offset, + // update the value with the new value + deleteValueAtOffset(offset) { + assert('Cannot delete value at offset outside bounds', + offset >= 0 && offset <= this.length); + + let width = 1; + let code = this.value.charCodeAt(offset); + if (code >= HIGH_SURROGATE_RANGE[0] && code <= HIGH_SURROGATE_RANGE[1]) { + width = 2; + } else if (code >= LOW_SURROGATE_RANGE[0] && code <= LOW_SURROGATE_RANGE[1]) { + width = 2; + offset = offset - 1; + } + + const [ left, right ] = [ + this.value.slice(0, offset), + this.value.slice(offset+width) + ]; + this.value = left + right; + + return width; + } + + canJoin(other) { + return other && other.isMarker && isArrayEqual(this.markups, other.markups); + } + + textUntil(offset) { + return this.value.slice(0, offset); + } + + split(offset=0, endOffset=this.length) { + let markers = [ + this.builder.createMarker(this.value.substring(0, offset)), + this.builder.createMarker(this.value.substring(offset, endOffset)), + this.builder.createMarker(this.value.substring(endOffset)) + ]; + + this.markups.forEach(mu => markers.forEach(m => m.addMarkup(mu))); + return markers; + } + + /** + * @return {Array} 2 markers either or both of which could be blank + */ + splitAtOffset(offset) { + assert('Cannot split a marker at an offset > its length', + offset <= this.length); + let { value, builder } = this; + + let pre = builder.createMarker(value.substring(0, offset)); + let post = builder.createMarker(value.substring(offset)); + + this.markups.forEach(markup => { + pre.addMarkup(markup); + post.addMarkup(markup); + }); + + return [pre, post]; + } + +}; + +mixin(Marker, Markerupable); + +var Keycodes = { + BACKSPACE: 8, + SPACE: 32, + ENTER: 13, + SHIFT: 16, + ESC: 27, + DELETE: 46, + '0': 48, + '9': 57, + A: 65, + Z: 90, + a: 97, + z: 122, + 'NUMPAD_0': 186, + 'NUMPAD_9': 111, + ';': 186, + '.': 190, + '`': 192, + '[': 219, + '"': 222, + + // Input Method Editor uses multiple keystrokes to display characters. + // Example on mac: press option-i then i. This fires 2 key events in Chrome + // with keyCode 229 and displays ˆ and then î. + // See http://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/keyCode-spec.html#fixed-virtual-key-codes + IME: 229, + + TAB: 9, + CLEAR: 12, + PAUSE: 19, + PAGEUP: 33, + PAGEDOWN: 34, + END: 35, + HOME: 36, + LEFT: 37, + UP: 38, + RIGHT: 39, + DOWN: 40, + INS: 45, + META: 91, + ALT: 18, + CTRL: 17 +}; + +var Keys = { + BACKSPACE: 'Backspace', + SPACE: ' ', + ENTER: 'Enter', + SHIFT: 'Shift', + ESC: 'Escape', + DELETE: 'Delete', + INS: 'Insert', + HOME: 'Home', + END: 'End', + PAGEUP: 'PageUp', + PAGEDOWN: 'PageDown', + CLEAR: 'Clear', + PAUSE: 'Pause', + TAB: 'Tab', + ALT: 'Alt', + CTRL: 'Control', + + LEFT: 'ArrowLeft', + RIGHT: 'ArrowRight', + UP: 'ArrowUp', + DOWN: 'ArrowDown' +}; + +const TAB = '\t'; +const ENTER = '\n'; + +/** + * @typedef Direction + * @enum {number} + * @property {number} FORWARD + * @property {number} BACKWARD + */ +const DIRECTION = { + FORWARD: 1, + BACKWARD: -1 +}; + +const MODIFIERS = { + META: 1, // also called "command" on OS X + CTRL: 2, + SHIFT: 4, + ALT: 8 // also called "option" on OS X +}; + +function modifierMask(event) { + let { + metaKey, shiftKey, ctrlKey, altKey + } = event; + let modVal = (val, modifier) => { + return (val && modifier) || 0; + }; + return modVal(metaKey, MODIFIERS.META) + + modVal(shiftKey, MODIFIERS.SHIFT) + + modVal(ctrlKey, MODIFIERS.CTRL) + + modVal(altKey, MODIFIERS.ALT); +} + +const SPECIAL_KEYS = { + BACKSPACE: Keycodes.BACKSPACE, + TAB: Keycodes.TAB, + ENTER: Keycodes.ENTER, + ESC: Keycodes.ESC, + SPACE: Keycodes.SPACE, + PAGEUP: Keycodes.PAGEUP, + PAGEDOWN: Keycodes.PAGEDOWN, + END: Keycodes.END, + HOME: Keycodes.HOME, + LEFT: Keycodes.LEFT, + UP: Keycodes.UP, + RIGHT: Keycodes.RIGHT, + DOWN: Keycodes.DOWN, + INS: Keycodes.INS, + DEL: Keycodes.DELETE +}; + +function specialCharacterToCode(specialCharacter) { + return SPECIAL_KEYS[specialCharacter]; +} + +// heuristic for determining if `event` is a key event +function isKeyEvent(event) { + return /^key/.test(event.type); +} + +/** + * An abstraction around a KeyEvent + * that key listeners in the editor can use + * to determine what sort of key was pressed + */ +const Key = class Key { + constructor(event) { + this.key = event.key; + this.keyCode = event.keyCode; + this.charCode = event.charCode; + this.event = event; + this.modifierMask = modifierMask(event); + } + + static fromEvent(event) { + assert('Must pass a Key event to Key.fromEvent', + event && isKeyEvent(event)); + return new Key(event); + } + + toString() { + if (this.isTab()) { return TAB; } + return String.fromCharCode(this.charCode); + } + + // See https://caniuse.com/#feat=keyboardevent-key for browser support. + isKeySupported() { + return this.key; + } + + isKey(identifier) { + if (this.isKeySupported()) { + assert(`Must define Keys.${identifier}.`, Keys[identifier]); + return this.key === Keys[identifier]; + } else { + assert(`Must define Keycodes.${identifier}.`, Keycodes[identifier]); + return this.keyCode === Keycodes[identifier]; + } + } + + isEscape() { + return this.isKey('ESC'); + } + + isDelete() { + return this.isKey('BACKSPACE') || this.isForwardDelete(); + } + + isForwardDelete() { + return this.isKey('DELETE'); + } + + isArrow() { + return this.isHorizontalArrow() || this.isVerticalArrow(); + } + + isHorizontalArrow() { + return this.isLeftArrow() || this.isRightArrow(); + } + + isHorizontalArrowWithoutModifiersOtherThanShift() { + return this.isHorizontalArrow() && + !(this.ctrlKey || this.metaKey || this.altKey); + } + + isVerticalArrow() { + return this.isKey('UP') || this.isKey('DOWN'); + } + + isLeftArrow() { + return this.isKey('LEFT'); + } + + isRightArrow() { + return this.isKey('RIGHT'); + } + + isHome() { + return this.isKey('HOME'); + } + + isEnd() { + return this.isKey('END'); + } + + isPageUp() { + return this.isKey('PAGEUP'); + } + + isPageDown() { + return this.isKey('PAGEDOWN'); + } + + isInsert() { + return this.isKey('INS'); + } + + isClear() { + return this.isKey('CLEAR'); + } + + isPause() { + return this.isKey('PAUSE'); + } + + isSpace() { + return this.isKey('SPACE'); + } + + // In Firefox, pressing ctrl-TAB will switch to another open browser tab, but + // it will also fire a keydown event for the tab+modifier (ctrl). This causes + // Mobiledoc to erroneously insert a tab character before FF switches to the + // new browser tab. Chrome doesn't fire this event so the issue doesn't + // arise there. Fix this by returning false when the TAB key event includes a + // modifier. + // See: https://github.com/bustle/mobiledoc-kit/issues/565 + isTab() { + return !this.hasAnyModifier() && this.isKey('TAB'); + } + + isEnter() { + return this.isKey('ENTER'); + } + + /* + * If the key is the actual shift key. This is false when the shift key + * is held down and the source `event` is not the shift key. + * @see {isShift} + * @return {bool} + */ + isShiftKey() { + return this.isKey('SHIFT'); + } + + /* + * If the key is the actual alt key (aka "option" on mac). This is false when the alt key + * is held down and the source `event` is not the alt key. + * @return {bool} + */ + isAltKey() { + return this.isKey('ALT'); + } + + /* + * If the key is the actual ctrl key. This is false when the ctrl key + * is held down and the source `event` is not the ctrl key. + * @return {bool} + */ + isCtrlKey() { + return this.isKey('CTRL'); + } + + isIME() { + // FIXME the IME action seems to get lost when we issue an + // `editor.deleteSelection` before it (in Chrome) + return this.keyCode === Keycodes.IME; + } + + get direction() { + switch (true) { + case this.isDelete(): + return this.isForwardDelete() ? DIRECTION.FORWARD : DIRECTION.BACKWARD; + case this.isHorizontalArrow(): + return this.isRightArrow() ? DIRECTION.FORWARD : DIRECTION.BACKWARD; + } + } + + /** + * If the shift key is depressed. + * For example, while holding down meta+shift, pressing the "v" + * key would result in an event whose `Key` had `isShift()` with a truthy value, + * because the shift key is down when pressing the "v". + * @see {isShiftKey} which checks if the key is actually the shift key itself. + * @return {bool} + */ + isShift() { + return this.shiftKey; + } + + hasModifier(modifier) { + return modifier & this.modifierMask; + } + + hasAnyModifier() { + return !!this.modifierMask; + } + + get ctrlKey() { + return MODIFIERS.CTRL & this.modifierMask; + } + + get metaKey() { + return MODIFIERS.META & this.modifierMask; + } + + get shiftKey() { + return MODIFIERS.SHIFT & this.modifierMask; + } + + get altKey() { + return MODIFIERS.ALT & this.modifierMask; + } + + isPrintableKey() { + return !( + this.isArrow() || + this.isHome() || this.isEnd() || + this.isPageUp() || this.isPageDown() || + this.isInsert() || this.isClear() || this.isPause() || + this.isEscape() + ); + } + + isNumberKey() { + if (this.isKeySupported()) { + return this.key >= '0' && this.key <= '9'; + } else { + const code = this.keyCode; + return (code >= Keycodes['0'] && code <= Keycodes['9']) || + (code >= Keycodes.NUMPAD_0 && code <= Keycodes.NUMPAD_9); // numpad keys + } + } + + isLetterKey() { + if (this.isKeySupported()) { + const key = this.key; + return (key >= 'a' && key <= 'z') || + (key >= 'A' && key <= 'Z'); + } else { + const code = this.keyCode; + return (code >= Keycodes.A && code <= Keycodes.Z) || + (code >= Keycodes.a && code <= Keycodes.z); + } + } + + isPunctuation() { + if (this.isKeySupported()) { + const key = this.key; + return (key >= ';' && key <= '`') || + (key >= '[' && key <= '"'); + } else { + const code = this.keyCode; + return (code >= Keycodes[';'] && code <= Keycodes['`']) || + (code >= Keycodes['['] && code <= Keycodes['"']); + } + } + + /** + * See https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/keyCode#Printable_keys_in_standard_position + * and http://stackoverflow.com/a/12467610/137784 + */ + isPrintable() { + if (this.ctrlKey || this.metaKey) { + return false; + } + + // Firefox calls keypress events for some keys that should not be printable + if (!this.isPrintableKey()) { + return false; + } + + return ( + this.keyCode !== 0 || + this.toString().length > 0 || + this.isNumberKey() || + this.isSpace() || + this.isTab() || + this.isEnter() || + this.isLetterKey() || + this.isPunctuation() || + this.isIME() + ); + } +}; + +function clearSelection() { + window.getSelection().removeAllRanges(); +} + +function textNodeRects(node) { + let range = document.createRange(); + range.setEnd(node, node.nodeValue.length); + range.setStart(node, 0); + return range.getClientRects(); +} + +function findOffsetInTextNode(node, coords) { + let len = node.nodeValue.length; + let range = document.createRange(); + for (let i = 0; i < len; i++) { + range.setEnd(node, i + 1); + range.setStart(node, i); + let rect = range.getBoundingClientRect(); + if (rect.top === rect.bottom) { + continue; + } + if (rect.left <= coords.left && rect.right >= coords.left && + rect.top <= coords.top && rect.bottom >= coords.top) { + return {node, offset: i + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0)}; + } + } + return {node, offset: 0}; +} + +/* + * @param {Object} coords with `top` and `left` + * @see https://github.com/ProseMirror/prosemirror/blob/4c22e3fe97d87a355a0534e25d65aaf0c0d83e57/src/edit/dompos.js + * @return {Object} {node, offset} + */ +/* eslint-disable complexity */ +function findOffsetInNode(node, coords) { + let closest, dyClosest = 1e8, coordsClosest, offset = 0; + for (let child = node.firstChild; child; child = child.nextSibling) { + let rects; + if (isElementNode(child)) { + rects = child.getClientRects(); + } else if (isTextNode(child)) { + rects = textNodeRects(child); + } else { + continue; + } + + for (let i = 0; i < rects.length; i++) { + let rect = rects[i]; + if (rect.left <= coords.left && rect.right >= coords.left) { + let dy = rect.top > coords.top ? rect.top - coords.top + : rect.bottom < coords.top ? coords.top - rect.bottom : 0; + if (dy < dyClosest) { + closest = child; + dyClosest = dy; + coordsClosest = dy ? {left: coords.left, top: rect.top} : coords; + if (isElementNode(child) && !child.firstChild) { + offset = i + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0); + } + continue; + } + } + if (!closest && + (coords.top >= rect.bottom || coords.top >= rect.top && coords.left >= rect.right)) { + offset = i + 1; + } + } + } + if (!closest) { + return {node, offset}; + } + if (isTextNode(closest)) { + return findOffsetInTextNode(closest, coordsClosest); + } + if (closest.firstChild) { + return findOffsetInNode(closest, coordsClosest); + } + return {node, offset}; +} +/* eslint-enable complexity */ + +function constrainNodeTo(node, parentNode, existingOffset) { + let compare = parentNode.compareDocumentPosition(node); + if (compare & Node.DOCUMENT_POSITION_CONTAINED_BY) { + // the node is inside parentNode, do nothing + return { node, offset: existingOffset}; + } else if (compare & Node.DOCUMENT_POSITION_CONTAINS) { + // the node contains parentNode. This shouldn't happen. + return { node, offset: existingOffset}; + } else if (compare & Node.DOCUMENT_POSITION_PRECEDING) { + // node is before parentNode. return start of deepest first child + let child = parentNode.firstChild; + while (child.firstChild) { + child = child.firstChild; + } + return { node: child, offset: 0}; + } else if (compare & Node.DOCUMENT_POSITION_FOLLOWING) { + // node is after parentNode. return end of deepest last child + let child = parentNode.lastChild; + while (child.lastChild) { + child = child.lastChild; + } + + let offset = isTextNode(child) ? child.textContent.length : 1; + return {node: child, offset}; + } else { + return { node, offset: existingOffset}; + } +} + +/* + * Returns a new selection that is constrained within parentNode. + * If the anchorNode or focusNode are outside the parentNode, they are replaced with the beginning + * or end of the parentNode's children + */ +function constrainSelectionTo(selection, parentNode) { + let { + node: anchorNode, + offset: anchorOffset + } = constrainNodeTo(selection.anchorNode, parentNode, selection.anchorOffset); + let { + node: focusNode, + offset: focusOffset + } = constrainNodeTo(selection.focusNode, parentNode, selection.focusOffset); + + return { anchorNode, anchorOffset, focusNode, focusOffset }; +} + +function comparePosition(selection) { + let { anchorNode, focusNode, anchorOffset, focusOffset } = selection; + let headNode, tailNode, headOffset, tailOffset, direction; + + const position = anchorNode.compareDocumentPosition(focusNode); + + // IE may select return focus and anchor nodes far up the DOM tree instead of + // picking the deepest, most specific possible node. For example in + // + //

abcdef
+ // + // with a cursor between c and d, IE might say the focusNode is
with + // an offset of 1. However the anchorNode for a selection might still be + // 2 if there was a selection. + // + // This code walks down the DOM tree until a good comparison of position can be + // made. + // + if (position & Node.DOCUMENT_POSITION_CONTAINS) { + if (focusOffset < focusNode.childNodes.length) { + focusNode = focusNode.childNodes[focusOffset]; + focusOffset = 0; + } else { + // This situation happens on IE when triple-clicking to select. + // Set the focus to the very last character inside the node. + while (focusNode.lastChild) { + focusNode = focusNode.lastChild; + } + focusOffset = focusNode.textContent.length; + } + + return comparePosition({ + focusNode, + focusOffset, + anchorNode, anchorOffset + }); + } else if (position & Node.DOCUMENT_POSITION_CONTAINED_BY) { + let offset = anchorOffset - 1; + if (offset < 0) { + offset = 0; + } + return comparePosition({ + anchorNode: anchorNode.childNodes[offset], + anchorOffset: 0, + focusNode, focusOffset + }); + // The meat of translating anchor and focus nodes to head and tail nodes + } else if (position & Node.DOCUMENT_POSITION_FOLLOWING) { + headNode = anchorNode; tailNode = focusNode; + headOffset = anchorOffset; tailOffset = focusOffset; + direction = DIRECTION.FORWARD; + } else if (position & Node.DOCUMENT_POSITION_PRECEDING) { + headNode = focusNode; tailNode = anchorNode; + headOffset = focusOffset; tailOffset = anchorOffset; + direction = DIRECTION.BACKWARD; + } else { // same node + headNode = tailNode = anchorNode; + headOffset = anchorOffset; + tailOffset = focusOffset; + if (tailOffset < headOffset) { + // Swap the offset order + headOffset = focusOffset; + tailOffset = anchorOffset; + direction = DIRECTION.BACKWARD; + } else if (headOffset < tailOffset) { + direction = DIRECTION.FORWARD; + } else { + direction = null; + } + } + + return {headNode, headOffset, tailNode, tailOffset, direction}; +} + +/** + * A logical range of a {@link Post}. + * Usually an instance of Range will be read from the {@link Editor#range} property, + * but it may be useful to instantiate a range directly when programmatically modifying a Post. + */ +class Range { + /** + * @param {Position} head + * @param {Position} [tail=head] + * @param {Direction} [direction=null] + * @return {Range} + * @private + */ + constructor(head, tail=head, direction=null) { + /** @property {Position} head */ + this.head = head; + + /** @property {Position} tail */ + this.tail = tail; + + /** @property {Direction} direction */ + this.direction = direction; + } + + /** + * Shorthand to create a new range from a section(s) and offset(s). + * When given only a head section and offset, creates a collapsed range. + * @param {Section} headSection + * @param {number} headOffset + * @param {Section} [tailSection=headSection] + * @param {number} [tailOffset=headOffset] + * @param {Direction} [direction=null] + * @return {Range} + */ + static create(headSection, headOffset, tailSection=headSection, tailOffset=headOffset, direction=null) { + return new Range( + new Position$1(headSection, headOffset), + new Position$1(tailSection, tailOffset), + direction + ); + } + + static blankRange() { + return new Range(Position$1.blankPosition(), Position$1.blankPosition()); + } + + /** + * @param {Markerable} section + * @return {Range} A range that is constrained to only the part that + * includes the section. + * FIXME -- if the section isn't the head or tail, it's assumed to be + * wholly contained. It's possible to call `trimTo` with a selection that is + * outside of the range, though, which would invalidate that assumption. + * There's no efficient way to determine if a section is within a range, yet. + * @private + */ + trimTo(section) { + const length = section.length; + + let headOffset = section === this.head.section ? + Math.min(this.head.offset, length) : 0; + let tailOffset = section === this.tail.section ? + Math.min(this.tail.offset, length) : length; + + return Range.create(section, headOffset, section, tailOffset); + } + + /** + * Expands the range 1 unit in the given direction + * If the range is expandable in the given direction, always returns a + * non-collapsed range. + * @param {Number} units If units is > 0, the range is extended to the right, + * otherwise range is extended to the left. + * @return {Range} + * @public + */ + extend(units) { + assert(`Must pass integer to Range#extend`, typeof units === 'number'); + + if (units === 0) { return this; } + + let { head, tail, direction: currentDirection } = this; + switch (currentDirection) { + case DIRECTION.FORWARD: + return new Range(head, tail.move(units), currentDirection); + case DIRECTION.BACKWARD: + return new Range(head.move(units), tail, currentDirection); + default: { + let newDirection = units > 0 ? DIRECTION.FORWARD : DIRECTION.BACKWARD; + return new Range(head, tail, newDirection).extend(units); + } + } + } + + /** + * Moves this range 1 unit in the given direction. + * If the range is collapsed, returns a collapsed range shifted by 1 unit, + * otherwise collapses this range to the position at the `direction` end of the range. + * Always returns a collapsed range. + * @param {Direction} direction + * @return {Range} + * @public + */ + move(direction) { + assert(`Must pass DIRECTION.FORWARD (${DIRECTION.FORWARD}) or DIRECTION.BACKWARD (${DIRECTION.BACKWARD}) to Range#move`, + direction === DIRECTION.FORWARD || direction === DIRECTION.BACKWARD); + + let { focusedPosition, isCollapsed } = this; + + if (isCollapsed) { + return new Range(focusedPosition.move(direction)); + } else { + return this._collapse(direction); + } + } + + /** + * expand a range to all markers matching a given check + * + * @param {Function} detectMarker + * @return {Range} The expanded range + * + * @public + */ + expandByMarker(detectMarker) { + let { + head, + tail, + direction + } = this; + let {section: headSection} = head; + if (headSection !== tail.section) { + throw new Error('#expandByMarker does not work across sections. Perhaps you should confirm the range is collapsed'); + } + + let firstNotMatchingDetect = i => { + return !detectMarker(i); + }; + + let headMarker = headSection.markers.detect(firstNotMatchingDetect, head.marker, true); + if (!headMarker && detectMarker(headSection.markers.head)) { + headMarker = headSection.markers.head; + } else { + headMarker = headMarker.next || head.marker; + } + let headPosition = new Position$1(headSection, headSection.offsetOfMarker(headMarker)); + + let tailMarker = tail.section.markers.detect(firstNotMatchingDetect, tail.marker); + if (!tailMarker && detectMarker(headSection.markers.tail)) { + tailMarker = headSection.markers.tail; + } else { + tailMarker = tailMarker.prev || tail.marker; + } + let tailPosition = new Position$1(tail.section, tail.section.offsetOfMarker(tailMarker) + tailMarker.length); + + return headPosition.toRange(tailPosition, direction); + } + + _collapse(direction) { + return new Range(direction === DIRECTION.BACKWARD ? this.head : this.tail); + } + + get focusedPosition() { + return this.direction === DIRECTION.BACKWARD ? this.head : this.tail; + } + + isEqual(other) { + return other && + this.head.isEqual(other.head) && + this.tail.isEqual(other.tail); + } + + get isBlank() { + return this.head.isBlank && this.tail.isBlank; + } + + // "legacy" APIs + get headSection() { + return this.head.section; + } + get tailSection() { + return this.tail.section; + } + get headSectionOffset() { + return this.head.offset; + } + get tailSectionOffset() { + return this.tail.offset; + } + get isCollapsed() { + return this.head.isEqual(this.tail); + } + get headMarker() { + return this.head.marker; + } + get tailMarker() { + return this.tail.marker; + } + get headMarkerOffset() { + return this.head.offsetInMarker; + } + get tailMarkerOffset() { + return this.tail.offsetInMarker; + } +} + +const { FORWARD, BACKWARD } = DIRECTION; + +// generated via http://xregexp.com/ to cover chars that \w misses +// (new XRegExp('\\p{Alphabetic}|[0-9]|_|:')).toString() +const WORD_CHAR_REGEX = /[A-Za-zªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͅͰ-ʹͶͷͺ-ͽͿΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙա-ևְ-ׇֽֿׁׂׅׄא-תװ-ײؐ-ؚؠ-ٗٙ-ٟٮ-ۓە-ۜۡ-ۭۨ-ۯۺ-ۼۿܐ-ܿݍ-ޱߊ-ߪߴߵߺࠀ-ࠗࠚ-ࠬࡀ-ࡘࢠ-ࢴࣣ-ࣰࣩ-ऻऽ-ौॎ-ॐॕ-ॣॱ-ঃঅ-ঌএঐও-নপ-রলশ-হঽ-ৄেৈোৌৎৗড়ঢ়য়-ৣৰৱਁ-ਃਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਾ-ੂੇੈੋੌੑਖ਼-ੜਫ਼ੰ-ੵઁ-ઃઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽ-ૅે-ૉોૌૐૠ-ૣૹଁ-ଃଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽ-ୄେୈୋୌୖୗଡ଼ଢ଼ୟ-ୣୱஂஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹா-ூெ-ைொ-ௌௐௗఀ-ఃఅ-ఌఎ-ఐఒ-నప-హఽ-ౄె-ైొ-ౌౕౖౘ-ౚౠ-ౣಁ-ಃಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ೄೆ-ೈೊ-ೌೕೖೞೠ-ೣೱೲഁ-ഃഅ-ഌഎ-ഐഒ-ഺഽ-ൄെ-ൈൊ-ൌൎൗൟ-ൣൺ-ൿංඃඅ-ඖක-නඳ-රලව-ෆා-ුූෘ-ෟෲෳก-ฺเ-ๆํກຂຄງຈຊຍດ-ທນ-ຟມ-ຣລວສຫອ-ູົ-ຽເ-ໄໆໍໜ-ໟༀཀ-ཇཉ-ཬཱ-ཱྀྈ-ྗྙ-ྼက-ံးျ-ဿၐ-ၢၥ-ၨၮ-ႆႎႜႝႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚ፟ᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛸᜀ-ᜌᜎ-ᜓᜠ-ᜳᝀ-ᝓᝠ-ᝬᝮ-ᝰᝲᝳក-ឳា-ៈៗៜᠠ-ᡷᢀ-ᢪᢰ-ᣵᤀ-ᤞᤠ-ᤫᤰ-ᤸᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨛᨠ-ᩞᩡ-ᩴᪧᬀ-ᬳᬵ-ᭃᭅ-ᭋᮀ-ᮩᮬ-ᮯᮺ-ᯥᯧ-ᯱᰀ-ᰵᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳳᳵᳶᴀ-ᶿᷧ-ᷴḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⒶ-ⓩⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⷠ-ⷿⸯ々-〇〡-〩〱-〵〸-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙴ-ꙻꙿ-ꛯꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠧꡀ-ꡳꢀ-ꣃꣲ-ꣷꣻꣽꤊ-ꤪꤰ-ꥒꥠ-ꥼꦀ-ꦲꦴ-ꦿꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨶꩀ-ꩍꩠ-ꩶꩺꩾ-ꪾꫀꫂꫛ-ꫝꫠ-ꫯꫲ-ꫵꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯪ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ]|[0-9]|_|:/; + +function findParentSectionFromNode(renderTree, node) { + let renderNode = renderTree.findRenderNodeFromElement( + node, + (renderNode) => renderNode.postNode.isSection + ); + + return renderNode && renderNode.postNode; +} + +function findOffsetInMarkerable(markerable, node, offset) { + let offsetInSection = 0; + let marker = markerable.markers.head; + while (marker) { + let markerNode = marker.renderNode.element; + if (markerNode === node) { + return offsetInSection + offset; + } else if (marker.isAtom) { + if (marker.renderNode.headTextNode === node) { + return offsetInSection; + } else if (marker.renderNode.tailTextNode === node) { + return offsetInSection + 1; + } + } + + offsetInSection += marker.length; + marker = marker.next; + } + + return offsetInSection; +} + +function findOffsetInSection(section, node, offset) { + if (section.isMarkerable) { + return findOffsetInMarkerable(section, node, offset); + } else { + assert('findOffsetInSection must be called with markerable or card section', + section.isCardSection); + + let wrapperNode = section.renderNode.element; + let endTextNode = wrapperNode.lastChild; + if (node === endTextNode) { + return 1; + } + return 0; + } +} + +let Position, BlankPosition; + +Position = class Position { + /** + * A position is a logical location (zero-width, or "collapsed") in a post, + * typically between two characters in a section. + * Two positions (a head and a tail) make up a {@link Range}. + * @constructor + */ + constructor(section, offset=0, isBlank=false) { + if (!isBlank) { + assert('Position must have a section that is addressable by the cursor', + (section && section.isLeafSection)); + assert('Position must have numeric offset', + (typeof offset === 'number')); + } + + this.section = section; + this.offset = offset; + this.isBlank = isBlank; + } + + /** + * @param {integer} x x-position in current viewport + * @param {integer} y y-position in current viewport + * @param {Editor} editor + * @return {Position|null} + */ + static atPoint(x, y, editor) { + let { _renderTree, element: rootElement } = editor; + let elementFromPoint = document.elementFromPoint(x, y); + if (!containsNode(rootElement, elementFromPoint)) { + return; + } + + let { node, offset } = findOffsetInNode(elementFromPoint, {left: x, top: y}); + return Position.fromNode(_renderTree, node, offset); + } + + static blankPosition() { + return new BlankPosition(); + } + + /** + * Returns a range from this position to the given tail. If no explicit + * tail is given this returns a collapsed range focused on this position. + * @param {Position} [tail=this] The ending position + * @return {Range} + * @public + */ + toRange(tail=this, direction=null) { + return new Range(this, tail, direction); + } + + get leafSectionIndex() { + let post = this.section.post; + let leafSectionIndex; + post.walkAllLeafSections((section, index) => { + if (section === this.section) { + leafSectionIndex = index; + } + }); + return leafSectionIndex; + } + + get isMarkerable() { + return this.section && this.section.isMarkerable; + } + + /** + * Returns the marker at this position, in the backward direction + * (i.e., the marker to the left of the cursor if the cursor is on a marker boundary and text is left-to-right) + * @return {Marker|undefined} + */ + get marker() { + return this.isMarkerable && this.markerPosition.marker; + } + + /** + * Returns the marker in `direction` from this position. + * If the position is in the middle of a marker, the direction is irrelevant. + * Otherwise, if the position is at a boundary between two markers, returns the + * marker to the left if `direction` === BACKWARD and the marker to the right + * if `direction` === FORWARD (assuming left-to-right text direction). + * @param {Direction} + * @return {Marker|undefined} + */ + markerIn(direction) { + if (!this.isMarkerable) { return; } + + let { marker, offsetInMarker } = this; + if (!marker) { return; } + + if (offsetInMarker > 0 && offsetInMarker < marker.length) { + return marker; + } else if (offsetInMarker === 0) { + return direction === BACKWARD ? marker : marker.prev; + } else if (offsetInMarker === marker.length) { + return direction === FORWARD ? marker.next : marker; + } + } + + get offsetInMarker() { + return this.markerPosition.offset; + } + + isEqual(position) { + return this.section === position.section && + this.offset === position.offset; + } + + /** + * @return {Boolean} If this position is at the head of the post + */ + isHeadOfPost() { + return this.move(BACKWARD).isEqual(this); + } + + /** + * @return {Boolean} If this position is at the tail of the post + */ + isTailOfPost() { + return this.move(FORWARD).isEqual(this); + } + + /** + * @return {Boolean} If this position is at the head of its section + */ + isHead() { + return this.isEqual(this.section.headPosition()); + } + + /** + * @return {Boolean} If this position is at the tail of its section + */ + isTail() { + return this.isEqual(this.section.tailPosition()); + } + + /** + * Move the position 1 unit in `direction`. + * + * @param {Number} units to move. > 0 moves right, < 0 moves left + * @return {Position} Return a new position one unit in the given + * direction. If the position is moving left and at the beginning of the post, + * the same position will be returned. Same if the position is moving right and + * at the end of the post. + */ + move(units) { + assert('Must pass integer to Position#move', typeof units === 'number'); + + if (units < 0) { + return this.moveLeft().move(++units); + } else if (units > 0) { + return this.moveRight().move(--units); + } else { + return this; + } + } + + /** + * @param {Number} direction (FORWARD or BACKWARD) + * @return {Position} The result of moving 1 "word" unit in `direction` + */ + moveWord(direction) { + let isPostBoundary = direction === BACKWARD ? this.isHeadOfPost() : this.isTailOfPost(); + if (isPostBoundary) { + return this; + } + + if (!this.isMarkerable) { + return this.move(direction); + } + + let pos = this; + + // Helper fn to check if the pos is at the `dir` boundary of its section + let isBoundary = (pos, dir) => { + return dir === BACKWARD ? pos.isHead() : pos.isTail(); + }; + // Get the char at this position (looking forward/right) + let getChar = (pos) => { + let { marker, offsetInMarker } = pos; + return marker.charAt(offsetInMarker); + }; + // Get the char in `dir` at this position + let peekChar = (pos, dir) => { + return dir === BACKWARD ? getChar(pos.move(BACKWARD)) : getChar(pos); + }; + // Whether there is an atom in `dir` from this position + let isAtom = (pos, dir) => { + // Special case when position is at end, the marker associated with it is + // the marker to its left. Normally `pos#marker` is the marker to the right of the pos's offset. + if (dir === BACKWARD && pos.isTail() && pos.marker.isAtom) { + return true; + } + return dir === BACKWARD ? pos.move(BACKWARD).marker.isAtom : pos.marker.isAtom; + }; + + if (isBoundary(pos, direction)) { + // extend movement into prev/next section + return pos.move(direction).moveWord(direction); + } + + let seekWord = (pos) => { + return !isBoundary(pos, direction) && + !isAtom(pos, direction) && + !WORD_CHAR_REGEX.test(peekChar(pos, direction)); + }; + + // move(dir) while we are seeking the first word char + while (seekWord(pos)) { + pos = pos.move(direction); + } + + if (isAtom(pos, direction)) { + return pos.move(direction); + } + + let seekBoundary = (pos) => { + return !isBoundary(pos, direction) && + !isAtom(pos, direction) && + WORD_CHAR_REGEX.test(peekChar(pos, direction)); + }; + + // move(dir) while we are seeking the first boundary position + while (seekBoundary(pos)) { + pos = pos.move(direction); + } + + return pos; + } + + /** + * The position to the left of this position. + * If this position is the post's headPosition it returns itself. + * @return {Position} + * @private + */ + moveLeft() { + if (this.isHead()) { + let prev = this.section.previousLeafSection(); + return prev ? prev.tailPosition() : this; + } else { + let offset = this.offset - 1; + if (this.isMarkerable && this.marker) { + let code = this.marker.value.charCodeAt(offset); + if (code >= LOW_SURROGATE_RANGE[0] && code <= LOW_SURROGATE_RANGE[1]) { + offset = offset - 1; + } + } + return new Position(this.section, offset); + } + } + + /** + * The position to the right of this position. + * If this position is the post's tailPosition it returns itself. + * @return {Position} + * @private + */ + moveRight() { + if (this.isTail()) { + let next = this.section.nextLeafSection(); + return next ? next.headPosition() : this; + } else { + let offset = this.offset + 1; + if (this.isMarkerable && this.marker) { + let code = this.marker.value.charCodeAt(offset - 1); + if (code >= HIGH_SURROGATE_RANGE[0] && code <= HIGH_SURROGATE_RANGE[1]) { + offset = offset + 1; + } + } + return new Position(this.section, offset); + } + } + + static fromNode(renderTree, node, offset) { + if (isTextNode(node)) { + return Position.fromTextNode(renderTree, node, offset); + } else { + return Position.fromElementNode(renderTree, node, offset); + } + } + + static fromTextNode(renderTree, textNode, offsetInNode) { + const renderNode = renderTree.getElementRenderNode(textNode); + let section, offsetInSection; + + if (renderNode) { + const marker = renderNode.postNode; + section = marker.section; + + assert(`Could not find parent section for mapped text node "${textNode.textContent}"`, + !!section); + offsetInSection = section.offsetOfMarker(marker, offsetInNode); + } else { + // all text nodes should be rendered by markers except: + // * text nodes inside cards + // * text nodes created by the browser during text input + // both of these should have rendered parent sections, though + section = findParentSectionFromNode(renderTree, textNode); + assert(`Could not find parent section for un-mapped text node "${textNode.textContent}"`, + !!section); + + offsetInSection = findOffsetInSection(section, textNode, offsetInNode); + } + + return new Position(section, offsetInSection); + } + + static fromElementNode(renderTree, elementNode, offset) { + let position; + + // The browser may change the reported selection to equal the editor's root + // element if the user clicks an element that is immediately removed, + // which can happen when clicking to remove a card. + if (elementNode === renderTree.rootElement) { + let post = renderTree.rootNode.postNode; + position = offset === 0 ? post.headPosition() : post.tailPosition(); + } else { + let section = findParentSectionFromNode(renderTree, elementNode); + assert('Could not find parent section from element node', !!section); + + if (section.isCardSection) { + // Selections in cards are usually made on a text node + // containing a ‌ on one side or the other of the card but + // some scenarios (Firefox) will result in selecting the + // card's wrapper div. If the offset is 2 we've selected + // the final zwnj and should consider the cursor at the + // end of the card (offset 1). Otherwise, the cursor is at + // the start of the card + position = offset < 2 ? section.headPosition() : section.tailPosition(); + } else { + + // In Firefox it is possible for the cursor to be on an atom's wrapper + // element. (In Chrome/Safari, the browser corrects this to be on + // one of the text nodes surrounding the wrapper). + // This code corrects for when the browser reports the cursor position + // to be on the wrapper element itself + let renderNode = renderTree.getElementRenderNode(elementNode); + let postNode = renderNode && renderNode.postNode; + if (postNode && postNode.isAtom) { + let sectionOffset = section.offsetOfMarker(postNode); + if (offset > 1) { + // we are on the tail side of the atom + sectionOffset += postNode.length; + } + position = new Position(section, sectionOffset); + } else if (offset >= elementNode.childNodes.length) { + + // This is to deal with how Firefox handles triple-click selections. + // See https://stackoverflow.com/a/21234837/1269194 for an + // explanation. + position = section.tailPosition(); + } else { + // The offset is 0 if the cursor is on a non-atom-wrapper element node + // (e.g., a
tag in a blank markup section) + position = section.headPosition(); + } + } + } + + return position; + } + + /** + * @private + */ + get markerPosition() { + assert('Cannot get markerPosition without a section', !!this.section); + assert('cannot get markerPosition of a non-markerable', !!this.section.isMarkerable); + return this.section.markerPositionAtOffset(this.offset); + } +}; + +BlankPosition = class BlankPosition extends Position { + constructor() { + super(null, 0, true); + } + + isEqual(other) { + return other && other.isBlank; + } + + toRange() { return Range.blankRange(); } + get leafSectionIndex() { assert('must implement get leafSectionIndex', false); } + + get isMarkerable() { return false; } + get marker() { return false; } + isHeadOfPost() { return false; } + isTailOfPost() { return false; } + isHead() { return false; } + isTail() { return false; } + move() { return this; } + moveWord() { return this; } + + get markerPosition() { return {}; } +}; + +var Position$1 = Position; + +class LifecycleCallbacks { + constructor(queueNames=[]) { + this.callbackQueues = {}; + this.removalQueues = {}; + + queueNames.forEach(name => { + this.callbackQueues[name] = []; + this.removalQueues[name] = []; + }); + } + + runCallbacks(queueName, args=[]) { + let queue = this._getQueue(queueName); + queue.forEach(cb => cb(...args)); + + let toRemove = this.removalQueues[queueName]; + toRemove.forEach(cb => { + let index = queue.indexOf(cb); + if (index !== -1) { + queue.splice(index, 1); + } + }); + + this.removalQueues[queueName] = []; + } + + addCallback(queueName, callback) { + this._getQueue(queueName).push(callback); + } + + _scheduleCallbackForRemoval(queueName, callback) { + this.removalQueues[queueName].push(callback); + } + + addCallbackOnce(queueName, callback) { + let queue = this._getQueue(queueName); + if (queue.indexOf(callback) === -1) { + queue.push(callback); + this._scheduleCallbackForRemoval(queueName, callback); + } + } + + _getQueue(queueName) { + let queue = this.callbackQueues[queueName]; + assert(`No queue found for "${queueName}"`, !!queue); + return queue; + } +} + +const MARKERABLE = 'markerable', + NESTED_MARKERABLE = 'nested_markerable', + NON_MARKERABLE = 'non_markerable'; + +class Visitor { + constructor(inserter, cursorPosition) { + let { postEditor, post } = inserter; + this.postEditor = postEditor; + this._post = post; + this.cursorPosition = cursorPosition; + this.builder = this.postEditor.builder; + + this._hasInsertedFirstLeafSection = false; + } + + get cursorPosition() { + return this._cursorPosition; + } + + set cursorPosition(position) { + this._cursorPosition = position; + this.postEditor.setRange(position); + } + + visit(node) { + let method = node.type; + assert(`Cannot visit node of type ${node.type}`, !!this[method]); + this[method](node); + } + + _canMergeSection(section) { + if (this._hasInsertedFirstLeafSection) { + return false; + } else { + return this._isMarkerable && section.isMarkerable; + } + } + + get _isMarkerable() { + return this.cursorSection.isMarkerable; + } + + get cursorSection() { + return this.cursorPosition.section; + } + + get cursorOffset() { + return this.cursorPosition.offset; + } + + get _isNested() { + return this.cursorSection.isNested; + } + + [POST_TYPE](node) { + if (this.cursorSection.isBlank && !this._isNested) { + // replace blank section with entire post + let newSections = node.sections.map(s => s.clone()); + this._replaceSection(this.cursorSection, newSections); + } else { + node.sections.forEach(section => this.visit(section)); + } + } + + [MARKUP_SECTION_TYPE](node) { + this[MARKERABLE](node); + } + + [LIST_SECTION_TYPE](node) { + let hasNext = !!node.next; + node.items.forEach(item => this.visit(item)); + + if (this._isNested && hasNext) { + this._breakNestedAtCursor(); + } + } + + [LIST_ITEM_TYPE](node) { + this[NESTED_MARKERABLE](node); + } + + [CARD_TYPE](node) { + this[NON_MARKERABLE](node); + } + + [IMAGE_SECTION_TYPE](node) { + this[NON_MARKERABLE](node); + } + + [NON_MARKERABLE](section) { + if (this._isNested) { + this._breakNestedAtCursor(); + } else if (!this.cursorSection.isBlank) { + this._breakAtCursor(); + } + + this._insertLeafSection(section); + } + + [MARKERABLE](section) { + if (this._canMergeSection(section)) { + this._mergeSection(section); + } else if (this._isNested && this._isMarkerable) { + // If we are attaching a markerable section to a list item, + // insert a linebreak then merge the section onto the resulting blank list item + this._breakAtCursor(); + + // Advance the cursor to the head of the blank list item + let nextPosition = this.cursorSection.next.headPosition(); + this.cursorPosition = nextPosition; + + // Merge this section onto the list item + this._mergeSection(section); + } else { + this._breakAtCursor(); + this._insertLeafSection(section); + } + } + + [NESTED_MARKERABLE](section) { + if (this._canMergeSection(section)) { + this._mergeSection(section); + return; + } + + section = this._isNested ? section : this._wrapNestedSection(section); + this._breakAtCursor(); + this._insertLeafSection(section); + } + + // break out of a nested cursor position + _breakNestedAtCursor() { + assert('Cannot call _breakNestedAtCursor if not nested', this._isNested); + + let parent = this.cursorSection.parent; + let cursorAtEndOfList = this.cursorPosition.isEqual(parent.tailPosition()); + + if (cursorAtEndOfList) { + let blank = this.builder.createMarkupSection(); + this._insertSectionAfter(blank, parent); + } else { + let [, blank,] = this._breakListAtCursor(); + this.cursorPosition = blank.tailPosition(); + } + } + + _breakListAtCursor() { + assert('Cannot _splitParentSection if cursor position is not nested', + this._isNested); + + let list = this.cursorSection.parent, + position = this.cursorPosition, + blank = this.builder.createMarkupSection(); + let [pre, post] = this.postEditor._splitListAtPosition(list, position); + + let collection = this._post.sections, + reference = post; + this.postEditor.insertSectionBefore(collection, blank, reference); + return [pre, blank, post]; + } + + _wrapNestedSection(section) { + let tagName = section.parent.tagName; + let parent = this.builder.createListSection(tagName); + parent.items.append(section.clone()); + return parent; + } + + _mergeSection(section) { + assert('Can only merge markerable sections', + this._isMarkerable && section.isMarkerable); + this._hasInsertedFirstLeafSection = true; + + let markers = section.markers.map(m => m.clone()); + let position = this.postEditor.insertMarkers(this.cursorPosition, markers); + + this.cursorPosition = position; + } + + // Can be called to add a line break when in a nested section or a parent + // section. + _breakAtCursor() { + if (this.cursorSection.isBlank) { + return; + } else if (this._isMarkerable) { + this._breakMarkerableAtCursor(); + } else { + this._breakNonMarkerableAtCursor(); + } + } + + // Inserts a blank section before/after the cursor, + // depending on cursor position. + _breakNonMarkerableAtCursor() { + let collection = this._post.sections, + blank = this.builder.createMarkupSection(), + reference = this.cursorPosition.isHead() ? this.cursorSection : + this.cursorSection.next; + this.postEditor.insertSectionBefore(collection, blank, reference); + this.cursorPosition = blank.tailPosition(); + } + + _breakMarkerableAtCursor() { + let [pre,] = + this.postEditor.splitSection(this.cursorPosition); + + this.cursorPosition = pre.tailPosition(); + } + + _replaceSection(section, newSections) { + assert('Cannot replace section that does not have parent.sections', + section.parent && section.parent.sections); + assert('Must pass enumerable to _replaceSection', !!newSections.forEach); + + let collection = section.parent.sections; + let reference = section.next; + this.postEditor.removeSection(section); + newSections.forEach(section => { + this.postEditor.insertSectionBefore(collection, section, reference); + }); + let lastSection = newSections[newSections.length - 1]; + + this.cursorPosition = lastSection.tailPosition(); + } + + _insertSectionBefore(section, reference) { + let collection = this.cursorSection.parent.sections; + this.postEditor.insertSectionBefore(collection, section, reference); + + this.cursorPosition = section.tailPosition(); + } + + // Insert a section after the parent section. + // E.g., add a markup section after a list section + _insertSectionAfter(section, parent) { + assert('Cannot _insertSectionAfter nested section', !parent.isNested); + let reference = parent.next; + let collection = this._post.sections; + this.postEditor.insertSectionBefore(collection, section, reference); + this.cursorPosition = section.tailPosition(); + } + + _insertLeafSection(section) { + assert('Can only _insertLeafSection when cursor is at end of section', + this.cursorPosition.isTail()); + + this._hasInsertedFirstLeafSection = true; + section = section.clone(); + + if (this.cursorSection.isBlank) { + assert('Cannot insert leaf non-markerable section when cursor is nested', + !(section.isMarkerable && this._isNested)); + this._replaceSection(this.cursorSection, [section]); + } else if (this.cursorSection.next && this.cursorSection.next.isBlank) { + this._replaceSection(this.cursorSection.next, [section]); + } else { + let reference = this.cursorSection.next; + this._insertSectionBefore(section, reference); + } + } +} + +class Inserter { + constructor(postEditor, post) { + this.postEditor = postEditor; + this.post = post; + } + + insert(cursorPosition, newPost) { + let visitor = new Visitor(this, cursorPosition); + if (!newPost.isBlank) { + visitor.visit(newPost); + } + return visitor.cursorPosition; + } +} + +/** + * Usage: + * Without a conditional, always prints deprecate message: + * `deprecate('This is deprecated')` + * + * Conditional deprecation, works similarly to `assert`, prints deprecation if + * conditional is false: + * `deprecate('Deprecated only if foo !== bar', foo === bar)` + */ +function deprecate(message, conditional=false) { + if (!conditional) { + // eslint-disable-next-line no-console + console.log(`[mobiledoc-kit] [DEPRECATED]: ${message}`); + } +} + +function toRange(rangeLike) { + assert(`Must pass non-blank object to "toRange"`, !!rangeLike); + + if (rangeLike instanceof Range) { + return rangeLike; + } else if (rangeLike instanceof Position$1) { + return rangeLike.toRange(); + } + + assert(`Incorrect structure for rangeLike: ${rangeLike}`, false); +} + +const { FORWARD: FORWARD$1, BACKWARD: BACKWARD$1 } = DIRECTION; + +function isListSectionTagName(tagName) { + return tagName === 'ul' || tagName === 'ol'; +} + +const CALLBACK_QUEUES = { + BEFORE_COMPLETE: 'beforeComplete', + COMPLETE: 'complete', + AFTER_COMPLETE: 'afterComplete' +}; + +// There are only two events that we're concerned about for Undo, that is inserting text and deleting content. +// These are the only two states that go on a "run" and create a combined undo, everything else has it's own +// deadicated undo. +const EDIT_ACTIONS = { + INSERT_TEXT: 1, + DELETE: 2 +}; + + +/** + * The PostEditor is used to modify a post. It should not be instantiated directly. + * Instead, a new instance of a PostEditor is created by the editor and passed + * as the argument to the callback in {@link Editor#run}. + * + * Usage: + * ``` + * editor.run((postEditor) => { + * // postEditor is an instance of PostEditor that can operate on the + * // editor's post + * }); + * ``` + */ +class PostEditor { + /** + * @private + */ + constructor(editor) { + this.editor = editor; + this.builder = this.editor.builder; + this._callbacks = new LifecycleCallbacks(values(CALLBACK_QUEUES)); + + this._didComplete = false; + this.editActionTaken = null; + + this._renderRange = () => this.editor.selectRange(this._range); + this._postDidChange = () => this.editor._postDidChange(); + this._rerender = () => this.editor.rerender(); + } + + addCallback(...args) { + this._callbacks.addCallback(...args); + } + + addCallbackOnce(...args) { + this._callbacks.addCallbackOnce(...args); + } + + runCallbacks(...args) { + this._callbacks.runCallbacks(...args); + } + + begin() { + // cache the editor's range + this._range = this.editor.range; + } + + /** + * Schedules to select the given range on the editor after the postEditor + * has completed its work. This also updates the postEditor's active range + * (so that multiple calls to range-changing methods on the postEditor will + * update the correct range). + * + * Usage: + * let range = editor.range; + * editor.run(postEditor => { + * let nextPosition = postEditor.deleteRange(range); + * + * // Will position the editor's cursor at `nextPosition` after + * // the postEditor finishes work and the editor rerenders. + * postEditor.setRange(nextPosition); + * }); + * @param {Range|Position} range + * @public + */ + setRange(range) { + range = toRange(range); + + // TODO validate that the range is valid + // (does not contain marked-for-removal head or tail sections?) + this._range = range; + this.scheduleAfterRender(this._renderRange, true); + } + + /** + * Delete a range from the post + * + * Usage: + * ``` + * let { range } = editor; + * editor.run((postEditor) => { + * let nextPosition = postEditor.deleteRange(range); + * postEditor.setRange(nextPosition); + * }); + * ``` + * @param {Range} range Cursor Range object with head and tail Positions + * @return {Position} The position where the cursor would go after deletion + * @public + */ + deleteRange(range) { + assert("Must pass MobiledocKit Range to `deleteRange`", range instanceof Range); + + this.editActionTaken = EDIT_ACTIONS.DELETE; + + let { + head, head: {section: headSection}, + tail, tail: {section: tailSection} + } = range; + + let { editor: { post } } = this; + + if (headSection === tailSection) { + return this.cutSection(headSection, head, tail); + } + + let nextSection = headSection.nextLeafSection(); + + let nextPos = this.cutSection(headSection, head, headSection.tailPosition()); + // cutSection can replace the section, so re-read headSection here + headSection = nextPos.section; + + // Remove sections in the middle of the range + while (nextSection !== tailSection) { + let tmp = nextSection; + nextSection = nextSection.nextLeafSection(); + this.removeSection(tmp); + } + + let tailPos = this.cutSection(tailSection, tailSection.headPosition(), tail); + // cutSection can replace the section, so re-read tailSection here + tailSection = tailPos.section; + + if (tailSection.isBlank) { + this.removeSection(tailSection); + } else { + // If head and tail sections are markerable, join them + // Note: They may not be the same section type. E.g. this may join + // a tail section that was a list item onto a markup section, or vice versa. + // (This is the desired behavior.) + if (headSection.isMarkerable && tailSection.isMarkerable) { + headSection.join(tailSection); + this._markDirty(headSection); + this.removeSection(tailSection); + } else if (headSection.isBlank) { + this.removeSection(headSection); + nextPos = tailPos; + } + } + + if (post.isBlank) { + post.sections.append(this.builder.createMarkupSection('p')); + nextPos = post.headPosition(); + } + + return nextPos; + } + + /** + * Note: This method may replace `section` with a different section. + * + * "Cut" out the part of the section inside `headOffset` and `tailOffset`. + * If section is markerable this splits markers that straddle the head or tail (if necessary), + * and removes markers that are wholly inside the offsets. + * If section is a card, this may replace it with a blank markup section if the + * positions contain the entire card. + * + * @param {Section} section + * @param {Position} head + * @param {Position} tail + * @return {Position} + * @private + */ + cutSection(section, head, tail) { + assert('Must pass head position and tail position to `cutSection`', + head instanceof Position$1 && tail instanceof Position$1); + assert('Must pass positions within same section to `cutSection`', + head.section === tail.section); + + if (section.isBlank || head.isEqual(tail)) { + return head; + } + if (section.isCardSection) { + if (head.isHead() && tail.isTail()) { + let newSection = this.builder.createMarkupSection(); + this.replaceSection(section, newSection); + return newSection.headPosition(); + } else { + return tail; + } + } + + let range = head.toRange(tail); + this.splitMarkers(range).forEach(m => this.removeMarker(m)); + + return head; + } + + _coalesceMarkers(section) { + if (section.isMarkerable) { + this._removeBlankMarkers(section); + this._joinSimilarMarkers(section); + } + } + + _removeBlankMarkers(section) { + forEach( + filter(section.markers, m => m.isBlank), + m => this.removeMarker(m) + ); + } + + // joins markers that have identical markups + _joinSimilarMarkers(section) { + let marker = section.markers.head; + let nextMarker; + while (marker && marker.next) { + nextMarker = marker.next; + + if (marker.canJoin(nextMarker)) { + nextMarker.value = marker.value + nextMarker.value; + this._markDirty(nextMarker); + this.removeMarker(marker); + } + + marker = nextMarker; + } + } + + removeMarker(marker) { + this._scheduleForRemoval(marker); + if (marker.section) { + this._markDirty(marker.section); + marker.section.markers.remove(marker); + } + } + + _scheduleForRemoval(postNode) { + if (postNode.renderNode) { + postNode.renderNode.scheduleForRemoval(); + + this.scheduleRerender(); + this.scheduleDidUpdate(); + } + let removedAdjacentToList = (postNode.prev && postNode.prev.isListSection) || + (postNode.next && postNode.next.isListSection); + if (removedAdjacentToList) { + this.addCallback( + CALLBACK_QUEUES.BEFORE_COMPLETE, + () => this._joinContiguousListSections() + ); + } + } + + _joinContiguousListSections() { + let { post } = this.editor; + let range = this._range; + let prev; + let groups = []; + let currentGroup; + + // FIXME do we need to force a re-render of the range if changed sections + // are contained within the range? + let updatedHead = null; + forEach(post.sections, section => { + if (prev && + prev.isListSection && + section.isListSection && + prev.tagName === section.tagName) { + + currentGroup = currentGroup || [prev]; + currentGroup.push(section); + } else { + if (currentGroup) { + groups.push(currentGroup); + } + currentGroup = null; + } + prev = section; + }); + + if (currentGroup) { + groups.push(currentGroup); + } + + forEach(groups, group => { + let list = group[0]; + forEach(group, listSection => { + if (listSection === list) { + return; + } + + let currentHead = range.head; + let prevPosition; + + // FIXME is there a currentHead if there is no range? + // is the current head a list item in the section + if (!range.isBlank && currentHead.section.isListItem && + currentHead.section.parent === listSection) { + prevPosition = list.tailPosition(); + } + this._joinListSections(list, listSection); + if (prevPosition) { + updatedHead = prevPosition.move(FORWARD$1); + } + }); + }); + + if (updatedHead) { + this.setRange(updatedHead); + } + } + + _joinListSections(baseList, nextList) { + baseList.join(nextList); + this._markDirty(baseList); + this.removeSection(nextList); + } + + _markDirty(postNode) { + if (postNode.renderNode) { + postNode.renderNode.markDirty(); + + this.scheduleRerender(); + this.scheduleDidUpdate(); + } + if (postNode.section) { + this._markDirty(postNode.section); + } + if (postNode.isMarkerable) { + this.addCallback( + CALLBACK_QUEUES.BEFORE_COMPLETE, () => this._coalesceMarkers(postNode)); + } + } + + /** + * @param {Position} position object with {section, offset} the marker and offset to delete from + * @param {Number} direction The direction to delete in (default is BACKWARD) + * @return {Position} for positioning the cursor + * @public + * @deprecated after v0.10.3 + */ + deleteFrom(position, direction=DIRECTION.BACKWARD) { + deprecate("`postEditor#deleteFrom is deprecated. Use `deleteAtPosition(position, direction=BACKWARD, {unit}={unit: 'char'})` instead"); + return this.deleteAtPosition(position, direction, {unit: 'char'}); + } + + /** + * Delete 1 `unit` (can be 'char' or 'word') in the given `direction` at the given + * `position`. In almost all cases this will be equivalent to deleting the range formed + * by expanding the position 1 unit in the given direction. The exception is when deleting + * backward from the beginning of a list item, which reverts the list item into a markup section + * instead of joining it with its previous list item (if any). + * + * Usage: + * + * let position = section.tailPosition(); + * // Section has text of "Howdy!" + * editor.run((postEditor) => { + * postEditor.deleteAtPosition(position); + * }); + * // section has text of "Howdy" + * + * @param {Position} position The position to delete at + * @param {Direction} [direction=DIRECTION.BACKWARD] direction The direction to delete in + * @param {Object} [options] + * @param {String} [options.unit="char"] The unit of deletion ("word" or "char") + * @return {Position} + */ + deleteAtPosition(position, direction=DIRECTION.BACKWARD, {unit}={unit: 'char'}) { + if (direction === DIRECTION.BACKWARD) { + return this._deleteAtPositionBackward(position, unit); + } else { + return this._deleteAtPositionForward(position, unit); + } + } + + _deleteAtPositionBackward(position, unit) { + if (position.isHead() && position.section.isListItem) { + this.toggleSection('p', position); + return this._range.head; + } else { + let prevPosition = unit === 'word' ? position.moveWord(BACKWARD$1) : position.move(BACKWARD$1); + let range = prevPosition.toRange(position); + return this.deleteRange(range); + } + } + + _deleteAtPositionForward(position, unit) { + let nextPosition = unit === 'word' ? position.moveWord(FORWARD$1) : position.move(FORWARD$1); + let range = position.toRange(nextPosition); + return this.deleteRange(range); + } + + /** + * Split markers at two positions, once at the head, and if necessary once + * at the tail. + * + * Usage: + * ``` + * let range = editor.range; + * editor.run((postEditor) => { + * postEditor.splitMarkers(range); + * }); + * ``` + * The return value will be marker object completely inside the offsets + * provided. Markers outside of the split may also have been modified. + * + * @param {Range} markerRange + * @return {Array} of markers that are inside the split + * @private + */ + splitMarkers(range) { + const { post } = this.editor; + const { head, tail } = range; + + this.splitSectionMarkerAtOffset(head.section, head.offset); + this.splitSectionMarkerAtOffset(tail.section, tail.offset); + + return post.markersContainedByRange(range); + } + + splitSectionMarkerAtOffset(section, offset) { + const edit = section.splitMarkerAtOffset(offset); + edit.removed.forEach(m => this.removeMarker(m)); + } + + /** + * Split the section at the position. + * + * Usage: + * ``` + * let position = editor.cursor.offsets.head; + * editor.run((postEditor) => { + * postEditor.splitSection(position); + * }); + * // Will result in the creation of two new sections + * // replacing the old one at the cursor position + * ``` + * The return value will be the two new sections. One or both of these + * sections can be blank (contain only a blank marker), for example if the + * headMarkerOffset is 0. + * + * @param {Position} position + * @return {Array} new sections, one for the first half and one for the second (either one can be null) + * @public + */ + splitSection(position) { + const { section } = position; + + if (section.isCardSection) { + return this._splitCardSection(section, position); + } else if (section.isListItem) { + let isLastAndBlank = section.isBlank && !section.next; + if (isLastAndBlank) { + // if is last, replace the item with a blank markup section + let parent = section.parent; + let collection = this.editor.post.sections; + let blank = this.builder.createMarkupSection(); + this.removeSection(section); + this.insertSectionBefore(collection, blank, parent.next); + + return [null, blank]; + } else { + let [pre, post] = this._splitListItem(section, position); + return [pre, post]; + } + } else { + let splitSections = section.splitAtPosition(position); + splitSections.forEach(s => this._coalesceMarkers(s)); + this._replaceSection(section, splitSections); + + return splitSections; + } + } + + /** + * @param {Section} cardSection + * @param {Position} position to split at + * @return {Section[]} 2-item array of pre and post-split sections + * @private + */ + _splitCardSection(cardSection, position) { + let { offset } = position; + assert('Cards section must be split at offset 0 or 1', + offset === 0 || offset === 1); + + let newSection = this.builder.createMarkupSection(); + let nextSection; + let surroundingSections; + + if (offset === 0) { + nextSection = cardSection; + surroundingSections = [newSection, cardSection]; + } else { + nextSection = cardSection.next; + surroundingSections = [cardSection, newSection]; + } + + let collection = this.editor.post.sections; + this.insertSectionBefore(collection, newSection, nextSection); + + return surroundingSections; + } + + /** + * @param {Section} section + * @param {Section} newSection + * @return null + * @public + */ + replaceSection(section, newSection) { + if (!section) { + // FIXME should a falsy section be a valid argument? + this.insertSectionBefore(this.editor.post.sections, newSection, null); + } else { + this._replaceSection(section, [newSection]); + } + } + + moveSectionBefore(collection, renderedSection, beforeSection) { + const newSection = renderedSection.clone(); + this.removeSection(renderedSection); + this.insertSectionBefore(collection, newSection, beforeSection); + return newSection; + } + + /** + * @param {Section} section A section that is already in DOM + * @public + */ + moveSectionUp(renderedSection) { + const isFirst = !renderedSection.prev; + if (isFirst) { + return renderedSection; + } + + const collection = renderedSection.parent.sections; + const beforeSection = renderedSection.prev; + return this.moveSectionBefore(collection, renderedSection, beforeSection); + } + + /** + * @param {Section} section A section that is already in DOM + * @public + */ + moveSectionDown(renderedSection) { + const isLast = !renderedSection.next; + if (isLast) { + return renderedSection; + } + + const beforeSection = renderedSection.next.next; + const collection = renderedSection.parent.sections; + return this.moveSectionBefore(collection, renderedSection, beforeSection); + } + + /** + * Insert an array of markers at the given position. If the position is in + * a non-markerable section (like a card section), this method throws an error. + * + * @param {Position} position + * @param {Marker[]} markers + * @return {Position} The position that represents the end of the inserted markers. + * @public + */ + insertMarkers(position, markers) { + let { section, offset } = position; + assert('Cannot insert markers at non-markerable position', + section.isMarkerable); + + this.editActionTaken = EDIT_ACTIONS.INSERT_TEXT; + + let edit = section.splitMarkerAtOffset(offset); + edit.removed.forEach(marker => this._scheduleForRemoval(marker)); + + let prevMarker = section.markerBeforeOffset(offset); + markers.forEach(marker => { + section.markers.insertAfter(marker, prevMarker); + offset += marker.length; + prevMarker = marker; + }); + + this._coalesceMarkers(section); + this._markDirty(section); + + let nextPosition = section.toPosition(offset); + this.setRange(nextPosition); + return nextPosition; + } + + /** + * Inserts text with the given markups, ignoring the existing markups at + * the position, if any. + * + * @param {Position} position + * @param {String} text + * @param {Markup[]} markups + * @return {Position} position at the end of the inserted text + */ + insertTextWithMarkup(position, text, markups=[]) { + let { section } = position; + if (!section.isMarkerable) { return; } + let marker = this.builder.createMarker(text, markups); + return this.insertMarkers(position, [marker]); + } + + /** + * Insert the text at the given position + * Inherits the markups already at that position, if any. + * + * @param {Position} position + * @param {String} text + * @return {Position} position at the end of the inserted text. + */ + insertText(position, text) { + let { section } = position; + if (!section.isMarkerable) { return; } + let markups = position.marker && position.marker.markups; + markups = markups || []; + return this.insertTextWithMarkup(position, text, markups); + } + + _replaceSection(section, newSections) { + let nextSection = section.next; + let collection = section.parent.sections; + + let nextNewSection = newSections[0]; + if (nextNewSection.isMarkupSection && section.isListItem) { + // put the new section after the ListSection (section.parent) + // instead of after the ListItem + collection = section.parent.parent.sections; + nextSection = section.parent.next; + } + + newSections.forEach(s => this.insertSectionBefore(collection, s, nextSection)); + this.removeSection(section); + } + + /** + * Given a markerRange (for example `editor.range`) mark all markers + * inside it as a given markup. The markup must be provided as a post + * abstract node. + * + * Usage: + * + * let range = editor.range; + * let strongMarkup = editor.builder.createMarkup('strong'); + * editor.run((postEditor) => { + * postEditor.addMarkupToRange(range, strongMarkup); + * }); + * // Will result some markers possibly being split, and the markup + * // being applied to all markers between the split. + * + * @param {Range} range + * @param {Markup} markup A markup post abstract node + * @public + */ + addMarkupToRange(range, markup) { + if (range.isCollapsed) { return; } + + let markers = this.splitMarkers(range); + if (markers.length) { + // We insert the new markup at a consistent index across the range. + // If we just push on the end of the list, it can end up in different positions + // of the markup stack. This results in unnecessary closing and re-opening of + // the markup each time it changes position. + // If we just push it at the beginning of the list, this causes unnecessary closing + // and re-opening of surrounding tags. + // So, we look for any tags open across the whole range, and push into the stack + // at the end of those. + // Prompted by https://github.com/bustle/mobiledoc-kit/issues/360 + + let markupsOpenAcrossRange = reduce(markers, function (soFar, marker) { + return commonItems(soFar, marker.markups); + }, markers[0].markups); + let indexToInsert = markupsOpenAcrossRange.length; + + markers.forEach(marker => { + marker.addMarkupAtIndex(markup, indexToInsert); + this._markDirty(marker); + }); + } + } + + /** + * Given a markerRange (for example `editor.range`) remove the given + * markup from all contained markers. + * + * Usage: + * ``` + * let { range } = editor; + * let markup = markerRange.headMarker.markups[0]; + * editor.run(postEditor => { + * postEditor.removeMarkupFromRange(range, markup); + * }); + * // Will result in some markers possibly being split, and the markup + * // being removed from all markers between the split. + * ``` + * @param {Range} range Object with offsets + * @param {Markup|Function} markupOrCallback A markup post abstract node or + * a function that returns true when passed a markup that should be removed + * @private + */ + removeMarkupFromRange(range, markupOrMarkupCallback) { + if (range.isCollapsed) { return; } + + this.splitMarkers(range).forEach(marker => { + marker.removeMarkup(markupOrMarkupCallback); + this._markDirty(marker); + }); + } + + /** + * Toggle the given markup in the given range (or at the position given). If the range/position + * has the markup, the markup will be removed. If nothing in the range/position + * has the markup, the markup will be added to everything in the range/position. + * + * Usage: + * ``` + * // Remove any 'strong' markup if it exists in the selection, otherwise + * // make it all 'strong' + * editor.run(postEditor => postEditor.toggleMarkup('strong')); + * + * // add/remove a link to 'bustle.com' to the selection + * editor.run(postEditor => { + * const linkMarkup = postEditor.builder.createMarkup('a', {href: 'http://bustle.com'}); + * postEditor.toggleMarkup(linkMarkup); + * }); + * ``` + * @param {Markup|String} markupOrString Either a markup object created using + * the builder (useful when adding a markup with attributes, like an 'a' markup), + * or, if a string, the tag name of the markup (e.g. 'strong', 'em') to toggle. + * @param {Range|Position} range in which to toggle. Defaults to current editor range. + * @public + */ + toggleMarkup(markupOrMarkupString, range=this._range) { + range = toRange(range); + const markup = typeof markupOrMarkupString === 'string' ? + this.builder.createMarkup(markupOrMarkupString) : + markupOrMarkupString; + + const hasMarkup = this.editor.detectMarkupInRange(range, markup.tagName); + // FIXME: This implies only a single markup in a range. This may not be + // true for links (which are not the same object instance like multiple + // strong tags would be). + if (hasMarkup) { + this.removeMarkupFromRange(range, hasMarkup); + } else { + this.addMarkupToRange(range, markup); + } + + this.setRange(range); + } + + /** + * Toggles the tagName of the active section or sections in the given range/position. + * If every section has the tag name, they will all be reset to default sections. + * Otherwise, every section will be changed to the requested type + * + * @param {String} sectionTagName A valid markup section or + * list section tag name (e.g. 'blockquote', 'h2', 'ul') + * @param {Range|Position} range The range over which to toggle. + * Defaults to the current editor range. + * @public + */ + toggleSection(sectionTagName, range=this._range) { + range = toRange(range); + + sectionTagName = normalizeTagName(sectionTagName); + let { post } = this.editor; + + let everySectionHasTagName = true; + post.walkMarkerableSections(range, section => { + if (!this._isSameSectionType(section, sectionTagName)) { + everySectionHasTagName = false; + } + }); + + let tagName = everySectionHasTagName ? 'p' : sectionTagName; + let sectionTransformations = []; + post.walkMarkerableSections(range, section => { + let changedSection = this.changeSectionTagName(section, tagName); + sectionTransformations.push({ + from: section, + to: changedSection + }); + }); + + let nextRange = this._determineNextRangeAfterToggleSection(range, sectionTransformations); + this.setRange(nextRange); + } + + _determineNextRangeAfterToggleSection(range, sectionTransformations) { + if (sectionTransformations.length) { + let changedHeadSection = detect(sectionTransformations, ({ from }) => { + return from === range.headSection; + }).to; + let changedTailSection = detect(sectionTransformations, ({ from }) => { + return from === range.tailSection; + }).to; + + if (changedHeadSection.isListSection || changedTailSection.isListSection) { + // We don't know to which ListItem's the original sections point at, so + // we don't have enough information to reconstruct the range when + // dealing with lists. + return sectionTransformations[0].to.headPosition().toRange(); + } else { + return Range.create( + changedHeadSection, + range.headSectionOffset, + changedTailSection, + range.tailSectionOffset, + range.direction + ); + } + } else { + return range; + } + } + + setAttribute(key, value, range=this._range) { + this._mutateAttribute(key, range, (section, attribute) => { + if (section.getAttribute(attribute) !== value) { + section.setAttribute(attribute, value); + return true; + } + }); + } + + removeAttribute(key, range=this._range) { + this._mutateAttribute(key, range, (section, attribute) => { + if (section.hasAttribute(attribute)) { + section.removeAttribute(attribute); + return true; + } + }); + } + + _mutateAttribute(key, range, cb) { + range = toRange(range); + let { post } = this.editor; + let attribute = `data-md-${key}`; + + post.walkMarkerableSections(range, section => { + if (section.isListItem) { + section = section.parent; + } + + if (cb(section, attribute) === true) { + this._markDirty(section); + } + }); + + this.setRange(range); + } + + _isSameSectionType(section, sectionTagName) { + return section.isListItem ? + section.parent.tagName === sectionTagName : + section.tagName === sectionTagName; + } + + /** + * @param {Markerable} section + * @private + */ + changeSectionTagName(section, newTagName) { + assert('Cannot pass non-markerable section to `changeSectionTagName`', + section.isMarkerable); + + if (isListSectionTagName(newTagName)) { + return this._changeSectionToListItem(section, newTagName); + } else if (section.isListItem) { + return this._changeSectionFromListItem(section, newTagName); + } else { + section.tagName = newTagName; + this._markDirty(section); + return section; + } + } + + /** + * Splits the item at the position given. + * If the position is at the start or end of the item, the pre- or post-item + * will contain a single empty ("") marker. + * @param {ListItem} item + * @param {Position} position + * @return {Array} the pre-item and post-item on either side of the split + * @private + */ + _splitListItem(item, position) { + let { section, offset } = position; + assert('Cannot split list item at position that does not include item', + item === section); + + item.splitMarkerAtOffset(offset); + let prevMarker = item.markerBeforeOffset(offset); + let preItem = this.builder.createListItem(), + postItem = this.builder.createListItem(); + + let currentItem = preItem; + item.markers.forEach(marker => { + currentItem.markers.append(marker.clone()); + if (marker === prevMarker) { + currentItem = postItem; + } + }); + this._replaceSection(item, [preItem, postItem]); + return [preItem, postItem]; + } + + /** + * Splits the list at the position given. + * @return {Array} pre-split list and post-split list, either of which could + * be blank (0-item list) if the position is at the start or end of the list. + * + * Note: Contiguous list sections will be joined in the before_complete queue + * of the postEditor. + * + * @private + */ + _splitListAtPosition(list, position) { + assert('Cannot split list at position not in list', + position.section.parent === list); + + let positionIsMiddle = !position.isHead() && !position.isTail(); + if (positionIsMiddle) { + let item = position.section; + let [pre,] = + this._splitListItem(item, position); + position = pre.tailPosition(); + } + + let preList = this.builder.createListSection(list.tagName); + let postList = this.builder.createListSection(list.tagName); + + let preItem = position.section; + let currentList = preList; + list.items.forEach(item => { + // If this item matches the start item and the position is at its start, + // it should be appended to the postList instead of the preList + if (item === preItem && position.isEqual(item.headPosition())) { + currentList = postList; + } + currentList.items.append(item.clone()); + // If we just appended the preItem, append the remaining items to the postList + if (item === preItem) { + currentList = postList; + } + }); + + this._replaceSection(list, [preList, postList]); + return [preList, postList]; + } + + /** + * @return Array of [prev, mid, next] lists. `prev` and `next` can + * be blank, depending on the position of `item`. `mid` will always + * be a 1-item list containing `item`. `prev` and `next` will be + * removed in the before_complete queue if they are blank + * (and still attached). + * + * @private + */ + _splitListAtItem(list, item) { + let next = list; + let prev = this.builder.createListSection(next.tagName, [], next.attributes); + let mid = this.builder.createListSection(next.tagName); + + let addToPrev = true; + // must turn the LinkedList into an array so that we can remove items + // as we iterate through it + let items = next.items.toArray(); + items.forEach(i => { + let listToAppend; + if (i === item) { + addToPrev = false; + listToAppend = mid; + } else if (addToPrev) { + listToAppend = prev; + } else { + return; // break after iterating prev and mid parts of the list + } + listToAppend.join(i); + this.removeSection(i); + }); + let found = !addToPrev; + assert('Cannot split list at item that is not present in the list', found); + + let collection = this.editor.post.sections; + this.insertSectionBefore(collection, mid, next); + this.insertSectionBefore(collection, prev, mid); + + // Remove possibly blank prev/next lists + this.addCallback(CALLBACK_QUEUES.BEFORE_COMPLETE, () => { + [prev, next].forEach(_list => { + let isAttached = !!_list.parent; + if (_list.isBlank && isAttached) { + this.removeSection(_list); + } + }); + }); + + return [prev, mid, next]; + } + + _changeSectionFromListItem(section, newTagName) { + assert('Must pass list item to `_changeSectionFromListItem`', + section.isListItem); + + let listSection = section.parent; + let markupSection = this.builder.createMarkupSection(newTagName); + markupSection.join(section); + + let [, mid,] = this._splitListAtItem(listSection, section); + this.replaceSection(mid, markupSection); + return markupSection; + } + + _changeSectionToListItem(section, newTagName) { + let isAlreadyCorrectListItem = section.isListItem && + section.parent.tagName === newTagName; + + if (isAlreadyCorrectListItem) { + return section; + } + + let listSection = this.builder.createListSection(newTagName); + listSection.join(section); + + let sectionToReplace; + if (section.isListItem) { + let [, mid,] = this._splitListAtItem(section.parent, section); + sectionToReplace = mid; + } else { + sectionToReplace = section; + } + this.replaceSection(sectionToReplace, listSection); + return listSection; + } + + /** + * Insert a given section before another one, updating the post abstract + * and the rendered UI. + * + * Usage: + * ``` + * let markerRange = editor.range; + * let sectionWithCursor = markerRange.headMarker.section; + * let section = editor.builder.createCardSection('my-image'); + * let collection = sectionWithCursor.parent.sections; + * editor.run((postEditor) => { + * postEditor.insertSectionBefore(collection, section, sectionWithCursor); + * }); + * ``` + * @param {LinkedList} collection The list of sections to insert into + * @param {Object} section The new section + * @param {Object} beforeSection Optional The section "before" is relative to, + * if falsy the new section will be appended to the collection + * @public + */ + insertSectionBefore(collection, section, beforeSection) { + collection.insertBefore(section, beforeSection); + this._markDirty(section.parent); + } + + /** + * Insert the given section after the current active section, or, if no + * section is active, at the end of the document. + * @param {Section} section + * @public + */ + insertSection(section) { + const activeSection = this.editor.activeSection; + const nextSection = activeSection && activeSection.next; + + const collection = this.editor.post.sections; + this.insertSectionBefore(collection, section, nextSection); + } + + /** + * Insert the given section at the end of the document. + * @param {Section} section + * @public + */ + insertSectionAtEnd(section) { + this.insertSectionBefore(this.editor.post.sections, section, null); + } + + /** + * Insert the `post` at the given position in the editor's post. + * @param {Position} position + * @param {Post} post + * @private + */ + insertPost(position, newPost) { + let post = this.editor.post; + let inserter = new Inserter(this, post); + let nextPosition = inserter.insert(position, newPost); + return nextPosition; + } + + /** + * Remove a given section from the post abstract and the rendered UI. + * + * Usage: + * ``` + * let { range } = editor; + * let sectionWithCursor = range.head.section; + * editor.run((postEditor) => { + * postEditor.removeSection(sectionWithCursor); + * }); + * ``` + * @param {Object} section The section to remove + * @public + */ + removeSection(section) { + let parent = section.parent; + this._scheduleForRemoval(section); + parent.sections.remove(section); + + if (parent.isListSection) { + this._scheduleListRemovalIfEmpty(parent); + } + } + + removeAllSections() { + this.editor.post.sections.toArray().forEach(section => { + this.removeSection(section); + }); + } + + migrateSectionsFromPost(post) { + post.sections.toArray().forEach(section => { + post.sections.remove(section); + this.insertSectionBefore(this.editor.post.sections, section, null); + }); + } + + _scheduleListRemovalIfEmpty(listSection) { + this.addCallback(CALLBACK_QUEUES.BEFORE_COMPLETE, () => { + // if the list is attached and blank after we do other rendering stuff, + // remove it + let isAttached = !!listSection.parent; + if (isAttached && listSection.isBlank) { + this.removeSection(listSection); + } + }); + } + + /** + * A method for adding work the deferred queue + * + * @param {Function} callback to run during completion + * @param {Boolean} [once=false] Whether to only schedule the callback once. + * @public + */ + schedule(callback, once=false) { + assert('Work can only be scheduled before a post edit has completed', + !this._didComplete); + if (once) { + this.addCallbackOnce(CALLBACK_QUEUES.COMPLETE, callback); + } else { + this.addCallback(CALLBACK_QUEUES.COMPLETE, callback); + } + } + + /** + * A method for adding work the deferred queue. The callback will only + * be added to the queue once, even if `scheduleOnce` is called multiple times. + * The function cannot be an anonymous function. + * + * @param {Function} callback to run during completion + * @public + */ + scheduleOnce(callback) { + this.schedule(callback, true); + } + + /** + * Add a rerender job to the queue + * + * @public + */ + scheduleRerender() { + this.scheduleOnce(this._rerender); + } + + /** + * Schedule a notification that the post has been changed. + * The notification will result in the editor firing its `postDidChange` + * hook after the postEditor completes its work (at the end of {@link Editor#run}). + * + * @public + */ + scheduleDidUpdate() { + this.scheduleOnce(this._postDidChange); + } + + scheduleAfterRender(callback, once=false) { + if (once) { + this.addCallbackOnce(CALLBACK_QUEUES.AFTER_COMPLETE, callback); + } else { + this.addCallback(CALLBACK_QUEUES.AFTER_COMPLETE, callback); + } + } + + /** + * Flush any work on the queue. {@link Editor#run} calls this method; it + * should not be called directly. + * + * @private + */ + complete() { + assert('Post editing can only be completed once', !this._didComplete); + + this.runCallbacks(CALLBACK_QUEUES.BEFORE_COMPLETE); + this._didComplete = true; + this.runCallbacks(CALLBACK_QUEUES.COMPLETE); + this.runCallbacks(CALLBACK_QUEUES.AFTER_COMPLETE); + } + + undoLastChange() { + this.editor._editHistory.stepBackward(this); + } + + redoLastChange() { + this.editor._editHistory.stepForward(this); + } + + cancelSnapshot() { + this._shouldCancelSnapshot = true; + } +} + +const placeholderImageSrc = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAOEAAADhCAMAAAAJbSJIAAAAMFBMVEXp7vG6vsHm6+63u77Hy868wMPe4+bO09bh5unr8fTR1djAxMfM0NPX3N/c4eTBxcjXRf5TAAACh0lEQVR4nO3b6ZKqMBSFUSQMYZL3f9tbBq/NEEDiqUqOfusn1ZXKbjcQlGQZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC6RkbsGHuabChEtHmiGYfS3EQYM+Sxw/gMQvmcNnYaj6oTDHi73WPn2eqnj9B8zo3TJXcq5uNjXmVff86VwSR3JtryMa1BYqi7S1hJDCVpSigyLcGhJJEwzlCSNtPKrbVhVwsdCfOhH7uuaG3ARV9DwsaOzxt3N1yPqCHhvXytTUz92VDpmE/LLhZwl++R6Sds6sUa/PL6K/2E2fIhw1xdRKefsFolrPc+xNx/N0k/4fpBsdhL2HfeiN+TsDCms8dDpeRyS3P3QDl6Iqaf8L0rTf+80m6Lmn7Ct+4Wxf+/2RY1/YRv3PHz/u+fsCmqgoTnq7Z+8SGviqoh4dnKu1ieqauiakh4/PQ0r6ivqDoSHj0B97eNRVG1JNxV+L4bnxdVecJtRTdFVZ7QU9F1UXUn9FZ0VVRlCav5ob2KLouqKmFjy676u2HsVnRRVFUJq3J+8KCi86IqSthMvyl209Hjijqm3RsqAZ5pNfa5PJ2KelJRjQmr1/r7cfy0ouoSNvOfvbvhvKLaEr4qOin9kTQnrN7LpDZhE/Zmhp6Eq4p+YcKgiipKGFhRRQkDK6ooYfgLbiSMioQkJGF8P5XwHv4O+7AaKiXzaeXh1kMl5AffTUxiKEm/krD94BR8Gdxl1fceSlR58ZhXKbEpyD2amNiBtmrJLTMHL1LF8/rpXkSZXEmz8K8uvAFFNm6Iq0aBLUFOmeCuJ6exrcCmoLpN7kYx891bSAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgh/wDdr8peyRHLogAAAAASUVORK5CYII="; + +var ImageCard = { + name: 'image', + type: 'dom', + + render({payload}) { + let img = document.createElement('img'); + img.src = payload.src || placeholderImageSrc; + return img; + } +}; + +function visit(visitor, node, opcodes) { + const method = node.type; + assert(`Cannot visit unknown type ${method}`, !!visitor[method]); + visitor[method](node, opcodes); +} + +function compile(compiler, opcodes) { + for (var i=0, l=opcodes.length; i { + visit(visitor, node, opcodes); + }); +} + +const MOBILEDOC_VERSION = '0.2.0'; +const MOBILEDOC_MARKUP_SECTION_TYPE = 1; +const MOBILEDOC_IMAGE_SECTION_TYPE = 2; +const MOBILEDOC_LIST_SECTION_TYPE = 3; +const MOBILEDOC_CARD_SECTION_TYPE = 10; + +const visitor = { + [POST_TYPE](node, opcodes) { + opcodes.push(['openPost']); + visitArray(visitor, node.sections, opcodes); + }, + [MARKUP_SECTION_TYPE](node, opcodes) { + opcodes.push(['openMarkupSection', node.tagName]); + visitArray(visitor, node.markers, opcodes); + }, + [LIST_SECTION_TYPE](node, opcodes) { + opcodes.push(['openListSection', node.tagName]); + visitArray(visitor, node.items, opcodes); + }, + [LIST_ITEM_TYPE](node, opcodes) { + opcodes.push(['openListItem']); + visitArray(visitor, node.markers, opcodes); + }, + [IMAGE_SECTION_TYPE](node, opcodes) { + opcodes.push(['openImageSection', node.src]); + }, + [CARD_TYPE](node, opcodes) { + opcodes.push(['openCardSection', node.name, node.payload]); + }, + [MARKER_TYPE](node, opcodes) { + opcodes.push(['openMarker', node.closedMarkups.length, node.value]); + visitArray(visitor, node.openedMarkups, opcodes); + }, + [MARKUP_TYPE](node, opcodes) { + opcodes.push(['openMarkup', node.tagName, objectToSortedKVArray(node.attributes)]); + } +}; + +const postOpcodeCompiler = { + openMarker(closeCount, value) { + this.markupMarkerIds = []; + this.markers.push([ + this.markupMarkerIds, + closeCount, + value || '' + ]); + }, + openMarkupSection(tagName) { + this.markers = []; + this.sections.push([MOBILEDOC_MARKUP_SECTION_TYPE, tagName, this.markers]); + }, + openListSection(tagName) { + this.items = []; + this.sections.push([MOBILEDOC_LIST_SECTION_TYPE, tagName, this.items]); + }, + openListItem() { + this.markers = []; + this.items.push(this.markers); + }, + openImageSection(url) { + this.sections.push([MOBILEDOC_IMAGE_SECTION_TYPE, url]); + }, + openCardSection(name, payload) { + this.sections.push([MOBILEDOC_CARD_SECTION_TYPE, name, payload]); + }, + openPost() { + this.markerTypes = []; + this.sections = []; + this.result = { + version: MOBILEDOC_VERSION, + sections: [this.markerTypes, this.sections] + }; + }, + openMarkup(tagName, attributes) { + const index = this._findOrAddMarkerTypeIndex(tagName, attributes); + this.markupMarkerIds.push(index); + }, + _findOrAddMarkerTypeIndex(tagName, attributesArray) { + if (!this._markerTypeCache) { this._markerTypeCache = {}; } + const key = `${tagName}-${attributesArray.join('-')}`; + + let index = this._markerTypeCache[key]; + if (index === undefined) { + let markerType = [tagName]; + if (attributesArray.length) { markerType.push(attributesArray); } + this.markerTypes.push(markerType); + + index = this.markerTypes.length - 1; + this._markerTypeCache[key] = index; + } + + return index; + } +}; + +/** + * Render from post -> mobiledoc + */ +var MobiledocRenderer_0_2 = { + /** + * @param {Post} + * @return {Mobiledoc} + */ + render(post) { + let opcodes = []; + visit(visitor, post, opcodes); + let compiler = Object.create(postOpcodeCompiler); + compile(compiler, opcodes); + return compiler.result; + } +}; + +/* + * Parses from mobiledoc -> post + */ +class MobiledocParser { + constructor(builder) { + this.builder = builder; + } + + /** + * @param {Mobiledoc} + * @return {Post} + */ + parse({sections: sectionData}) { + try { + const markerTypes = sectionData[0]; + const sections = sectionData[1]; + + const post = this.builder.createPost(); + + this.markups = []; + this.markerTypes = this.parseMarkerTypes(markerTypes); + this.parseSections(sections, post); + + return post; + } catch (e) { + assert(`Unable to parse mobiledoc: ${e.message}`, false); + } + } + + parseMarkerTypes(markerTypes) { + return markerTypes.map((markerType) => this.parseMarkerType(markerType)); + } + + parseMarkerType([tagName, attributesArray]) { + const attributesObject = kvArrayToObject(attributesArray || []); + return this.builder.createMarkup(tagName, attributesObject); + } + + parseSections(sections, post) { + sections.forEach((section) => this.parseSection(section, post)); + } + + parseSection(section, post) { + let [type] = section; + switch(type) { + case MOBILEDOC_MARKUP_SECTION_TYPE: + this.parseMarkupSection(section, post); + break; + case MOBILEDOC_IMAGE_SECTION_TYPE: + this.parseImageSection(section, post); + break; + case MOBILEDOC_CARD_SECTION_TYPE: + this.parseCardSection(section, post); + break; + case MOBILEDOC_LIST_SECTION_TYPE: + this.parseListSection(section, post); + break; + default: + assert(`Unexpected section type ${type}`, false); + } + } + + parseCardSection([, name, payload], post) { + const section = this.builder.createCardSection(name, payload); + post.sections.append(section); + } + + parseImageSection([, src], post) { + const section = this.builder.createImageSection(src); + post.sections.append(section); + } + + parseMarkupSection([, tagName, markers], post) { + const section = this.builder.createMarkupSection(tagName.toLowerCase() === 'pull-quote' ? 'aside' : tagName); + post.sections.append(section); + this.parseMarkers(markers, section); + // Strip blank markers after they have been created. This ensures any + // markup they include has been correctly populated. + filter(section.markers, m => m.isBlank).forEach(m => { + section.markers.remove(m); + }); + } + + parseListSection([, tagName, items], post) { + const section = this.builder.createListSection(tagName); + post.sections.append(section); + this.parseListItems(items, section); + } + + parseListItems(items, section) { + items.forEach(i => this.parseListItem(i, section)); + } + + parseListItem(markers, section) { + const item = this.builder.createListItem(); + this.parseMarkers(markers, item); + section.items.append(item); + } + + parseMarkers(markers, parent) { + markers.forEach(m => this.parseMarker(m, parent)); + } + + parseMarker([markerTypeIndexes, closeCount, value], parent) { + markerTypeIndexes.forEach(index => { + this.markups.push(this.markerTypes[index]); + }); + const marker = this.builder.createMarker(value, this.markups.slice()); + parent.markers.append(marker); + this.markups = this.markups.slice(0, this.markups.length-closeCount); + } +} + +const MOBILEDOC_VERSION$1 = '0.3.0'; +const MOBILEDOC_MARKUP_SECTION_TYPE$1 = 1; +const MOBILEDOC_IMAGE_SECTION_TYPE$1 = 2; +const MOBILEDOC_LIST_SECTION_TYPE$1 = 3; +const MOBILEDOC_CARD_SECTION_TYPE$1 = 10; + +const MOBILEDOC_MARKUP_MARKER_TYPE = 0; +const MOBILEDOC_ATOM_MARKER_TYPE = 1; + +const visitor$1 = { + [POST_TYPE](node, opcodes) { + opcodes.push(['openPost']); + visitArray(visitor$1, node.sections, opcodes); + }, + [MARKUP_SECTION_TYPE](node, opcodes) { + opcodes.push(['openMarkupSection', node.tagName]); + visitArray(visitor$1, node.markers, opcodes); + }, + [LIST_SECTION_TYPE](node, opcodes) { + opcodes.push(['openListSection', node.tagName]); + visitArray(visitor$1, node.items, opcodes); + }, + [LIST_ITEM_TYPE](node, opcodes) { + opcodes.push(['openListItem']); + visitArray(visitor$1, node.markers, opcodes); + }, + [IMAGE_SECTION_TYPE](node, opcodes) { + opcodes.push(['openImageSection', node.src]); + }, + [CARD_TYPE](node, opcodes) { + opcodes.push(['openCardSection', node.name, node.payload]); + }, + [MARKER_TYPE](node, opcodes) { + opcodes.push(['openMarker', node.closedMarkups.length, node.value]); + visitArray(visitor$1, node.openedMarkups, opcodes); + }, + [MARKUP_TYPE](node, opcodes) { + opcodes.push(['openMarkup', node.tagName, objectToSortedKVArray(node.attributes)]); + }, + [ATOM_TYPE](node, opcodes) { + opcodes.push(['openAtom', node.closedMarkups.length, node.name, node.value, node.payload]); + visitArray(visitor$1, node.openedMarkups, opcodes); + } +}; + +const postOpcodeCompiler$1 = { + openMarker(closeCount, value) { + this.markupMarkerIds = []; + this.markers.push([ + MOBILEDOC_MARKUP_MARKER_TYPE, + this.markupMarkerIds, + closeCount, + value || '' + ]); + }, + openMarkupSection(tagName) { + this.markers = []; + this.sections.push([MOBILEDOC_MARKUP_SECTION_TYPE$1, tagName, this.markers]); + }, + openListSection(tagName) { + this.items = []; + this.sections.push([MOBILEDOC_LIST_SECTION_TYPE$1, tagName, this.items]); + }, + openListItem() { + this.markers = []; + this.items.push(this.markers); + }, + openImageSection(url) { + this.sections.push([MOBILEDOC_IMAGE_SECTION_TYPE$1, url]); + }, + openCardSection(name, payload) { + const index = this._addCardTypeIndex(name, payload); + this.sections.push([MOBILEDOC_CARD_SECTION_TYPE$1, index]); + }, + openAtom(closeCount, name, value, payload) { + const index = this._addAtomTypeIndex(name, value, payload); + this.markupMarkerIds = []; + this.markers.push([ + MOBILEDOC_ATOM_MARKER_TYPE, + this.markupMarkerIds, + closeCount, + index + ]); + }, + openPost() { + this.atomTypes = []; + this.cardTypes = []; + this.markerTypes = []; + this.sections = []; + this.result = { + version: MOBILEDOC_VERSION$1, + atoms: this.atomTypes, + cards: this.cardTypes, + markups: this.markerTypes, + sections: this.sections + }; + }, + openMarkup(tagName, attributes) { + const index = this._findOrAddMarkerTypeIndex(tagName, attributes); + this.markupMarkerIds.push(index); + }, + _addCardTypeIndex(cardName, payload) { + let cardType = [cardName, payload]; + this.cardTypes.push(cardType); + return this.cardTypes.length - 1; + }, + _addAtomTypeIndex(atomName, atomValue, payload) { + let atomType = [atomName, atomValue, payload]; + this.atomTypes.push(atomType); + return this.atomTypes.length - 1; + }, + _findOrAddMarkerTypeIndex(tagName, attributesArray) { + if (!this._markerTypeCache) { this._markerTypeCache = {}; } + const key = `${tagName}-${attributesArray.join('-')}`; + + let index = this._markerTypeCache[key]; + if (index === undefined) { + let markerType = [tagName]; + if (attributesArray.length) { markerType.push(attributesArray); } + this.markerTypes.push(markerType); + + index = this.markerTypes.length - 1; + this._markerTypeCache[key] = index; + } + + return index; + } +}; + +/** + * Render from post -> mobiledoc + */ +var MobiledocRenderer_0_3 = { + /** + * @param {Post} + * @return {Mobiledoc} + */ + render(post) { + let opcodes = []; + visit(visitor$1, post, opcodes); + let compiler = Object.create(postOpcodeCompiler$1); + compile(compiler, opcodes); + return compiler.result; + } +}; + +/* + * Parses from mobiledoc -> post + */ +class MobiledocParser$1 { + constructor(builder) { + this.builder = builder; + } + + /** + * @param {Mobiledoc} + * @return {Post} + */ + parse({ sections, markups: markerTypes, cards: cardTypes, atoms: atomTypes }) { + try { + const post = this.builder.createPost(); + + this.markups = []; + this.markerTypes = this.parseMarkerTypes(markerTypes); + this.cardTypes = this.parseCardTypes(cardTypes); + this.atomTypes = this.parseAtomTypes(atomTypes); + this.parseSections(sections, post); + + return post; + } catch (e) { + assert(`Unable to parse mobiledoc: ${e.message}`, false); + } + } + + parseMarkerTypes(markerTypes) { + return markerTypes.map((markerType) => this.parseMarkerType(markerType)); + } + + parseMarkerType([tagName, attributesArray]) { + const attributesObject = kvArrayToObject(attributesArray || []); + return this.builder.createMarkup(tagName, attributesObject); + } + + parseCardTypes(cardTypes) { + return cardTypes.map((cardType) => this.parseCardType(cardType)); + } + + parseCardType([cardName, cardPayload]) { + return [cardName, cardPayload]; + } + + parseAtomTypes(atomTypes) { + return atomTypes.map((atomType) => this.parseAtomType(atomType)); + } + + parseAtomType([atomName, atomValue, atomPayload]) { + return [atomName, atomValue, atomPayload]; + } + + parseSections(sections, post) { + sections.forEach((section) => this.parseSection(section, post)); + } + + parseSection(section, post) { + let [type] = section; + switch(type) { + case MOBILEDOC_MARKUP_SECTION_TYPE$1: + this.parseMarkupSection(section, post); + break; + case MOBILEDOC_IMAGE_SECTION_TYPE$1: + this.parseImageSection(section, post); + break; + case MOBILEDOC_CARD_SECTION_TYPE$1: + this.parseCardSection(section, post); + break; + case MOBILEDOC_LIST_SECTION_TYPE$1: + this.parseListSection(section, post); + break; + default: + assert('Unexpected section type ${type}', false); + } + } + + getAtomTypeFromIndex(index) { + const atomType = this.atomTypes[index]; + assert(`No atom definition found at index ${index}`, !!atomType); + return atomType; + } + + getCardTypeFromIndex(index) { + const cardType = this.cardTypes[index]; + assert(`No card definition found at index ${index}`, !!cardType); + return cardType; + } + + parseCardSection([, cardIndex], post) { + const [name, payload] = this.getCardTypeFromIndex(cardIndex); + const section = this.builder.createCardSection(name, payload); + post.sections.append(section); + } + + parseImageSection([, src], post) { + const section = this.builder.createImageSection(src); + post.sections.append(section); + } + + parseMarkupSection([, tagName, markers], post) { + const section = this.builder.createMarkupSection(tagName.toLowerCase() === 'pull-quote' ? 'aside' : tagName); + post.sections.append(section); + this.parseMarkers(markers, section); + // Strip blank markers after they have been created. This ensures any + // markup they include has been correctly populated. + filter(section.markers, m => m.isBlank).forEach(m => { + section.markers.remove(m); + }); + } + + parseListSection([, tagName, items], post) { + const section = this.builder.createListSection(tagName); + post.sections.append(section); + this.parseListItems(items, section); + } + + parseListItems(items, section) { + items.forEach(i => this.parseListItem(i, section)); + } + + parseListItem(markers, section) { + const item = this.builder.createListItem(); + this.parseMarkers(markers, item); + section.items.append(item); + } + + parseMarkers(markers, parent) { + markers.forEach(m => this.parseMarker(m, parent)); + } + + parseMarker([type, markerTypeIndexes, closeCount, value], parent) { + markerTypeIndexes.forEach(index => { + this.markups.push(this.markerTypes[index]); + }); + + const marker = this.buildMarkerType(type, value); + parent.markers.append(marker); + + this.markups = this.markups.slice(0, this.markups.length-closeCount); + } + + buildMarkerType(type, value) { + switch (type) { + case MOBILEDOC_MARKUP_MARKER_TYPE: + return this.builder.createMarker(value, this.markups.slice()); + case MOBILEDOC_ATOM_MARKER_TYPE: { + const [atomName, atomValue, atomPayload] = this.getAtomTypeFromIndex(value); + return this.builder.createAtom(atomName, atomValue, atomPayload, this.markups.slice()); + } + default: + assert(`Unexpected marker type ${type}`, false); + } + } +} + +const MOBILEDOC_VERSION$2 = '0.3.1'; +const MOBILEDOC_MARKUP_SECTION_TYPE$2 = 1; +const MOBILEDOC_IMAGE_SECTION_TYPE$2 = 2; +const MOBILEDOC_LIST_SECTION_TYPE$2 = 3; +const MOBILEDOC_CARD_SECTION_TYPE$2 = 10; + +const MOBILEDOC_MARKUP_MARKER_TYPE$1 = 0; +const MOBILEDOC_ATOM_MARKER_TYPE$1 = 1; + +const visitor$2 = { + [POST_TYPE](node, opcodes) { + opcodes.push(['openPost']); + visitArray(visitor$2, node.sections, opcodes); + }, + [MARKUP_SECTION_TYPE](node, opcodes) { + opcodes.push(['openMarkupSection', node.tagName]); + visitArray(visitor$2, node.markers, opcodes); + }, + [LIST_SECTION_TYPE](node, opcodes) { + opcodes.push(['openListSection', node.tagName]); + visitArray(visitor$2, node.items, opcodes); + }, + [LIST_ITEM_TYPE](node, opcodes) { + opcodes.push(['openListItem']); + visitArray(visitor$2, node.markers, opcodes); + }, + [IMAGE_SECTION_TYPE](node, opcodes) { + opcodes.push(['openImageSection', node.src]); + }, + [CARD_TYPE](node, opcodes) { + opcodes.push(['openCardSection', node.name, node.payload]); + }, + [MARKER_TYPE](node, opcodes) { + opcodes.push(['openMarker', node.closedMarkups.length, node.value]); + visitArray(visitor$2, node.openedMarkups, opcodes); + }, + [MARKUP_TYPE](node, opcodes) { + opcodes.push(['openMarkup', node.tagName, objectToSortedKVArray(node.attributes)]); + }, + [ATOM_TYPE](node, opcodes) { + opcodes.push(['openAtom', node.closedMarkups.length, node.name, node.value, node.payload]); + visitArray(visitor$2, node.openedMarkups, opcodes); + } +}; + +const postOpcodeCompiler$2 = { + openMarker(closeCount, value) { + this.markupMarkerIds = []; + this.markers.push([ + MOBILEDOC_MARKUP_MARKER_TYPE$1, + this.markupMarkerIds, + closeCount, + value || '' + ]); + }, + openMarkupSection(tagName) { + this.markers = []; + this.sections.push([MOBILEDOC_MARKUP_SECTION_TYPE$2, tagName, this.markers]); + }, + openListSection(tagName) { + this.items = []; + this.sections.push([MOBILEDOC_LIST_SECTION_TYPE$2, tagName, this.items]); + }, + openListItem() { + this.markers = []; + this.items.push(this.markers); + }, + openImageSection(url) { + this.sections.push([MOBILEDOC_IMAGE_SECTION_TYPE$2, url]); + }, + openCardSection(name, payload) { + const index = this._addCardTypeIndex(name, payload); + this.sections.push([MOBILEDOC_CARD_SECTION_TYPE$2, index]); + }, + openAtom(closeCount, name, value, payload) { + const index = this._addAtomTypeIndex(name, value, payload); + this.markupMarkerIds = []; + this.markers.push([ + MOBILEDOC_ATOM_MARKER_TYPE$1, + this.markupMarkerIds, + closeCount, + index + ]); + }, + openPost() { + this.atomTypes = []; + this.cardTypes = []; + this.markerTypes = []; + this.sections = []; + this.result = { + version: MOBILEDOC_VERSION$2, + atoms: this.atomTypes, + cards: this.cardTypes, + markups: this.markerTypes, + sections: this.sections + }; + }, + openMarkup(tagName, attributes) { + const index = this._findOrAddMarkerTypeIndex(tagName, attributes); + this.markupMarkerIds.push(index); + }, + _addCardTypeIndex(cardName, payload) { + let cardType = [cardName, payload]; + this.cardTypes.push(cardType); + return this.cardTypes.length - 1; + }, + _addAtomTypeIndex(atomName, atomValue, payload) { + let atomType = [atomName, atomValue, payload]; + this.atomTypes.push(atomType); + return this.atomTypes.length - 1; + }, + _findOrAddMarkerTypeIndex(tagName, attributesArray) { + if (!this._markerTypeCache) { this._markerTypeCache = {}; } + const key = `${tagName}-${attributesArray.join('-')}`; + + let index = this._markerTypeCache[key]; + if (index === undefined) { + let markerType = [tagName]; + if (attributesArray.length) { markerType.push(attributesArray); } + this.markerTypes.push(markerType); + + index = this.markerTypes.length - 1; + this._markerTypeCache[key] = index; + } + + return index; + } +}; + +/** + * Render from post -> mobiledoc + */ +var MobiledocRenderer_0_3_1 = { + /** + * @param {Post} + * @return {Mobiledoc} + */ + render(post) { + let opcodes = []; + visit(visitor$2, post, opcodes); + let compiler = Object.create(postOpcodeCompiler$2); + compile(compiler, opcodes); + return compiler.result; + } +}; + +/* + * Parses from mobiledoc -> post + */ +class MobiledocParser$2 { + constructor(builder) { + this.builder = builder; + } + + /** + * @param {Mobiledoc} + * @return {Post} + */ + parse({ sections, markups: markerTypes, cards: cardTypes, atoms: atomTypes }) { + try { + const post = this.builder.createPost(); + + this.markups = []; + this.markerTypes = this.parseMarkerTypes(markerTypes); + this.cardTypes = this.parseCardTypes(cardTypes); + this.atomTypes = this.parseAtomTypes(atomTypes); + this.parseSections(sections, post); + + return post; + } catch (e) { + assert(`Unable to parse mobiledoc: ${e.message}`, false); + } + } + + parseMarkerTypes(markerTypes) { + return markerTypes.map((markerType) => this.parseMarkerType(markerType)); + } + + parseMarkerType([tagName, attributesArray]) { + const attributesObject = kvArrayToObject(attributesArray || []); + return this.builder.createMarkup(tagName, attributesObject); + } + + parseCardTypes(cardTypes) { + return cardTypes.map((cardType) => this.parseCardType(cardType)); + } + + parseCardType([cardName, cardPayload]) { + return [cardName, cardPayload]; + } + + parseAtomTypes(atomTypes) { + return atomTypes.map((atomType) => this.parseAtomType(atomType)); + } + + parseAtomType([atomName, atomValue, atomPayload]) { + return [atomName, atomValue, atomPayload]; + } + + parseSections(sections, post) { + sections.forEach((section) => this.parseSection(section, post)); + } + + parseSection(section, post) { + let [type] = section; + switch(type) { + case MOBILEDOC_MARKUP_SECTION_TYPE$2: + this.parseMarkupSection(section, post); + break; + case MOBILEDOC_IMAGE_SECTION_TYPE$2: + this.parseImageSection(section, post); + break; + case MOBILEDOC_CARD_SECTION_TYPE$2: + this.parseCardSection(section, post); + break; + case MOBILEDOC_LIST_SECTION_TYPE$2: + this.parseListSection(section, post); + break; + default: + assert('Unexpected section type ${type}', false); + } + } + + getAtomTypeFromIndex(index) { + const atomType = this.atomTypes[index]; + assert(`No atom definition found at index ${index}`, !!atomType); + return atomType; + } + + getCardTypeFromIndex(index) { + const cardType = this.cardTypes[index]; + assert(`No card definition found at index ${index}`, !!cardType); + return cardType; + } + + parseCardSection([, cardIndex], post) { + const [name, payload] = this.getCardTypeFromIndex(cardIndex); + const section = this.builder.createCardSection(name, payload); + post.sections.append(section); + } + + parseImageSection([, src], post) { + const section = this.builder.createImageSection(src); + post.sections.append(section); + } + + parseMarkupSection([, tagName, markers], post) { + const section = this.builder.createMarkupSection(tagName); + post.sections.append(section); + this.parseMarkers(markers, section); + // Strip blank markers after they have been created. This ensures any + // markup they include has been correctly populated. + filter(section.markers, m => m.isBlank).forEach(m => { + section.markers.remove(m); + }); + } + + parseListSection([, tagName, items], post) { + const section = this.builder.createListSection(tagName); + post.sections.append(section); + this.parseListItems(items, section); + } + + parseListItems(items, section) { + items.forEach(i => this.parseListItem(i, section)); + } + + parseListItem(markers, section) { + const item = this.builder.createListItem(); + this.parseMarkers(markers, item); + section.items.append(item); + } + + parseMarkers(markers, parent) { + markers.forEach(m => this.parseMarker(m, parent)); + } + + parseMarker([type, markerTypeIndexes, closeCount, value], parent) { + markerTypeIndexes.forEach(index => { + this.markups.push(this.markerTypes[index]); + }); + + const marker = this.buildMarkerType(type, value); + parent.markers.append(marker); + + this.markups = this.markups.slice(0, this.markups.length-closeCount); + } + + buildMarkerType(type, value) { + switch (type) { + case MOBILEDOC_MARKUP_MARKER_TYPE$1: + return this.builder.createMarker(value, this.markups.slice()); + case MOBILEDOC_ATOM_MARKER_TYPE$1: { + const [atomName, atomValue, atomPayload] = this.getAtomTypeFromIndex(value); + return this.builder.createAtom(atomName, atomValue, atomPayload, this.markups.slice()); + } + default: + assert(`Unexpected marker type ${type}`, false); + } + } +} + +const MOBILEDOC_VERSION$3 = '0.3.2'; +const MOBILEDOC_MARKUP_SECTION_TYPE$3 = 1; +const MOBILEDOC_IMAGE_SECTION_TYPE$3 = 2; +const MOBILEDOC_LIST_SECTION_TYPE$3 = 3; +const MOBILEDOC_CARD_SECTION_TYPE$3 = 10; + +const MOBILEDOC_MARKUP_MARKER_TYPE$2 = 0; +const MOBILEDOC_ATOM_MARKER_TYPE$2 = 1; + +const visitor$3 = { + [POST_TYPE](node, opcodes) { + opcodes.push(['openPost']); + visitArray(visitor$3, node.sections, opcodes); + }, + [MARKUP_SECTION_TYPE](node, opcodes) { + opcodes.push(['openMarkupSection', node.tagName, objectToSortedKVArray(node.attributes)]); + visitArray(visitor$3, node.markers, opcodes); + }, + [LIST_SECTION_TYPE](node, opcodes) { + opcodes.push(['openListSection', node.tagName, objectToSortedKVArray(node.attributes)]); + visitArray(visitor$3, node.items, opcodes); + }, + [LIST_ITEM_TYPE](node, opcodes) { + opcodes.push(['openListItem']); + visitArray(visitor$3, node.markers, opcodes); + }, + [IMAGE_SECTION_TYPE](node, opcodes) { + opcodes.push(['openImageSection', node.src]); + }, + [CARD_TYPE](node, opcodes) { + opcodes.push(['openCardSection', node.name, node.payload]); + }, + [MARKER_TYPE](node, opcodes) { + opcodes.push(['openMarker', node.closedMarkups.length, node.value]); + visitArray(visitor$3, node.openedMarkups, opcodes); + }, + [MARKUP_TYPE](node, opcodes) { + opcodes.push(['openMarkup', node.tagName, objectToSortedKVArray(node.attributes)]); + }, + [ATOM_TYPE](node, opcodes) { + opcodes.push(['openAtom', node.closedMarkups.length, node.name, node.value, node.payload]); + visitArray(visitor$3, node.openedMarkups, opcodes); + } +}; + +const postOpcodeCompiler$3 = { + openMarker(closeCount, value) { + this.markupMarkerIds = []; + this.markers.push([ + MOBILEDOC_MARKUP_MARKER_TYPE$2, + this.markupMarkerIds, + closeCount, + value || '' + ]); + }, + openMarkupSection(tagName, attributes) { + this.markers = []; + if (attributes && attributes.length !== 0) { + this.sections.push([MOBILEDOC_MARKUP_SECTION_TYPE$3, tagName, this.markers, attributes]); + } else { + this.sections.push([MOBILEDOC_MARKUP_SECTION_TYPE$3, tagName, this.markers]); + } + }, + openListSection(tagName, attributes) { + this.items = []; + if (attributes && attributes.length !== 0) { + this.sections.push([MOBILEDOC_LIST_SECTION_TYPE$3, tagName, this.items, attributes]); + } else { + this.sections.push([MOBILEDOC_LIST_SECTION_TYPE$3, tagName, this.items]); + } + }, + openListItem() { + this.markers = []; + this.items.push(this.markers); + }, + openImageSection(url) { + this.sections.push([MOBILEDOC_IMAGE_SECTION_TYPE$3, url]); + }, + openCardSection(name, payload) { + const index = this._addCardTypeIndex(name, payload); + this.sections.push([MOBILEDOC_CARD_SECTION_TYPE$3, index]); + }, + openAtom(closeCount, name, value, payload) { + const index = this._addAtomTypeIndex(name, value, payload); + this.markupMarkerIds = []; + this.markers.push([ + MOBILEDOC_ATOM_MARKER_TYPE$2, + this.markupMarkerIds, + closeCount, + index + ]); + }, + openPost() { + this.atomTypes = []; + this.cardTypes = []; + this.markerTypes = []; + this.sections = []; + this.result = { + version: MOBILEDOC_VERSION$3, + atoms: this.atomTypes, + cards: this.cardTypes, + markups: this.markerTypes, + sections: this.sections + }; + }, + openMarkup(tagName, attributes) { + const index = this._findOrAddMarkerTypeIndex(tagName, attributes); + this.markupMarkerIds.push(index); + }, + _addCardTypeIndex(cardName, payload) { + let cardType = [cardName, payload]; + this.cardTypes.push(cardType); + return this.cardTypes.length - 1; + }, + _addAtomTypeIndex(atomName, atomValue, payload) { + let atomType = [atomName, atomValue, payload]; + this.atomTypes.push(atomType); + return this.atomTypes.length - 1; + }, + _findOrAddMarkerTypeIndex(tagName, attributesArray) { + if (!this._markerTypeCache) { this._markerTypeCache = {}; } + const key = `${tagName}-${attributesArray.join('-')}`; + + let index = this._markerTypeCache[key]; + if (index === undefined) { + let markerType = [tagName]; + if (attributesArray.length) { markerType.push(attributesArray); } + this.markerTypes.push(markerType); + + index = this.markerTypes.length - 1; + this._markerTypeCache[key] = index; + } + + return index; + } +}; + +/** + * Render from post -> mobiledoc + */ +var MobiledocRenderer_0_3_2 = { + /** + * @param {Post} + * @return {Mobiledoc} + */ + render(post) { + let opcodes = []; + visit(visitor$3, post, opcodes); + let compiler = Object.create(postOpcodeCompiler$3); + compile(compiler, opcodes); + return compiler.result; + } +}; + +function entries(obj) { + const ownProps = Object.keys(obj); + let i = ownProps.length; + const resArray = new Array(i); + + while (i--) { + resArray[i] = [ownProps[i], obj[ownProps[i]]]; + } + + return resArray; +} + +/* + * Parses from mobiledoc -> post + */ +class MobiledocParser$3 { + constructor(builder) { + this.builder = builder; + } + + /** + * @param {Mobiledoc} + * @return {Post} + */ + parse({ sections, markups: markerTypes, cards: cardTypes, atoms: atomTypes }) { + try { + const post = this.builder.createPost(); + + this.markups = []; + this.markerTypes = this.parseMarkerTypes(markerTypes); + this.cardTypes = this.parseCardTypes(cardTypes); + this.atomTypes = this.parseAtomTypes(atomTypes); + this.parseSections(sections, post); + + return post; + } catch (e) { + assert(`Unable to parse mobiledoc: ${e.message}`, false); + } + } + + parseMarkerTypes(markerTypes) { + return markerTypes.map((markerType) => this.parseMarkerType(markerType)); + } + + parseMarkerType([tagName, attributesArray]) { + const attributesObject = kvArrayToObject(attributesArray || []); + return this.builder.createMarkup(tagName, attributesObject); + } + + parseCardTypes(cardTypes) { + return cardTypes.map((cardType) => this.parseCardType(cardType)); + } + + parseCardType([cardName, cardPayload]) { + return [cardName, cardPayload]; + } + + parseAtomTypes(atomTypes) { + return atomTypes.map((atomType) => this.parseAtomType(atomType)); + } + + parseAtomType([atomName, atomValue, atomPayload]) { + return [atomName, atomValue, atomPayload]; + } + + parseSections(sections, post) { + sections.forEach((section) => this.parseSection(section, post)); + } + + parseSection(section, post) { + let [type] = section; + switch(type) { + case MOBILEDOC_MARKUP_SECTION_TYPE$3: + this.parseMarkupSection(section, post); + break; + case MOBILEDOC_IMAGE_SECTION_TYPE$3: + this.parseImageSection(section, post); + break; + case MOBILEDOC_CARD_SECTION_TYPE$3: + this.parseCardSection(section, post); + break; + case MOBILEDOC_LIST_SECTION_TYPE$3: + this.parseListSection(section, post); + break; + default: + assert('Unexpected section type ${type}', false); + } + } + + getAtomTypeFromIndex(index) { + const atomType = this.atomTypes[index]; + assert(`No atom definition found at index ${index}`, !!atomType); + return atomType; + } + + getCardTypeFromIndex(index) { + const cardType = this.cardTypes[index]; + assert(`No card definition found at index ${index}`, !!cardType); + return cardType; + } + + parseCardSection([, cardIndex], post) { + const [name, payload] = this.getCardTypeFromIndex(cardIndex); + const section = this.builder.createCardSection(name, payload); + post.sections.append(section); + } + + parseImageSection([, src], post) { + const section = this.builder.createImageSection(src); + post.sections.append(section); + } + + parseMarkupSection([, tagName, markers, attributesArray], post) { + const section = this.builder.createMarkupSection(tagName); + post.sections.append(section); + if (attributesArray) { + entries(kvArrayToObject(attributesArray)).forEach(([key, value]) => { + section.setAttribute(key, value); + }); + } + this.parseMarkers(markers, section); + // Strip blank markers after they have been created. This ensures any + // markup they include has been correctly populated. + filter(section.markers, m => m.isBlank).forEach(m => { + section.markers.remove(m); + }); + } + + parseListSection([, tagName, items, attributesArray], post) { + const section = this.builder.createListSection(tagName); + post.sections.append(section); + if (attributesArray) { + entries(kvArrayToObject(attributesArray)).forEach(([key, value]) => { + section.setAttribute(key, value); + }); + } + this.parseListItems(items, section); + } + + parseListItems(items, section) { + items.forEach(i => this.parseListItem(i, section)); + } + + parseListItem(markers, section) { + const item = this.builder.createListItem(); + this.parseMarkers(markers, item); + section.items.append(item); + } + + parseMarkers(markers, parent) { + markers.forEach(m => this.parseMarker(m, parent)); + } + + parseMarker([type, markerTypeIndexes, closeCount, value], parent) { + markerTypeIndexes.forEach(index => { + this.markups.push(this.markerTypes[index]); + }); + + const marker = this.buildMarkerType(type, value); + parent.markers.append(marker); + + this.markups = this.markups.slice(0, this.markups.length-closeCount); + } + + buildMarkerType(type, value) { + switch (type) { + case MOBILEDOC_MARKUP_MARKER_TYPE$2: + return this.builder.createMarker(value, this.markups.slice()); + case MOBILEDOC_ATOM_MARKER_TYPE$2: { + const [atomName, atomValue, atomPayload] = this.getAtomTypeFromIndex(value); + return this.builder.createAtom(atomName, atomValue, atomPayload, this.markups.slice()); + } + default: + assert(`Unexpected marker type ${type}`, false); + } + } +} + +function parseVersion(mobiledoc) { + return mobiledoc.version; +} + +var mobiledocParsers = { + parse(builder, mobiledoc) { + let version = parseVersion(mobiledoc); + switch (version) { + case MOBILEDOC_VERSION: + return new MobiledocParser(builder).parse(mobiledoc); + case MOBILEDOC_VERSION$1: + return new MobiledocParser$1(builder).parse(mobiledoc); + case MOBILEDOC_VERSION$2: + return new MobiledocParser$2(builder).parse(mobiledoc); + case MOBILEDOC_VERSION$3: + return new MobiledocParser$3(builder).parse(mobiledoc); + default: + assert(`Unknown version of mobiledoc parser requested: ${version}`, + false); + } + } +}; + +class CardNode { + constructor(editor, card, section, element, options) { + this.editor = editor; + this.card = card; + this.section = section; + this.element = element; + this.options = options; + + this.mode = null; + + this._teardownCallback = null; + this._rendered = null; + } + + render(mode) { + if (this.mode === mode) { return; } + + this.teardown(); + + this.mode = mode; + + let method = mode === 'display' ? 'render' : 'edit'; + method = this.card[method]; + + assert(`Card is missing "${method}" (tried to render mode: "${mode}")`, + !!method); + let rendered = method({ + env: this.env, + options: this.options, + payload: this.section.payload + }); + + this._validateAndAppendRenderResult(rendered); + } + + teardown() { + if (this._teardownCallback) { + this._teardownCallback(); + this._teardownCallback = null; + } + if (this._rendered) { + this.element.removeChild(this._rendered); + this._rendered = null; + } + } + + didRender() { + if (this._didRenderCallback) { + this._didRenderCallback(); + } + } + + get env() { + return { + name: this.card.name, + isInEditor: true, + onTeardown: (callback) => this._teardownCallback = callback, + didRender: (callback) => this._didRenderCallback = callback, + edit: () => this.edit(), + save: (payload, transition=true) => { + this.section.payload = payload; + + this.editor._postDidChange(); + if (transition) { + this.display(); + } + }, + cancel: () => this.display(), + remove: () => this.remove(), + postModel: this.section + }; + } + + display() { + this.render('display'); + } + + edit() { + this.render('edit'); + } + + remove() { + this.editor.run(postEditor => postEditor.removeSection(this.section)); + } + + _validateAndAppendRenderResult(rendered) { + if (!rendered) { + return; + } + + let { card: { name } } = this; + assert( + `Card "${name}" must render dom (render value was: "${rendered}")`, + !!rendered.nodeType + ); + this.element.appendChild(rendered); + this._rendered = rendered; + this.didRender(); + } +} + +class AtomNode { + constructor(editor, atom, model, element, atomOptions) { + this.editor = editor; + this.atom = atom; + this.model = model; + this.atomOptions = atomOptions; + this.element = element; + + this._teardownCallback = null; + this._rendered = null; + } + + render() { + if (!this._rendered) { + let {atomOptions: options, env, model: { value, payload } } = this; + // cache initial render + this._rendered = this.atom.render({options, env, value, payload}); + } + + this._validateAndAppendRenderResult(this._rendered); + } + + get env() { + return { + name: this.atom.name, + onTeardown: (callback) => this._teardownCallback = callback, + save: (value, payload={}) => { + this.model.value = value; + this.model.payload = payload; + + this.editor._postDidChange(); + this.teardown(); + this.render(); + } + }; + } + + teardown() { + if (this._teardownCallback) { + this._teardownCallback(); + this._teardownCallback = null; + } + if (this._rendered) { + this.element.removeChild(this._rendered); + this._rendered = null; + } + } + + _validateAndAppendRenderResult(rendered) { + if (!rendered) { + return; + } + + let { atom: { name } } = this; + assert( + `Atom "${name}" must return a DOM node (returned value was: "${rendered}")`, + !!rendered.nodeType + ); + this.element.appendChild(rendered); + } +} + +class Set { + constructor(items=[]) { + this.items = []; + items.forEach(i => this.add(i)); + } + + add(item) { + if (!this.has(item)) { + this.items.push(item); + } + } + + get length() { + return this.items.length; + } + + has(item) { + return this.items.indexOf(item) !== -1; + } + + toArray() { + return this.items; + } +} + +const PARENT_PROP = '__parent'; + +class LinkedList { + constructor(options) { + this.head = null; + this.tail = null; + this.length = 0; + + if (options) { + const {adoptItem, freeItem} = options; + this._adoptItem = adoptItem; + this._freeItem = freeItem; + } + } + adoptItem(item) { + item[PARENT_PROP]= this; + this.length++; + if (this._adoptItem) { this._adoptItem(item); } + } + freeItem(item) { + item[PARENT_PROP] = null; + this.length--; + if (this._freeItem) { this._freeItem(item); } + } + get isEmpty() { + return this.length === 0; + } + prepend(item) { + this.insertBefore(item, this.head); + } + append(item) { + this.insertBefore(item, null); + } + insertAfter(item, prevItem) { + let nextItem = prevItem ? prevItem.next : this.head; + this.insertBefore(item, nextItem); + } + _ensureItemIsNotAlreadyInList(item){ + assert( + 'Cannot insert an item into a list if it is already in a list', + !item.next && !item.prev && this.head !== item + ); + } + insertBefore(item, nextItem) { + this._ensureItemIsNotInList(item); + this.adoptItem(item); + + let insertPos; + if (nextItem && nextItem.prev) { + insertPos = 'middle'; + } else if (nextItem) { + insertPos = 'start'; + } else { + insertPos = 'end'; + } + + switch (insertPos) { + case 'start': + if (this.head) { + item.next = this.head; + this.head.prev = item; + } + this.head = item; + + break; + case 'middle': { + let prevItem = nextItem.prev; + item.next = nextItem; + item.prev = prevItem; + nextItem.prev = item; + prevItem.next = item; + + break; + } + case 'end': { + let tail = this.tail; + item.prev = tail; + + if (tail) { + tail.next = item; + } else { + this.head = item; + } + this.tail = item; + + break; + } + } + } + remove(item) { + if (!item[PARENT_PROP]) { + return; + } + this._ensureItemIsInThisList(item); + this.freeItem(item); + + let [prev, next] = [item.prev, item.next]; + item.prev = null; + item.next = null; + + if (prev) { + prev.next = next; + } else { + this.head = next; + } + + if (next) { + next.prev = prev; + } else { + this.tail = prev; + } + } + forEach(callback) { + let item = this.head; + let index = 0; + while (item) { + callback(item, index++); + item = item.next; + } + } + map(callback) { + let result = []; + this.forEach(i => result.push(callback(i))); + return result; + } + walk(startItem, endItem, callback) { + let item = startItem || this.head; + while (item) { + callback(item); + if (item === endItem) { + break; + } + item = item.next; + } + } + readRange(startItem, endItem) { + let items = []; + this.walk(startItem, endItem, (item) => { + items.push(item); + }); + return items; + } + toArray() { + return this.readRange(); + } + detect(callback, item=this.head, reverse=false) { + while (item) { + if (callback(item)) { + return item; + } + item = reverse ? item.prev : item.next; + } + } + any(callback) { + return !!this.detect(callback); + } + every(callback) { + let item = this.head; + while (item) { + if (!callback(item)) { + return false; + } + item = item.next; + } + return true; + } + objectAt(targetIndex) { + let index = -1; + return this.detect(() => { + index++; + return (targetIndex === index); + }); + } + splice(targetItem, removalCount, newItems) { + let item = targetItem; + let nextItem = item.next; + let count = 0; + while (item && count < removalCount) { + count++; + nextItem = item.next; + this.remove(item); + item = nextItem; + } + newItems.forEach((newItem) => { + this.insertBefore(newItem, nextItem); + }); + } + removeBy(conditionFn) { + let item = this.head; + while (item) { + let nextItem = item.next; + + if (conditionFn(item)) { + this.remove(item); + } + + item = nextItem; + } + } + _ensureItemIsNotInList(item) { + assert('Cannot insert an item into a list if it is already in a list', + !item[PARENT_PROP]); + } + _ensureItemIsInThisList(item) { + assert('Cannot remove item that is in another list', + item[PARENT_PROP] === this); + } +} + +function unimplementedMethod(methodName, me) { + assert(`\`${methodName}()\` must be implemented by ${me.constructor.name}`, + false); +} + +class Section extends LinkedItem { + constructor(type) { + super(); + assert('Cannot create section without type', !!type); + this.type = type; + this.isSection = true; + this.isMarkerable = false; + this.isNested = false; + this.isSection = true; + this.isLeafSection = true; + } + + set tagName(val) { + let normalizedTagName = normalizeTagName(val); + assert(`Cannot set section tagName to ${val}`, + this.isValidTagName(normalizedTagName)); + this._tagName = normalizedTagName; + } + + get tagName() { + return this._tagName; + } + + isValidTagName(/* normalizedTagName */) { + unimplementedMethod('isValidTagName', this); + } + + get length() { + return 0; + } + + get isBlank() { + unimplementedMethod('isBlank', this); + } + + clone() { + unimplementedMethod('clone', this); + } + + canJoin(/* otherSection */) { + unimplementedMethod('canJoin', this); + } + + /** + * @return {Position} The position at the start of this section + * @public + */ + headPosition() { + return this.toPosition(0); + } + + /** + * @return {Position} The position at the end of this section + * @public + */ + tailPosition() { + return this.toPosition(this.length); + } + + /** + * @param {Number} offset + * @return {Position} The position in this section at the given offset + * @public + */ + toPosition(offset) { + assert("Must pass number to `toPosition`", typeof offset === 'number'); + assert("Cannot call `toPosition` with offset > length", offset <= this.length); + + return new Position$1(this, offset); + } + + /** + * @return {Range} A range from this section's head to tail positions + * @public + */ + toRange() { + return this.headPosition().toRange(this.tailPosition()); + } + + join() { + unimplementedMethod('join', this); + } + + textUntil(/* position */) { + return ''; + } + + /** + * Markerable sections should override this method + */ + splitMarkerAtOffset() { + let blankEdit = { added: [], removed: [] }; + return blankEdit; + } + + nextLeafSection() { + const next = this.next; + if (next) { + if (next.items) { + return next.items.head; + } else { + return next; + } + } else { + if (this.isNested) { + return this.parent.nextLeafSection(); + } + } + } + + immediatelyNextMarkerableSection() { + let next = this.nextLeafSection(); + while (next && !next.isMarkerable) { + next = next.nextLeafSection(); + } + return next; + } + + previousLeafSection() { + const prev = this.prev; + + if (prev) { + if (prev.items) { + return prev.items.tail; + } else { + return prev; + } + } else { + if (this.isNested) { + return this.parent.previousLeafSection(); + } + } + } +} + +class Markerable extends Section { + constructor(type, tagName, markers=[]) { + super(type); + this.isMarkerable = true; + this.tagName = tagName; + this.markers = new LinkedList({ + adoptItem: m => { + assert(`Can only insert markers and atoms into markerable (was: ${m.type})`, + m.isMarker || m.isAtom); + m.section = m.parent = this; + }, + freeItem: m => m.section = m.parent = null + }); + + markers.forEach(m => this.markers.append(m)); + } + + canJoin(other) { + return other.isMarkerable && + other.type === this.type && + other.tagName === this.tagName; + } + + clone() { + const newMarkers = this.markers.map(m => m.clone()); + return this.builder.createMarkerableSection( + this.type, this.tagName, newMarkers); + } + + get isBlank() { + if (!this.markers.length) { + return true; + } + return this.markers.every(m => m.isBlank); + } + + textUntil(position) { + assert(`Cannot get textUntil for a position not in this section`, position.section === this); + let {marker, offsetInMarker} = position; + let text = ''; + let currentMarker = this.markers.head; + while (currentMarker) { + if (currentMarker === marker) { + text += currentMarker.textUntil(offsetInMarker); + break; + } else { + text += currentMarker.text; + currentMarker = currentMarker.next; + } + } + return text; + } + + /** + * @param {Marker} + * @param {Number} markerOffset The offset relative to the start of the marker + * + * @return {Number} The offset relative to the start of this section + */ + offsetOfMarker(marker, markerOffset=0) { + assert(`Cannot get offsetOfMarker for marker that is not child of this`, + marker.section === this); + + // FIXME it is possible, when we get a cursor position before having finished reparsing, + // for markerOffset to be > marker.length. We shouldn't rely on this functionality. + + let offset = 0; + let currentMarker = this.markers.head; + while (currentMarker && currentMarker !== marker.next) { + let length = currentMarker === marker ? markerOffset : + currentMarker.length; + offset += length; + currentMarker = currentMarker.next; + } + + return offset; + } + + // puts clones of this.markers into beforeSection and afterSection, + // all markers before the marker/offset split go in beforeSection, and all + // after the marker/offset split go in afterSection + // @return {Array} [beforeSection, afterSection], two new sections + _redistributeMarkers(beforeSection, afterSection, marker, offset=0) { + let currentSection = beforeSection; + forEach(this.markers, m => { + if (m === marker) { + const [beforeMarker, ...afterMarkers] = marker.split(offset); + beforeSection.markers.append(beforeMarker); + forEach(afterMarkers, _m => afterSection.markers.append(_m)); + currentSection = afterSection; + } else { + currentSection.markers.append(m.clone()); + } + }); + + return [beforeSection, afterSection]; + } + + splitAtMarker(/*marker, offset=0*/) { + assert('splitAtMarker must be implemented by sub-class', false); + } + + /** + * Split this section's marker (if any) at the given offset, so that + * there is now a marker boundary at that offset (useful for later applying + * a markup to a range) + * @param {Number} sectionOffset The offset relative to start of this section + * @return {EditObject} An edit object with 'removed' and 'added' keys with arrays of Markers. The added markers may be blank. + * After calling `splitMarkerAtOffset(offset)`, there will always be a valid + * result returned from `markerBeforeOffset(offset)`. + */ + splitMarkerAtOffset(sectionOffset) { + assert('Cannot splitMarkerAtOffset when offset is > length', + sectionOffset <= this.length); + let markerOffset; + let len = 0; + let currentMarker = this.markers.head; + let edit = {added: [], removed: []}; + + if (!currentMarker) { + let blankMarker = this.builder.createMarker(); + this.markers.prepend(blankMarker); + edit.added.push(blankMarker); + } else { + while (currentMarker) { + len += currentMarker.length; + if (len === sectionOffset) { + // nothing to do, there is a gap at the requested offset + break; + } else if (len > sectionOffset) { + markerOffset = currentMarker.length - (len - sectionOffset); + let newMarkers = currentMarker.splitAtOffset(markerOffset); + edit.added.push(...newMarkers); + edit.removed.push(currentMarker); + this.markers.splice(currentMarker, 1, newMarkers); + break; + } else { + currentMarker = currentMarker.next; + } + } + } + + return edit; + } + + splitAtPosition(position) { + const {marker, offsetInMarker} = position; + return this.splitAtMarker(marker, offsetInMarker); + } + + // returns the marker just before this offset. + // It is an error to call this method with an offset that is in the middle + // of a marker. + markerBeforeOffset(sectionOffset) { + let len = 0; + let currentMarker = this.markers.head; + + while (currentMarker) { + len += currentMarker.length; + if (len === sectionOffset) { + return currentMarker; + } else { + assert('markerBeforeOffset called with sectionOffset not between markers', + len < sectionOffset); + currentMarker = currentMarker.next; + } + } + } + + markerPositionAtOffset(offset) { + let currentOffset = 0; + let currentMarker; + let remaining = offset; + this.markers.detect((marker) => { + currentOffset = Math.min(remaining, marker.length); + remaining -= currentOffset; + if (remaining === 0) { + currentMarker = marker; + return true; // break out of detect + } + }); + + return {marker:currentMarker, offset:currentOffset}; + } + + get text() { + return reduce(this.markers, (prev, m) => prev + m.value, ''); + } + + get length() { + return reduce(this.markers, (prev, m) => prev + m.length, 0); + } + + /** + * @return {Array} New markers that match the boundaries of the + * range. Does not change the existing markers in this section. + */ + markersFor(headOffset, tailOffset) { + const range = {head: {section:this, offset:headOffset}, + tail: {section:this, offset:tailOffset}}; + + let markers = []; + this._markersInRange(range, (marker, {markerHead, markerTail, isContained}) => { + const cloned = marker.clone(); + if (!isContained) { + // cannot do marker.value.slice if the marker is an atom -- this breaks the atom's "atomic" value + // If a marker is an atom `isContained` should always be true so + // we shouldn't hit this code path. FIXME add tests + cloned.value = marker.value.slice(markerHead, markerTail); + } + markers.push(cloned); + }); + return markers; + } + + markupsInRange(range) { + const markups = new Set(); + this._markersInRange(range, marker => { + marker.markups.forEach(m => markups.add(m)); + }); + return markups.toArray(); + } + + // calls the callback with (marker, {markerHead, markerTail, isContained}) + // for each marker that is wholly or partially contained in the range. + _markersInRange(range, callback) { + const { head, tail } = range; + assert('Cannot call #_markersInRange if range expands beyond this section', + head.section === this && tail.section === this); + const {offset:headOffset} = head, {offset:tailOffset} = tail; + + let currentHead = 0, currentTail = 0, currentMarker = this.markers.head; + + while (currentMarker) { + currentTail += currentMarker.length; + + if (currentTail > headOffset && currentHead < tailOffset) { + let markerHead = Math.max(headOffset - currentHead, 0); + let markerTail = currentMarker.length - + Math.max(currentTail - tailOffset, 0); + let isContained = markerHead === 0 && markerTail === currentMarker.length; + + callback(currentMarker, {markerHead, markerTail, isContained}); + } + + currentHead += currentMarker.length; + currentMarker = currentMarker.next; + + if (currentHead > tailOffset) { break; } + } + } + + // mutates this by appending the other section's (cloned) markers to it + join(otherSection) { + let beforeMarker = this.markers.tail; + let afterMarker = null; + + otherSection.markers.forEach(m => { + if (!m.isBlank) { + m = m.clone(); + this.markers.append(m); + if (!afterMarker) { + afterMarker = m; + } + } + }); + + return { beforeMarker, afterMarker }; + } +} + +const VALID_ATTRIBUTES = [ + 'data-md-text-align' +]; + +/* + * A "mixin" to add section attribute support + * to markup and list sections. + */ +function attributable(ctx) { + ctx.attributes = {}; + + ctx.hasAttribute = key => key in ctx.attributes; + + ctx.setAttribute = (key, value) => { + if (!contains(VALID_ATTRIBUTES, key)) { + throw new Error(`Invalid attribute "${key}" was passed. Constrain attributes to the spec-compliant whitelist.`); + } + ctx.attributes[key] = value; + }; + ctx.removeAttribute = key => { + delete ctx.attributes[key]; + }; + ctx.getAttribute = key => ctx.attributes[key]; + ctx.eachAttribute = cb => { + entries(ctx.attributes).forEach(([k,v]) => cb(k,v)); + }; +} + +// valid values of `tagName` for a MarkupSection +const VALID_MARKUP_SECTION_TAGNAMES = [ + 'aside', + 'blockquote', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'p' +].map(normalizeTagName); + +// valid element names for a MarkupSection. A MarkupSection with a tagName +// not in this will be rendered as a div with a className matching the +// tagName +const MARKUP_SECTION_ELEMENT_NAMES = [ + 'aside', + 'blockquote', + 'h1', + 'h2', + 'h3', + 'h4', + 'h5', + 'h6', + 'p' +].map(normalizeTagName); +const DEFAULT_TAG_NAME = VALID_MARKUP_SECTION_TAGNAMES[8]; + +const MarkupSection = class MarkupSection extends Markerable { + constructor(tagName=DEFAULT_TAG_NAME, markers=[], attributes={}) { + super(MARKUP_SECTION_TYPE, tagName, markers); + + attributable(this); + entries(attributes).forEach(([k,v]) => this.setAttribute(k, v)); + + this.isMarkupSection = true; + } + + isValidTagName(normalizedTagName) { + return contains(VALID_MARKUP_SECTION_TAGNAMES, normalizedTagName); + } + + splitAtMarker(marker, offset=0) { + let [beforeSection, afterSection] = [ + this.builder.createMarkupSection(this.tagName, [], false, this.attributes), + this.builder.createMarkupSection() + ]; + + return this._redistributeMarkers(beforeSection, afterSection, marker, offset); + } +}; + +const CARD_ELEMENT_CLASS_NAME = '__mobiledoc-card'; +const NO_BREAK_SPACE = '\u00A0'; +const TAB_CHARACTER = '\u2003'; +const SPACE = ' '; +const ZWNJ = '\u200c'; +const ATOM_CLASS_NAME = '-mobiledoc-kit__atom'; +const EDITOR_HAS_NO_CONTENT_CLASS_NAME = '__has-no-content'; +const EDITOR_ELEMENT_CLASS_NAME = '__mobiledoc-editor'; + +function createElementFromMarkup(doc, markup) { + let element = doc.createElement(markup.tagName); + Object.keys(markup.attributes).forEach(k => { + element.setAttribute(k, markup.attributes[k]); + }); + return element; +} + +const TWO_SPACES = `${SPACE}${SPACE}`; +const SPACE_AND_NO_BREAK = `${SPACE}${NO_BREAK_SPACE}`; +const SPACES_REGEX = new RegExp(TWO_SPACES, 'g'); +const TAB_REGEX = new RegExp(TAB, 'g'); +const endsWithSpace = function(text) { + return endsWith(text, SPACE); +}; +const startsWithSpace = function(text) { + return startsWith(text, SPACE); +}; + +// FIXME: This can be done more efficiently with a single pass +// building a correct string based on the original. +function renderHTMLText(marker) { + let text = marker.value; + text = text.replace(SPACES_REGEX, SPACE_AND_NO_BREAK) + .replace(TAB_REGEX, TAB_CHARACTER); + + // If the first marker has a leading space or the last marker has a + // trailing space, the browser will collapse the space when we position + // the cursor. + // See https://github.com/bustle/mobiledoc-kit/issues/68 + // and https://github.com/bustle/mobiledoc-kit/issues/75 + if (marker.isMarker && endsWithSpace(text) && !marker.next) { + text = text.substr(0, text.length - 1) + NO_BREAK_SPACE; + } + if (marker.isMarker && startsWithSpace(text) && + (!marker.prev || (marker.prev.isMarker && endsWithSpace(marker.prev.value)))) { + text = NO_BREAK_SPACE + text.substr(1); + } + return text; +} + +// ascends from element upward, returning the last parent node that is not +// parentElement +function penultimateParentOf(element, parentElement) { + while (parentElement && + element.parentNode !== parentElement && + element.parentNode !== document.body // ensure the while loop stops + ) { + element = element.parentNode; + } + return element; +} + +function setSectionAttributesOnElement(section, element) { + section.eachAttribute((key, value) => { + element.setAttribute(key, value); + }); +} + +function renderMarkupSection(section) { + let element; + if (MARKUP_SECTION_ELEMENT_NAMES.indexOf(section.tagName) !== -1) { + element = document.createElement(section.tagName); + } else { + element = document.createElement('div'); + addClassName(element, section.tagName); + } + + setSectionAttributesOnElement(section, element); + + return element; +} + +function renderListSection(section) { + let element = document.createElement(section.tagName); + + setSectionAttributesOnElement(section, element); + + return element; +} + +function renderListItem() { + return document.createElement('li'); +} + +function renderCursorPlaceholder() { + return document.createElement('br'); +} + +function renderInlineCursorPlaceholder() { + return document.createTextNode(ZWNJ); +} + +function renderCard() { + let wrapper = document.createElement('div'); + let cardElement = document.createElement('div'); + cardElement.contentEditable = false; + addClassName(cardElement, CARD_ELEMENT_CLASS_NAME); + wrapper.appendChild(renderInlineCursorPlaceholder()); + wrapper.appendChild(cardElement); + wrapper.appendChild(renderInlineCursorPlaceholder()); + return { wrapper, cardElement }; +} + +/** + * Wrap the element in all of the opened markups + * @return {DOMElement} the wrapped element + * @private + */ +function wrapElement(element, openedMarkups) { + let wrappedElement = element; + + for (let i=openedMarkups.length - 1; i>=0; i--) { + let markup = openedMarkups[i]; + let openedElement = createElementFromMarkup(document, markup); + openedElement.appendChild(wrappedElement); + wrappedElement = openedElement; + } + + return wrappedElement; +} + +// Attach the element to its parent element at the correct position based on the +// previousRenderNode +function attachElementToParent(element, parentElement, previousRenderNode=null) { + if (previousRenderNode) { + let previousSibling = previousRenderNode.element; + let previousSiblingPenultimate = penultimateParentOf(previousSibling, + parentElement); + parentElement.insertBefore(element, previousSiblingPenultimate.nextSibling); + } else { + parentElement.insertBefore(element, parentElement.firstChild); + } +} + +function renderAtom(atom, element, previousRenderNode) { + let atomElement = document.createElement('span'); + atomElement.contentEditable = false; + + let wrapper = document.createElement('span'); + addClassName(wrapper, ATOM_CLASS_NAME); + let headTextNode = renderInlineCursorPlaceholder(); + let tailTextNode = renderInlineCursorPlaceholder(); + + wrapper.appendChild(headTextNode); + wrapper.appendChild(atomElement); + wrapper.appendChild(tailTextNode); + + let wrappedElement = wrapElement(wrapper, atom.openedMarkups); + attachElementToParent(wrappedElement, element, previousRenderNode); + + return { + markupElement: wrappedElement, + wrapper, + atomElement, + headTextNode, + tailTextNode + }; +} + +function getNextMarkerElement(renderNode) { + let element = renderNode.element.parentNode; + let marker = renderNode.postNode; + let closedCount = marker.closedMarkups.length; + + while (closedCount--) { + element = element.parentNode; + } + return element; +} + +/** + * Render the marker + * @param {Marker} marker the marker to render + * @param {DOMNode} element the element to attach the rendered marker to + * @param {RenderNode} [previousRenderNode] The render node before this one, which + * affects the determination of where to insert this rendered marker. + * @return {Object} With properties `element` and `markupElement`. + * The element (textNode) that has the text for + * this marker, and the outermost rendered element. If the marker has no + * markups, element and markupElement will be the same textNode + * @private + */ +function renderMarker(marker, parentElement, previousRenderNode) { + let text = renderHTMLText(marker); + + let element = document.createTextNode(text); + let markupElement = wrapElement(element, marker.openedMarkups); + attachElementToParent(markupElement, parentElement, previousRenderNode); + + return { element, markupElement }; +} + +// Attach the render node's element to the DOM, +// replacing the originalElement if it exists +function attachRenderNodeElementToDOM(renderNode, originalElement=null) { + const element = renderNode.element; + const hasRendered = !!originalElement; + + if (hasRendered) { + let parentElement = renderNode.parent.element; + parentElement.replaceChild(element, originalElement); + } else { + let parentElement, nextSiblingElement; + if (renderNode.prev) { + let previousElement = renderNode.prev.element; + parentElement = previousElement.parentNode; + nextSiblingElement = previousElement.nextSibling; + } else { + parentElement = renderNode.parent.element; + nextSiblingElement = parentElement.firstChild; + } + parentElement.insertBefore(element, nextSiblingElement); + } +} + +function removeRenderNodeSectionFromParent(renderNode, section) { + const parent = renderNode.parent.postNode; + parent.sections.remove(section); +} + +function removeRenderNodeElementFromParent(renderNode) { + if (renderNode.element && renderNode.element.parentNode) { + renderNode.element.parentNode.removeChild(renderNode.element); + } +} + +function validateCards(cards=[]) { + forEach(cards, card => { + assert( + `Card "${card.name}" must define type "dom", has: "${card.type}"`, + card.type === 'dom' + ); + assert( + `Card "${card.name}" must define \`render\` method`, + !!card.render + ); + }); + return cards; +} + +function validateAtoms(atoms=[]) { + forEach(atoms, atom => { + assert( + `Atom "${atom.name}" must define type "dom", has: "${atom.type}"`, + atom.type === 'dom' + ); + assert( + `Atom "${atom.name}" must define \`render\` method`, + !!atom.render + ); + }); + return atoms; +} + +class Visitor$1 { + constructor(editor, cards, atoms, unknownCardHandler, unknownAtomHandler, options) { + this.editor = editor; + this.cards = validateCards(cards); + this.atoms = validateAtoms(atoms); + this.unknownCardHandler = unknownCardHandler; + this.unknownAtomHandler = unknownAtomHandler; + this.options = options; + } + + _findCard(cardName) { + let card = detect(this.cards, card => card.name === cardName); + return card || this._createUnknownCard(cardName); + } + + _createUnknownCard(cardName) { + assert( + `Unknown card "${cardName}" found, but no unknownCardHandler is defined`, + !!this.unknownCardHandler + ); + + return { + name: cardName, + type: 'dom', + render: this.unknownCardHandler, + edit: this.unknownCardHandler + }; + } + + _findAtom(atomName) { + let atom = detect(this.atoms, atom => atom.name === atomName); + return atom || this._createUnknownAtom(atomName); + } + + _createUnknownAtom(atomName) { + assert( + `Unknown atom "${atomName}" found, but no unknownAtomHandler is defined`, + !!this.unknownAtomHandler + ); + + return { + name: atomName, + type: 'dom', + render: this.unknownAtomHandler + }; + } + + [POST_TYPE](renderNode, post, visit) { + if (!renderNode.element) { + renderNode.element = document.createElement('div'); + } + addClassName(renderNode.element, EDITOR_ELEMENT_CLASS_NAME); + if (post.hasContent) { + removeClassName(renderNode.element, EDITOR_HAS_NO_CONTENT_CLASS_NAME); + } else { + addClassName(renderNode.element, EDITOR_HAS_NO_CONTENT_CLASS_NAME); + } + visit(renderNode, post.sections); + } + + [MARKUP_SECTION_TYPE](renderNode, section, visit) { + const originalElement = renderNode.element; + + // Always rerender the section -- its tag name or attributes may have changed. + // TODO make this smarter, only rerendering and replacing the element when necessary + renderNode.element = renderMarkupSection(section); + renderNode.cursorElement = null; + attachRenderNodeElementToDOM(renderNode, originalElement); + + if (section.isBlank) { + let cursorPlaceholder = renderCursorPlaceholder(); + renderNode.element.appendChild(cursorPlaceholder); + renderNode.cursorElement = cursorPlaceholder; + } else { + const visitAll = true; + visit(renderNode, section.markers, visitAll); + } + } + + [LIST_SECTION_TYPE](renderNode, section, visit) { + const originalElement = renderNode.element; + + renderNode.element = renderListSection(section); + attachRenderNodeElementToDOM(renderNode, originalElement); + + const visitAll = true; + visit(renderNode, section.items, visitAll); + } + + [LIST_ITEM_TYPE](renderNode, item, visit) { + // FIXME do we need to do anything special for rerenders? + renderNode.element = renderListItem(); + renderNode.cursorElement = null; + attachRenderNodeElementToDOM(renderNode, null); + + if (item.isBlank) { + let cursorPlaceholder = renderCursorPlaceholder(); + renderNode.element.appendChild(cursorPlaceholder); + renderNode.cursorElement = cursorPlaceholder; + } else { + const visitAll = true; + visit(renderNode, item.markers, visitAll); + } + } + + [MARKER_TYPE](renderNode, marker) { + let parentElement; + + if (renderNode.prev) { + parentElement = getNextMarkerElement(renderNode.prev); + } else { + parentElement = renderNode.parent.element; + } + + let { element, markupElement } = + renderMarker(marker, parentElement, renderNode.prev); + + renderNode.element = element; + renderNode.markupElement = markupElement; + } + + [IMAGE_SECTION_TYPE](renderNode, section) { + if (renderNode.element) { + if (renderNode.element.src !== section.src) { + renderNode.element.src = section.src; + } + } else { + let element = document.createElement('img'); + element.src = section.src; + if (renderNode.prev) { + let previousElement = renderNode.prev.element; + let nextElement = previousElement.nextSibling; + if (nextElement) { + nextElement.parentNode.insertBefore(element, nextElement); + } + } + if (!element.parentNode) { + renderNode.parent.element.appendChild(element); + } + renderNode.element = element; + } + } + + [CARD_TYPE](renderNode, section) { + const originalElement = renderNode.element; + const {editor, options} = this; + + const card = this._findCard(section.name); + + let { wrapper, cardElement } = renderCard(); + renderNode.element = wrapper; + attachRenderNodeElementToDOM(renderNode, originalElement); + + const cardNode = new CardNode( + editor, card, section, cardElement, options); + renderNode.cardNode = cardNode; + + const initialMode = section._initialMode; + cardNode[initialMode](); + } + + [ATOM_TYPE](renderNode, atomModel) { + let parentElement; + + if (renderNode.prev) { + parentElement = getNextMarkerElement(renderNode.prev); + } else { + parentElement = renderNode.parent.element; + } + + const { editor, options } = this; + const { + wrapper, + markupElement, + atomElement, + headTextNode, + tailTextNode + } = renderAtom(atomModel, parentElement, renderNode.prev); + const atom = this._findAtom(atomModel.name); + + let atomNode = renderNode.atomNode; + if (!atomNode) { + // create new AtomNode + atomNode = new AtomNode(editor, atom, atomModel, atomElement, options); + } else { + // retarget atomNode to new atom element + atomNode.element = atomElement; + } + + atomNode.render(); + + renderNode.atomNode = atomNode; + renderNode.element = wrapper; + renderNode.headTextNode = headTextNode; + renderNode.tailTextNode = tailTextNode; + renderNode.markupElement = markupElement; + } +} + +let destroyHooks = { + [POST_TYPE](/*renderNode, post*/) { + assert('post destruction is not supported by the renderer', false); + }, + + [MARKUP_SECTION_TYPE](renderNode, section) { + removeRenderNodeSectionFromParent(renderNode, section); + removeRenderNodeElementFromParent(renderNode); + }, + + [LIST_SECTION_TYPE](renderNode, section) { + removeRenderNodeSectionFromParent(renderNode, section); + removeRenderNodeElementFromParent(renderNode); + }, + + [LIST_ITEM_TYPE](renderNode, li) { + removeRenderNodeSectionFromParent(renderNode, li); + removeRenderNodeElementFromParent(renderNode); + }, + + [MARKER_TYPE](renderNode, marker) { + // FIXME before we render marker, should delete previous renderNode's element + // and up until the next marker element + + // If an atom throws during render we may end up later destroying a renderNode + // that has not rendered yet, so exit early here if so. + if (!renderNode.isRendered) { + return; + } + let { markupElement } = renderNode; + + if (marker.section) { + marker.section.markers.remove(marker); + } + + if (markupElement.parentNode) { + // if no parentNode, the browser already removed this element + markupElement.parentNode.removeChild(markupElement); + } + }, + + [IMAGE_SECTION_TYPE](renderNode, section) { + removeRenderNodeSectionFromParent(renderNode, section); + removeRenderNodeElementFromParent(renderNode); + }, + + [CARD_TYPE](renderNode, section) { + if (renderNode.cardNode) { + renderNode.cardNode.teardown(); + } + removeRenderNodeSectionFromParent(renderNode, section); + removeRenderNodeElementFromParent(renderNode); + }, + + [ATOM_TYPE](renderNode, atom) { + if (renderNode.atomNode) { + renderNode.atomNode.teardown(); + } + + // an atom is a kind of marker so just call its destroy hook vs copying here + destroyHooks[MARKER_TYPE](renderNode, atom); + } +}; + +// removes children from parentNode (a RenderNode) that are scheduled for removal +function removeDestroyedChildren(parentNode, forceRemoval=false) { + let child = parentNode.childNodes.head; + let nextChild, method; + while (child) { + nextChild = child.next; + if (child.isRemoved || forceRemoval) { + removeDestroyedChildren(child, true); + method = child.postNode.type; + assert(`editor-dom cannot destroy "${method}"`, !!destroyHooks[method]); + destroyHooks[method](child, child.postNode); + parentNode.childNodes.remove(child); + } + child = nextChild; + } +} + +// Find an existing render node for the given postNode, or +// create one, insert it into the tree, and return it +function lookupNode(renderTree, parentNode, postNode, previousNode) { + if (postNode.renderNode) { + return postNode.renderNode; + } else { + const renderNode = renderTree.buildRenderNode(postNode); + parentNode.childNodes.insertAfter(renderNode, previousNode); + return renderNode; + } +} + +class Renderer { + constructor(editor, cards, atoms, unknownCardHandler, unknownAtomHandler, options) { + this.editor = editor; + this.visitor = new Visitor$1(editor, cards, atoms, unknownCardHandler, unknownAtomHandler, options); + this.nodes = []; + this.hasRendered = false; + } + + destroy() { + if (!this.hasRendered) { + return; + } + let renderNode = this.renderTree.rootNode; + let force = true; + removeDestroyedChildren(renderNode, force); + } + + visit(renderTree, parentNode, postNodes, visitAll=false) { + let previousNode; + postNodes.forEach(postNode => { + let node = lookupNode(renderTree, parentNode, postNode, previousNode); + if (node.isDirty || visitAll) { + this.nodes.push(node); + } + previousNode = node; + }); + } + + render(renderTree) { + this.hasRendered = true; + this.renderTree = renderTree; + let renderNode = renderTree.rootNode; + let method, postNode; + + while (renderNode) { + removeDestroyedChildren(renderNode); + postNode = renderNode.postNode; + + method = postNode.type; + assert(`EditorDom visitor cannot handle type ${method}`, !!this.visitor[method]); + this.visitor[method](renderNode, postNode, + (...args) => this.visit(renderTree, ...args)); + renderNode.markClean(); + renderNode = this.nodes.shift(); + } + } +} + +const VALID_LIST_SECTION_TAGNAMES = [ + 'ul', 'ol' +].map(normalizeTagName); + +const DEFAULT_TAG_NAME$1 = VALID_LIST_SECTION_TAGNAMES[0]; + +class ListSection extends Section { + constructor(tagName=DEFAULT_TAG_NAME$1, items=[], attributes={}) { + super(LIST_SECTION_TYPE); + this.tagName = tagName; + this.isListSection = true; + this.isLeafSection = false; + + attributable(this); + entries(attributes).forEach(([k,v]) => this.setAttribute(k, v)); + + this.items = new LinkedList({ + adoptItem: i => { + assert(`Cannot insert non-list-item to list (is: ${i.type})`, + i.isListItem); + i.section = i.parent = this; + }, + freeItem: i => i.section = i.parent = null + }); + this.sections = this.items; + + items.forEach(i => this.items.append(i)); + } + + canJoin() { + return false; + } + + isValidTagName(normalizedTagName) { + return contains(VALID_LIST_SECTION_TAGNAMES, normalizedTagName); + } + + headPosition() { + return this.items.head.headPosition(); + } + + tailPosition() { + return this.items.tail.tailPosition(); + } + + get isBlank() { + return this.items.isEmpty; + } + + clone() { + let newSection = this.builder.createListSection(this.tagName); + forEach(this.items, i => newSection.items.append(i.clone())); + return newSection; + } + + /** + * Mutates this list + * @param {ListSection|Markerable} + * @return null + */ + join(other) { + if (other.isListSection) { + other.items.forEach(i => this.join(i)); + } else if (other.isMarkerable) { + let item = this.builder.createListItem(); + item.join(other); + this.items.append(item); + } + } +} + +const VALID_LIST_ITEM_TAGNAMES = [ + 'li' +].map(normalizeTagName); + +class ListItem extends Markerable { + constructor(tagName, markers=[]) { + super(LIST_ITEM_TYPE, tagName, markers); + this.isListItem = true; + this.isNested = true; + } + + isValidTagName(normalizedTagName) { + return contains(VALID_LIST_ITEM_TAGNAMES, normalizedTagName); + } + + splitAtMarker(marker, offset=0) { + // FIXME need to check if we are going to split into two list items + // or a list item and a new markup section: + const isLastItem = !this.next; + const createNewSection = (!marker && offset === 0 && isLastItem); + + let [beforeSection, afterSection] = [ + this.builder.createListItem(), + createNewSection ? this.builder.createMarkupSection() : + this.builder.createListItem() + ]; + + return this._redistributeMarkers( + beforeSection, afterSection, marker, offset); + } + + get post() { + return this.section.post; + } +} + +const VALID_MARKUP_TAGNAMES = [ + 'a', + 'b', + 'code', + 'em', + 'i', + 's', // strikethrough + 'strong', + 'sub', // subscript + 'sup', // superscript + 'u' +].map(normalizeTagName); + +const VALID_ATTRIBUTES$1 = [ + 'href', + 'rel' +]; + +/** + * A Markup is similar with an inline HTML tag that might be added to + * text to modify its meaning and/or display. Examples of types of markup + * that could be added are bold ('b'), italic ('i'), strikethrough ('s'), and `a` tags (links). + * @property {String} tagName + */ +class Markup { + /* + * @param {Object} attributes key-values + */ + constructor(tagName, attributes={}) { + this.tagName = normalizeTagName(tagName); + + assert('Must use attributes object param (not array) for Markup', + !Array.isArray(attributes)); + + this.attributes = filterObject(attributes, VALID_ATTRIBUTES$1); + this.type = MARKUP_TYPE; + + assert(`Cannot create markup of tagName ${tagName}`, + VALID_MARKUP_TAGNAMES.indexOf(this.tagName) !== -1); + } + + /** + * Whether text in the forward direction of the cursor (i.e. to the right in ltr text) + * should be considered to have this markup applied to it. + * @private + */ + isForwardInclusive() { + return this.tagName === normalizeTagName("a") ? false : true; + } + + isBackwardInclusive() { + return false; + } + + hasTag(tagName) { + return this.tagName === normalizeTagName(tagName); + } + + /** + * Returns the attribute value + * @param {String} name, e.g. "href" + */ + getAttribute(name) { + return this.attributes[name]; + } + + static isValidElement(element) { + const tagName = normalizeTagName(element.tagName); + return VALID_MARKUP_TAGNAMES.indexOf(tagName) !== -1; + } +} + +const SKIPPABLE_ELEMENT_TAG_NAMES = [ + 'style', 'head', 'title', 'meta' +].map(normalizeTagName); + +const NEWLINES = /\n/g; +function sanitize(text) { + return text.replace(NEWLINES, ' '); +} + +/** + * parses an element into a section, ignoring any non-markup + * elements contained within + * @private + */ +class SectionParser { + constructor(builder, options={}) { + this.builder = builder; + this.plugins = options.plugins || []; + } + + parse(element) { + if (this._isSkippable(element)) { + return []; + } + this.sections = []; + this.state = {}; + + this._updateStateFromElement(element); + + let finished = false; + + // top-level text nodes will be run through parseNode later so avoid running + // the node through parserPlugins twice + if (!isTextNode(element)) { + finished = this.runPlugins(element); + } + + if (!finished) { + let childNodes = isTextNode(element) ? [element] : element.childNodes; + + forEach(childNodes, el => { + this.parseNode(el); + }); + } + + this._closeCurrentSection(); + + return this.sections; + } + + runPlugins(node) { + let isNodeFinished = false; + let env = { + addSection: (section) => { + // avoid creating empty paragraphs due to wrapper elements around + // parser-plugin-handled elements + if (this.state.section.isMarkerable && !this.state.text && !this.state.section.text) { + this.state.section = null; + } else { + this._closeCurrentSection(); + } + this.sections.push(section); + }, + addMarkerable: (marker) => { + let { state } = this; + let { section } = state; + assert( + 'Markerables can only be appended to markup sections and list item sections', + section && section.isMarkerable + ); + if (state.text) { + this._createMarker(); + } + section.markers.append(marker); + }, + nodeFinished() { + isNodeFinished = true; + } + }; + for (let i=0; i. + // deals with typical case of
  • Text

  • Text

  • + if ( + this.state.section.isListItem && + tagName === 'p' && + !node.nextSibling && + contains(VALID_LIST_ITEM_TAGNAMES, normalizeTagName(node.parentElement.tagName)) + ) { + this.parseElementNode(node); + return; + } + + // avoid creating empty paragraphs due to wrapper elements around + // section-creating elements + if (this.state.section.isMarkerable && !this.state.text && this.state.section.markers.length === 0) { + this.state.section = null; + } else { + this._closeCurrentSection(); + } + + this._updateStateFromElement(node); + } + + if (this.state.section.isListSection) { + // ensure the list section is closed and added to the sections list. + // _closeCurrentSection handles pushing list items onto the list section + this._closeCurrentSection(); + + forEach(node.childNodes, (node) => { + this.parseNode(node); + }); + return; + } + } + + switch (node.nodeType) { + case NODE_TYPES.TEXT: + this.parseTextNode(node); + break; + case NODE_TYPES.ELEMENT: + this.parseElementNode(node); + break; + } + } + + parseElementNode(element) { + let { state } = this; + + const markups = this._markupsFromElement(element); + if (markups.length && state.text.length && state.section.isMarkerable) { + this._createMarker(); + } + state.markups.push(...markups); + + forEach(element.childNodes, (node) => { + this.parseNode(node); + }); + + if (markups.length && state.text.length && state.section.isMarkerable) { + // create the marker started for this node + this._createMarker(); + } + + // pop the current markups from the stack + state.markups.splice(-markups.length, markups.length); + } + + parseTextNode(textNode) { + let { state } = this; + state.text += sanitize(textNode.textContent); + } + + _updateStateFromElement(element) { + let { state } = this; + state.section = this._createSectionFromElement(element); + state.markups = this._markupsFromElement(element); + state.text = ''; + } + + _closeCurrentSection() { + let { sections, state } = this; + let lastSection = sections[sections.length - 1]; + + if (!state.section) { + return; + } + + // close a trailing text node if it exists + if (state.text.length && state.section.isMarkerable) { + this._createMarker(); + } + + // push listItems onto the listSection or add a new section + if (state.section.isListItem && lastSection && lastSection.isListSection) { + trimSectionText(state.section); + lastSection.items.append(state.section); + } else { + // avoid creating empty markup sections, especially useful for indented source + if ( + state.section.isMarkerable && + !state.section.text.trim() && + !any(state.section.markers, marker => marker.isAtom) + ) { + state.section = null; + state.text = ''; + return; + } + + // remove empty list sections before creating a new section + if (lastSection && lastSection.isListSection && lastSection.items.length === 0) { + sections.pop(); + } + + sections.push(state.section); + } + + state.section = null; + state.text = ''; + } + + _markupsFromElement(element) { + let { builder } = this; + let markups = []; + if (isTextNode(element)) { + return markups; + } + + const tagName = normalizeTagName(element.tagName); + if (this._isValidMarkupForElement(tagName, element)) { + markups.push(builder.createMarkup(tagName, getAttributes(element))); + } + + this._markupsFromElementStyle(element).forEach( + markup => markups.push(markup) + ); + + return markups; + } + + _isValidMarkupForElement(tagName, element) { + if (VALID_MARKUP_TAGNAMES.indexOf(tagName) === -1) { + return false; + } else if (tagName === 'b') { + // google docs add a that should not + // create a "b" markup + return element.style.fontWeight !== 'normal'; + } + return true; + } + + _markupsFromElementStyle(element) { + let { builder } = this; + let markups = []; + let { fontStyle, fontWeight } = element.style; + if (fontStyle === 'italic') { + markups.push(builder.createMarkup('em')); + } + if (fontWeight === 'bold' || fontWeight === '700') { + markups.push(builder.createMarkup('strong')); + } + return markups; + } + + _createMarker() { + let { state } = this; + let text = transformHTMLText(state.text); + let marker = this.builder.createMarker(text, state.markups); + state.section.markers.append(marker); + state.text = ''; + } + + _getSectionDetails(element) { + let sectionType, + tagName, + inferredTagName = false; + if (isTextNode(element)) { + tagName = DEFAULT_TAG_NAME; + sectionType = MARKUP_SECTION_TYPE; + inferredTagName = true; + } else { + tagName = normalizeTagName(element.tagName); + + if (contains(VALID_LIST_SECTION_TAGNAMES, tagName)) { + sectionType = LIST_SECTION_TYPE; + } else if (contains(VALID_LIST_ITEM_TAGNAMES, tagName)) { + sectionType = LIST_ITEM_TYPE; + } else if (contains(VALID_MARKUP_SECTION_TAGNAMES, tagName)) { + sectionType = MARKUP_SECTION_TYPE; + } else { + sectionType = MARKUP_SECTION_TYPE; + tagName = DEFAULT_TAG_NAME; + inferredTagName = true; + } + } + + return {sectionType, tagName, inferredTagName}; + } + + _createSectionFromElement(element) { + let { builder } = this; + + let section; + let {tagName, sectionType, inferredTagName} = + this._getSectionDetails(element); + + switch (sectionType) { + case LIST_SECTION_TYPE: + section = builder.createListSection(tagName); + break; + case LIST_ITEM_TYPE: + section = builder.createListItem(); + break; + case MARKUP_SECTION_TYPE: + section = builder.createMarkupSection(tagName); + section._inferredTagName = inferredTagName; + break; + default: + assert('Cannot parse section from element', false); + } + + return section; + } + + _isSkippable(element) { + return isCommentNode(element) || + (element.nodeType === NODE_TYPES.ELEMENT && + contains(SKIPPABLE_ELEMENT_TAG_NAMES, + normalizeTagName(element.tagName))); + } +} + +const GOOGLE_DOCS_CONTAINER_ID_REGEX = /^docs\-internal\-guid/; + +const NO_BREAK_SPACE_REGEX = new RegExp(NO_BREAK_SPACE, 'g'); +const TAB_CHARACTER_REGEX = new RegExp(TAB_CHARACTER, 'g'); +function transformHTMLText(textContent) { + let text = textContent; + text = text.replace(NO_BREAK_SPACE_REGEX, ' '); + text = text.replace(TAB_CHARACTER_REGEX, TAB); + return text; +} + +function trimSectionText(section) { + if (section.isMarkerable && section.markers.length) { + let { head, tail } = section.markers; + head.value = head.value.replace(/^\s+/, ''); + tail.value = tail.value.replace(/\s+$/, ''); + } +} + +function isGoogleDocsContainer(element) { + return !isTextNode(element) && + !isCommentNode(element) && + normalizeTagName(element.tagName) === normalizeTagName('b') && + GOOGLE_DOCS_CONTAINER_ID_REGEX.test(element.id); +} + +function detectRootElement(element) { + let childNodes = element.childNodes || []; + let googleDocsContainer = detect(childNodes, isGoogleDocsContainer); + + if (googleDocsContainer) { + return googleDocsContainer; + } else { + return element; + } +} + +const TAG_REMAPPING = { + 'b': 'strong', + 'i': 'em' +}; + +function remapTagName(tagName) { + let normalized = normalizeTagName(tagName); + let remapped = TAG_REMAPPING[normalized]; + return remapped || normalized; +} + +function trim(str) { + return str.replace(/^\s+/, '').replace(/\s+$/, ''); +} + +function walkMarkerableNodes(parent, callback) { + let currentNode = parent; + + if ( + isTextNode(currentNode) || + ( + isElementNode(currentNode) && + currentNode.classList.contains(ATOM_CLASS_NAME) + ) + ) { + callback(currentNode); + } else { + currentNode = currentNode.firstChild; + while (currentNode) { + walkMarkerableNodes(currentNode, callback); + currentNode = currentNode.nextSibling; + } + } +} + +/** + * Parses DOM element -> Post + * @private + */ +class DOMParser { + constructor(builder, options={}) { + this.builder = builder; + this.sectionParser = new SectionParser(this.builder, options); + } + + parse(element) { + const post = this.builder.createPost(); + let rootElement = detectRootElement(element); + + this._eachChildNode(rootElement, child => { + let sections = this.parseSections(child); + this.appendSections(post, sections); + }); + + // trim leading/trailing whitespace of markerable sections to avoid + // unnessary whitespace from indented HTML input + forEach(post.sections, section => trimSectionText(section)); + + return post; + } + + appendSections(post, sections) { + forEach(sections, section => this.appendSection(post, section)); + } + + appendSection(post, section) { + if ( + section.isBlank || + (section.isMarkerable && + trim(section.text) === "" && + !any(section.markers, marker => marker.isAtom)) + ) { + return; + } + + let lastSection = post.sections.tail; + if (lastSection && + lastSection._inferredTagName && + section._inferredTagName && + lastSection.tagName === section.tagName) { + lastSection.join(section); + } else { + post.sections.append(section); + } + } + + _eachChildNode(element, callback) { + let nodes = isTextNode(element) ? [element] : element.childNodes; + forEach(nodes, node => callback(node)); + } + + parseSections(element) { + return this.sectionParser.parse(element); + } + + // walk up from the textNode until the rootNode, converting each + // parentNode into a markup + collectMarkups(textNode, rootNode) { + let markups = []; + let currentNode = textNode.parentNode; + while (currentNode && currentNode !== rootNode) { + let markup = this.markupFromNode(currentNode); + if (markup) { + markups.push(markup); + } + + currentNode = currentNode.parentNode; + } + return markups; + } + + // Turn an element node into a markup + markupFromNode(node) { + if (Markup.isValidElement(node)) { + let tagName = remapTagName(node.tagName); + let attributes = getAttributes(node); + return this.builder.createMarkup(tagName, attributes); + } + } + + // FIXME should move to the section parser? + // FIXME the `collectMarkups` logic could simplify the section parser? + reparseSection(section, renderTree) { + switch (section.type) { + case LIST_SECTION_TYPE: + return this.reparseListSection(section, renderTree); + case LIST_ITEM_TYPE: + return this.reparseListItem(section, renderTree); + case MARKUP_SECTION_TYPE: + return this.reparseMarkupSection(section, renderTree); + default: + return; // can only parse the above types + } + } + + reparseMarkupSection(section, renderTree) { + return this._reparseSectionContainingMarkers(section, renderTree); + } + + reparseListItem(listItem, renderTree) { + return this._reparseSectionContainingMarkers(listItem, renderTree); + } + + reparseListSection(listSection, renderTree) { + listSection.items.forEach(li => this.reparseListItem(li, renderTree)); + } + + _reparseSectionContainingMarkers(section, renderTree) { + let element = section.renderNode.element; + let seenRenderNodes = []; + let previousMarker; + + walkMarkerableNodes(element, (node) => { + let marker; + let renderNode = renderTree.getElementRenderNode(node); + if (renderNode) { + if (renderNode.postNode.isMarker) { + let text = transformHTMLText(node.textContent); + let markups = this.collectMarkups(node, element); + if (text.length) { + marker = renderNode.postNode; + marker.value = text; + marker.markups = markups; + } else { + renderNode.scheduleForRemoval(); + } + } else if (renderNode.postNode.isAtom) { + let { headTextNode, tailTextNode } = renderNode; + if (headTextNode.textContent !== ZWNJ) { + let value = headTextNode.textContent.replace(new RegExp(ZWNJ, 'g'), ''); + headTextNode.textContent = ZWNJ; + if (previousMarker && previousMarker.isMarker) { + previousMarker.value += value; + if (previousMarker.renderNode) { + previousMarker.renderNode.markDirty(); + } + } else { + let postNode = renderNode.postNode; + let newMarkups = postNode.markups.slice(); + let newPreviousMarker = this.builder.createMarker(value, newMarkups); + section.markers.insertBefore(newPreviousMarker, postNode); + + let newPreviousRenderNode = renderTree.buildRenderNode(newPreviousMarker); + newPreviousRenderNode.markDirty(); + section.renderNode.markDirty(); + + seenRenderNodes.push(newPreviousRenderNode); + section.renderNode.childNodes.insertBefore(newPreviousRenderNode, + renderNode); + } + } + if (tailTextNode.textContent !== ZWNJ) { + let value = tailTextNode.textContent.replace(new RegExp(ZWNJ, 'g'), ''); + tailTextNode.textContent = ZWNJ; + + if (renderNode.postNode.next && renderNode.postNode.next.isMarker) { + let nextMarker = renderNode.postNode.next; + + if (nextMarker.renderNode) { + let nextValue = nextMarker.renderNode.element.textContent; + nextMarker.renderNode.element.textContent = value + nextValue; + } else { + let nextValue = value + nextMarker.value; + nextMarker.value = nextValue; + } + } else { + let postNode = renderNode.postNode; + let newMarkups = postNode.markups.slice(); + let newMarker = this.builder.createMarker(value, newMarkups); + + section.markers.insertAfter(newMarker, postNode); + + let newRenderNode = renderTree.buildRenderNode(newMarker); + seenRenderNodes.push(newRenderNode); + + newRenderNode.markDirty(); + section.renderNode.markDirty(); + + section.renderNode.childNodes.insertAfter(newRenderNode, renderNode); + } + } + if (renderNode) { + marker = renderNode.postNode; + } + } + } else if (isTextNode(node)) { + let text = transformHTMLText(node.textContent); + let markups = this.collectMarkups(node, element); + marker = this.builder.createMarker(text, markups); + + renderNode = renderTree.buildRenderNode(marker); + renderNode.element = node; + renderNode.markClean(); + section.renderNode.markDirty(); + + let previousRenderNode = previousMarker && previousMarker.renderNode; + section.markers.insertAfter(marker, previousMarker); + section.renderNode.childNodes.insertAfter(renderNode, previousRenderNode); + } + + if (renderNode) { + seenRenderNodes.push(renderNode); + } + previousMarker = marker; + }); + + let renderNode = section.renderNode.childNodes.head; + while (renderNode) { + if (seenRenderNodes.indexOf(renderNode) === -1) { + renderNode.scheduleForRemoval(); + } + renderNode = renderNode.next; + } + } +} + +class HTMLParser { + constructor(builder, options={}) { + assert('Must pass builder to HTMLParser', builder); + this.builder = builder; + this.options = options; + } + + /** + * @param {String} html to parse + * @return {Post} A post abstract + */ + parse(html) { + let dom = parseHTML(html); + let parser = new DOMParser(this.builder, this.options); + return parser.parse(dom); + } +} + +class RenderNode extends LinkedItem { + constructor(postNode, renderTree) { + super(); + this.parent = null; + this.isDirty = true; + this.isRemoved = false; + this.postNode = postNode; + this._childNodes = null; + this._element = null; + this._cursorElement = null; // blank render nodes need a cursor element + this.renderTree = renderTree; + + // RenderNodes for Markers keep track of their markupElement + this.markupElement = null; + + // RenderNodes for Atoms use these properties + this.headTextNode = null; + this.tailTextNode = null; + this.atomNode = null; + + // RenderNodes for cards use this property + this.cardNode = null; + } + isAttached() { + assert('Cannot check if a renderNode is attached without an element.', + !!this.element); + return containsNode(this.renderTree.rootElement, this.element); + } + get childNodes() { + if (!this._childNodes) { + this._childNodes = new LinkedList({ + adoptItem: item => item.parent = this, + freeItem: item => item.destroy() + }); + } + return this._childNodes; + } + scheduleForRemoval() { + this.isRemoved = true; + if (this.parent) { this.parent.markDirty(); } + } + markDirty() { + this.isDirty = true; + if (this.parent) { this.parent.markDirty(); } + } + get isRendered() { + return !!this.element; + } + markClean() { + this.isDirty = false; + } + set element(element) { + const currentElement = this._element; + this._element = element; + + if (currentElement) { + this.renderTree.removeElementRenderNode(currentElement); + } + + if (element) { + this.renderTree.setElementRenderNode(element, this); + } + } + get element() { + return this._element; + } + set cursorElement(cursorElement) { + this._cursorElement = cursorElement; + } + get cursorElement() { + return this._cursorElement || this.element; + } + destroy() { + this.element = null; + this.parent = null; + this.postNode = null; + this.renderTree = null; + } + reparsesMutationOfChildNode(node) { + if (this.postNode.isCardSection) { + return !containsNode(this.cardNode.element, node); + } else if (this.postNode.isAtom) { + return !containsNode(this.atomNode.element, node); + } + return true; + } +} + +// start at one to make the falsy semantics easier +let uuidGenerator = 1; + +class ElementMap { + constructor() { + this._map = {}; + } + set(key, value) { + let uuid = key._uuid; + if (!uuid) { + key._uuid = uuid = '' + uuidGenerator++; + } + this._map[uuid] = value; + } + get(key) { + if (key._uuid) { + return this._map[key._uuid]; + } + return null; + } + remove(key) { + assert('tried to fetch a value for an element not seen before', !!key._uuid); + delete this._map[key._uuid]; + } + +} + +class RenderTree { + constructor(rootPostNode) { + this._rootNode = this.buildRenderNode(rootPostNode); + this._elements = new ElementMap(); + } + /* + * @return {RenderNode} The root render node in this tree + */ + get rootNode() { + return this._rootNode; + } + /** + * @return {Boolean} + */ + get isDirty() { + return this.rootNode && this.rootNode.isDirty; + } + /* + * @return {DOMNode} The root DOM element in this tree + */ + get rootElement() { + return this.rootNode.element; + } + /* + * @param {DOMNode} element + * @return {RenderNode} The renderNode for this element, if any + */ + getElementRenderNode(element) { + return this._elements.get(element); + } + setElementRenderNode(element, renderNode) { + this._elements.set(element, renderNode); + } + removeElementRenderNode(element) { + this._elements.remove(element); + } + /** + * @param {DOMNode} element + * Walk up from the dom element until we find a renderNode element + */ + findRenderNodeFromElement(element, conditionFn=()=>true) { + let renderNode; + while (element) { + renderNode = this.getElementRenderNode(element); + if (renderNode && conditionFn(renderNode)) { + return renderNode; + } + + // continue loop + element = element.parentNode; + + // stop if we are at the root element + if (element === this.rootElement) { + if (conditionFn(this.rootNode)) { + return this.rootNode; + } else { + return; + } + } + } + } + buildRenderNode(postNode) { + const renderNode = new RenderNode(postNode, this); + postNode.renderNode = renderNode; + return renderNode; + } +} + +const MOBILEDOC_VERSION$4 = MOBILEDOC_VERSION$3; + +var mobiledocRenderers = { + render(post, version) { + switch (version) { + case MOBILEDOC_VERSION: + return MobiledocRenderer_0_2.render(post); + case MOBILEDOC_VERSION$1: + return MobiledocRenderer_0_3.render(post); + case MOBILEDOC_VERSION$2: + return MobiledocRenderer_0_3_1.render(post); + case undefined: + case null: + case MOBILEDOC_VERSION$3: + return MobiledocRenderer_0_3_2.render(post); + default: + assert(`Unknown version of mobiledoc renderer requested: ${version}`, false); + } + } +}; + +function mergeWithOptions(original, updates, options) { + options = options || {}; + for(var prop in updates) { + if (options.hasOwnProperty(prop)) { + original[prop] = options[prop]; + } else if (updates.hasOwnProperty(prop)) { + original[prop] = updates[prop]; + } + } + return original; +} + +const Cursor = class Cursor { + constructor(editor) { + this.editor = editor; + this.renderTree = editor._renderTree; + this.post = editor.post; + } + + clearSelection() { + clearSelection(); + } + + /** + * @return {Boolean} true when there is either a collapsed cursor in the + * editor's element or a selection that is contained in the editor's element + */ + hasCursor() { + return this.editor.hasRendered && + (this._hasCollapsedSelection() || this._hasSelection()); + } + + hasSelection() { + return this.editor.hasRendered && + this._hasSelection(); + } + + /** + * @return {Boolean} Can the cursor be on this element? + */ + isAddressable(element) { + let { renderTree } = this; + let renderNode = renderTree.findRenderNodeFromElement(element); + if (renderNode && renderNode.postNode.isCardSection) { + let renderedElement = renderNode.element; + + // card sections have addressable text nodes containing ‌ + // as their first and last child + if (element !== renderedElement && + element !== renderedElement.firstChild && + element !== renderedElement.lastChild) { + return false; + } + } + + return !!renderNode; + } + + /* + * @return {Range} Cursor#Range object + */ + get offsets() { + if (!this.hasCursor()) { return Range.blankRange(); } + + let { selection, renderTree } = this; + let parentNode = this.editor.element; + selection = constrainSelectionTo(selection, parentNode); + + const { + headNode, headOffset, tailNode, tailOffset, direction + } = comparePosition(selection); + + const headPosition = Position$1.fromNode(renderTree, headNode, headOffset); + const tailPosition = Position$1.fromNode(renderTree, tailNode, tailOffset); + + return new Range(headPosition, tailPosition, direction); + } + + _findNodeForPosition(position) { + let { section } = position; + let node, offset; + if (section.isCardSection) { + offset = 0; + if (position.offset === 0) { + node = section.renderNode.element.firstChild; + } else { + node = section.renderNode.element.lastChild; + } + } else if (section.isBlank) { + node = section.renderNode.cursorElement; + offset = 0; + } else { + let {marker, offsetInMarker} = position; + if (marker.isAtom) { + if (offsetInMarker > 0) { + // FIXME -- if there is a next marker, focus on it? + offset = 0; + node = marker.renderNode.tailTextNode; + } else { + offset = 0; + node = marker.renderNode.headTextNode; + } + } else { + node = marker.renderNode.element; + offset = offsetInMarker; + } + } + + return {node, offset}; + } + + selectRange(range) { + if (range.isBlank) { + this.clearSelection(); + return; + } + + const { head, tail, direction } = range; + const { node:headNode, offset:headOffset } = this._findNodeForPosition(head), + { node:tailNode, offset:tailOffset } = this._findNodeForPosition(tail); + this._moveToNode(headNode, headOffset, tailNode, tailOffset, direction); + + // Firefox sometimes doesn't keep focus in the editor after adding a card + this.editor._ensureFocus(); + } + + get selection() { + return window.getSelection(); + } + + selectedText() { + // FIXME remove this + return this.selection.toString(); + } + + /** + * @param {textNode} node + * @param {integer} offset + * @param {textNode} endNode + * @param {integer} endOffset + * @param {integer} direction forward or backward, default forward + * @private + */ + _moveToNode(node, offset, endNode, endOffset, direction=DIRECTION.FORWARD) { + this.clearSelection(); + + if (direction === DIRECTION.BACKWARD) { + [node, offset, endNode, endOffset] = [ endNode, endOffset, node, offset ]; + } + + const range = document.createRange(); + range.setStart(node, offset); + if (direction === DIRECTION.BACKWARD && !!this.selection.extend) { + this.selection.addRange(range); + this.selection.extend(endNode, endOffset); + } else { + range.setEnd(endNode, endOffset); + this.selection.addRange(range); + } + } + + _hasSelection() { + const element = this.editor.element; + const { _selectionRange } = this; + if (!_selectionRange || _selectionRange.collapsed) { return false; } + + return containsNode(element, this.selection.anchorNode) && + containsNode(element, this.selection.focusNode); + } + + _hasCollapsedSelection() { + const { _selectionRange } = this; + if (!_selectionRange) { return false; } + + const element = this.editor.element; + return containsNode(element, this.selection.anchorNode); + } + + get _selectionRange() { + const { selection } = this; + if (selection.rangeCount === 0) { return null; } + return selection.getRangeAt(0); + } +}; + +var Environment = { + hasDOM() { + return typeof document !== 'undefined'; + } +}; + +const ATOM_LENGTH = 1; + +class Atom extends LinkedItem { + constructor(name, value, payload, markups=[]) { + super(); + this.name = name; + this.value = value; + this.text = ''; // An atom never has text, but it does have a value + assert('Atom must have value', value !== undefined && value !== null); + this.payload = payload; + this.type = ATOM_TYPE; + this.isMarker = false; + this.isAtom = true; + + this.markups = []; + markups.forEach(m => this.addMarkup(m)); + } + + clone() { + let clonedMarkups = this.markups.slice(); + return this.builder.createAtom( + this.name, this.value, this.payload, clonedMarkups + ); + } + + get isBlank() { + return false; + } + + get length() { + return ATOM_LENGTH; + } + + canJoin(/* other */) { + return false; + } + + textUntil(/* offset */) { + return ''; + } + + split(offset=0, endOffset=offset) { + let markers = []; + + if (endOffset === 0) { + markers.push(this.builder.createMarker('', this.markups.slice())); + } + + markers.push(this.clone()); + + if (offset === ATOM_LENGTH) { + markers.push(this.builder.createMarker('', this.markups.slice())); + } + + return markers; + } + + splitAtOffset(offset) { + assert('Cannot split a marker at an offset > its length', + offset <= this.length); + + let { builder } = this; + let clone = this.clone(); + let blankMarker = builder.createMarker(''); + let pre, post; + + if (offset === 0) { + ([pre, post] = [blankMarker, clone]); + } else if (offset === ATOM_LENGTH) { + ([pre, post] = [clone, blankMarker]); + } else { + assert(`Invalid offset given to Atom#splitAtOffset: "${offset}"`, false); + } + + this.markups.forEach(markup => { + pre.addMarkup(markup); + post.addMarkup(markup); + }); + return [pre, post]; + } +} + +mixin(Atom, Markerupable); + +/** + * The Post is an in-memory representation of an editor's document. + * An editor always has a single post. The post is organized into a list of + * sections. Each section may be markerable (contains "markers", aka editable + * text) or non-markerable (e.g., a card). + * When persisting a post, it must first be serialized (loss-lessly) into + * mobiledoc using {@link Editor#serialize}. + */ +class Post { + /** + * @private + */ + constructor() { + this.type = POST_TYPE; + this.sections = new LinkedList({ + adoptItem: s => s.post = s.parent = this, + freeItem: s => s.post = s.parent = null + }); + } + + /** + * @return {Position} The position at the start of the post (will be a {@link BlankPosition} + * if the post is blank) + * @public + */ + headPosition() { + if (this.isBlank) { + return Position$1.blankPosition(); + } else { + return this.sections.head.headPosition(); + } + } + + /** + * @return {Position} The position at the end of the post (will be a {@link BlankPosition} + * if the post is blank) + * @public + */ + tailPosition() { + if (this.isBlank) { + return Position$1.blankPosition(); + } else { + return this.sections.tail.tailPosition(); + } + } + + /** + * @return {Range} A range encompassing the entire post + * @public + */ + toRange() { + return this.headPosition().toRange(this.tailPosition()); + } + + get isBlank() { + return this.sections.isEmpty; + } + + /** + * If the post has no sections, or only has one, blank section, then it does + * not have content and this method returns false. Otherwise it is true. + * @return {Boolean} + * @public + */ + get hasContent() { + if ((this.sections.length > 1) || + (this.sections.length === 1 && !this.sections.head.isBlank)) { + return true; + } else { + return false; + } + } + + /** + * @param {Range} range + * @return {Array} markers that are completely contained by the range + */ + markersContainedByRange(range) { + const markers = []; + + this.walkMarkerableSections(range, section => { + section._markersInRange( + range.trimTo(section), + (m, {isContained}) => { if (isContained) { markers.push(m); } } + ); + }); + + return markers; + } + + markupsInRange(range) { + const markups = new Set(); + + if (range.isCollapsed) { + let pos = range.head; + if (pos.isMarkerable) { + let [back, forward] = [pos.markerIn(-1), pos.markerIn(1)]; + if (back && forward && back === forward) { + back.markups.forEach(m => markups.add(m)); + } else { + (back && back.markups || []).forEach(m => { + if (m.isForwardInclusive()) { + markups.add(m); + } + }); + (forward && forward.markups || []).forEach(m => { + if (m.isBackwardInclusive()) { + markups.add(m); + } + }); + } + } + } else { + this.walkMarkerableSections(range, (section) => { + forEach( + section.markupsInRange(range.trimTo(section)), + m => markups.add(m) + ); + }); + } + + return markups.toArray(); + } + + walkAllLeafSections(callback) { + let range = this.headPosition().toRange(this.tailPosition()); + return this.walkLeafSections(range, callback); + } + + walkLeafSections(range, callback) { + const { head, tail } = range; + + let index = 0; + let nextSection, shouldStop; + let currentSection = head.section; + + while (currentSection) { + nextSection = this._nextLeafSection(currentSection); + shouldStop = currentSection === tail.section; + + callback(currentSection, index); + index++; + + if (shouldStop) { + break; + } else { + currentSection = nextSection; + } + } + } + + walkMarkerableSections(range, callback) { + this.walkLeafSections(range, section => { + if (section.isMarkerable) { + callback(section); + } + }); + } + + // return the next section that has markers after this one, + // possibly skipping non-markerable sections + _nextLeafSection(section) { + if (!section) { return null; } + + const next = section.next; + if (next) { + if (next.isLeafSection) { + return next; + } else if (next.items) { + return next.items.head; + } else { + assert('Cannot determine next section from non-leaf-section', false); + } + } else if (section.isNested) { + // if there is no section after this, but this section is a child + // (e.g. a ListItem inside a ListSection), check for a markerable + // section after its parent + return this._nextLeafSection(section.parent); + } + } + + /** + * @param {Range} range + * @return {Post} A new post, constrained to {range} + */ + trimTo(range) { + const post = this.builder.createPost(); + const { builder } = this; + + let sectionParent = post, + listParent = null; + this.walkLeafSections(range, section => { + let newSection; + if (section.isMarkerable) { + if (section.isListItem) { + if (listParent) { + sectionParent = null; + } else { + listParent = builder.createListSection(section.parent.tagName); + post.sections.append(listParent); + sectionParent = null; + } + newSection = builder.createListItem(); + listParent.items.append(newSection); + } else { + listParent = null; + sectionParent = post; + newSection = builder.createMarkupSection(section.tagName); + } + + let currentRange = range.trimTo(section); + forEach( + section.markersFor(currentRange.headSectionOffset, currentRange.tailSectionOffset), + m => newSection.markers.append(m) + ); + } else { + newSection = section.clone(); + sectionParent = post; + } + if (sectionParent) { + sectionParent.sections.append(newSection); + } + }); + return post; + } +} + +class Image extends Section { + constructor() { + super(IMAGE_SECTION_TYPE); + this.src = null; + } + + canJoin() { + return false; + } + + get isBlank() { + return false; + } + + get length() { + return 1; + } +} + +function shallowCopyObject(object) { + let copy = {}; + Object.keys(object).forEach(key => { + copy[key] = object[key]; + }); + return copy; +} + +const CARD_MODES = { + DISPLAY: 'display', + EDIT: 'edit' +}; + +const CARD_LENGTH = 1; + +const DEFAULT_INITIAL_MODE = CARD_MODES.DISPLAY; + +class Card extends Section { + constructor(name, payload) { + super(CARD_TYPE); + this.name = name; + this.payload = payload; + this.setInitialMode(DEFAULT_INITIAL_MODE); + this.isCardSection = true; + } + + get isBlank() { + return false; + } + + canJoin() { + return false; + } + + get length() { + return CARD_LENGTH; + } + + clone() { + let payload = shallowCopyObject(this.payload); + let card = this.builder.createCardSection(this.name, payload); + // If this card is currently rendered, clone the mode it is + // currently in as the default mode of the new card. + let mode = this._initialMode; + if (this.renderNode && this.renderNode.cardNode) { + mode = this.renderNode.cardNode.mode; + } + card.setInitialMode(mode); + return card; + } + + /** + * set the mode that this will be rendered into initially + * @private + */ + setInitialMode(initialMode) { + // TODO validate initialMode + this._initialMode = initialMode; + } +} + +function cacheKey(tagName, attributes) { + return `${normalizeTagName(tagName)}-${objectToSortedKVArray(attributes).join('-')}`; +} + +function addMarkupToCache(cache, markup) { + cache[cacheKey(markup.tagName, markup.attributes)] = markup; +} + +function findMarkupInCache(cache, tagName, attributes) { + const key = cacheKey(tagName, attributes); + return cache[key]; +} + +/** + * The PostNodeBuilder is used to create new {@link Post} primitives, such + * as a MarkupSection, a CardSection, a Markup, etc. Every instance of an + * {@link Editor} has its own builder instance. The builder can be used + * inside an {@link Editor#run} callback to programmatically create new + * Post primitives to insert into the document. + * A PostNodeBuilder should be read from the Editor, *not* instantiated on its own. + */ +class PostNodeBuilder { + /** + * @private + */ + constructor() { + this.markupCache = {}; + } + + /** + * @return {Post} A new, blank post + */ + createPost(sections=[]) { + const post = new Post(); + post.builder = this; + + sections.forEach(s => post.sections.append(s)); + + return post; + } + + createMarkerableSection(type, tagName, markers=[]) { + switch (type) { + case LIST_ITEM_TYPE: + return this.createListItem(markers); + case MARKUP_SECTION_TYPE: + return this.createMarkupSection(tagName, markers); + default: + assert(`Cannot create markerable section of type ${type}`, false); + } + } + + /** + * @param {tagName} [tagName='P'] + * @param {Marker[]} [markers=[]] + * @return {MarkupSection} + */ + createMarkupSection(tagName=DEFAULT_TAG_NAME, markers=[], isGenerated=false, attributes={}) { + tagName = normalizeTagName(tagName); + const section = new MarkupSection(tagName, markers, attributes); + if (isGenerated) { + section.isGenerated = true; + } + section.builder = this; + return section; + } + + createListSection(tagName=DEFAULT_TAG_NAME$1, items=[], attributes={}) { + tagName = normalizeTagName(tagName); + const section = new ListSection(tagName, items, attributes); + section.builder = this; + return section; + } + + createListItem(markers=[]) { + const tagName = normalizeTagName('li'); + const item = new ListItem(tagName, markers); + item.builder = this; + return item; + } + + createImageSection(url) { + let section = new Image(); + if (url) { + section.src = url; + } + return section; + } + + /** + * @param {String} name + * @param {Object} [payload={}] + * @return {CardSection} + */ + createCardSection(name, payload={}) { + const card = new Card(name, payload); + card.builder = this; + return card; + } + + /** + * @param {String} value + * @param {Markup[]} [markups=[]] + * @return {Marker} + */ + createMarker(value, markups=[]) { + const marker = new Marker(value, markups); + marker.builder = this; + return marker; + } + + /** + * @param {String} name + * @param {String} [value=''] + * @param {Object} [payload={}] + * @param {Markup[]} [markups=[]] + * @return {Atom} + */ + createAtom(name, value='', payload={}, markups=[]) { + const atom = new Atom(name, value, payload, markups); + atom.builder = this; + return atom; + } + + /** + * @param {String} tagName + * @param {Object} attributes Key-value pairs of attributes for the markup + * @return {Markup} + */ + createMarkup(tagName, attributes={}) { + tagName = normalizeTagName(tagName); + + let markup = findMarkupInCache(this.markupCache, tagName, attributes); + if (!markup) { + markup = new Markup(tagName, attributes); + markup.builder = this; + addMarkupToCache(this.markupCache, markup); + } + + return markup; + } +} + +/** + * Convert section at the editor's cursor position into a list. + * Does nothing if the cursor position is not at the start of the section, + * or if the section is already a list item. + * + * @param {Editor} editor + * @param {String} listTagName ("ul" or "ol") + * @public + */ +function replaceWithListSection(editor, listTagName) { + let { range: { head, head: { section } } } = editor; + // Skip if cursor is not at end of section + if (!head.isTail()) { + return; + } + + if (section.isListItem) { + return; + } + + editor.run(postEditor => { + let { builder } = postEditor; + let item = builder.createListItem(); + let listSection = builder.createListSection(listTagName, [item]); + + postEditor.replaceSection(section, listSection); + postEditor.setRange(listSection.headPosition()); + }); +} + +/** + * Convert section at the editor's cursor position into a header section. + * Does nothing if the cursor position is not at the start of the section. + * + * @param {Editor} editor + * @param {String} headingTagName ('h1', 'h2', 'h3', 'h4', 'h5', 'h6') + * @public + */ +function replaceWithHeaderSection(editor, headingTagName) { + let { range: { head, head: { section } } } = editor; + // Skip if cursor is not at end of section + if (!head.isTail()) { + return; + } + + editor.run(postEditor => { + let { builder } = postEditor; + let newSection = builder.createMarkupSection(headingTagName); + postEditor.replaceSection(section, newSection); + postEditor.setRange(newSection.headPosition()); + }); +} + +const DEFAULT_TEXT_INPUT_HANDLERS = [ + { + name: 'ul', + // "* " -> ul + match: /^\* $/, + run(editor) { + replaceWithListSection(editor, 'ul'); + } + }, + { + name: 'ol', + // "1" -> ol, "1." -> ol + match: /^1\.? $/, + run(editor) { + replaceWithListSection(editor, 'ol'); + } + }, + { + name: 'heading', + /* + * "# " -> h1 + * "## " -> h2 + * "### " -> h3 + * "#### " -> h4 + * "##### " -> h5 + * "###### " -> h6 + */ + match: /^(#{1,6}) $/, + run(editor, matches) { + let capture = matches[1]; + let headingTag = 'h' + capture.length; + replaceWithHeaderSection(editor, headingTag); + } + } +]; + +var Browser = { + isMac() { + return (typeof window !== 'undefined') && window.navigator && /Mac/.test(window.navigator.platform); + }, + isWin() { + return (typeof window !== 'undefined') && window.navigator && /Win/.test(window.navigator.platform); + } +}; + +/** + * @module UI + */ + +/** + * @callback promptCallback + * @param {String} url The URL to pass back to the editor for linking + * to the selected text. + */ + +/** + * @callback showPrompt + * @param {String} message The text of the prompt. + * @param {String} defaultValue The initial URL to display in the prompt. + * @param {module:UI~promptCallback} callback Once your handler has accepted a URL, + * it should pass it to `callback` so that the editor may link the + * selected text. + */ + +/** + * Exposes the core behavior for linking and unlinking text, and allows for + * customization of the URL input handler. + * @param {Editor} editor An editor instance to operate on. If a range is selected, + * either prompt for a URL and add a link or un-link the + * currently linked text. + * @param {module:UI~showPrompt} [showPrompt] An optional custom input handler. Defaults + * to using `window.prompt`. + * @example + * let myPrompt = (message, defaultURL, promptCallback) => { + * let url = window.prompt("Overriding the defaults", "http://placekitten.com"); + * promptCallback(url); + * }; + * + * editor.registerKeyCommand({ + * str: "META+K", + * run(editor) { + * toggleLink(editor, myPrompt); + * } + * }); + * @public + */ + +let defaultShowPrompt = (message, defaultValue, callback) => callback(window.prompt(message, defaultValue)); + +function toggleLink(editor, showPrompt=defaultShowPrompt) { + if (editor.range.isCollapsed) { + return; + } + + let selectedText = editor.cursor.selectedText(); + let defaultUrl = ''; + if (selectedText.indexOf('http') !== -1) { defaultUrl = selectedText; } + + let {range} = editor; + let hasLink = editor.detectMarkupInRange(range, 'a'); + + if (hasLink) { + editor.toggleMarkup('a'); + } else { + showPrompt('Enter a URL', defaultUrl, url => { + if (!url) { return; } + + editor.toggleMarkup('a', {href: url}); + }); + } +} + +var ui = { + toggleLink +}; + +function selectAll(editor) { + let { post } = editor; + editor.selectRange(post.toRange()); +} + +function gotoStartOfLine(editor) { + let {range} = editor; + let {tail: {section}} = range; + editor.run(postEditor => { + postEditor.setRange(section.headPosition()); + }); +} + +function gotoEndOfLine(editor) { + let {range} = editor; + let {tail: {section}} = range; + editor.run(postEditor => { + postEditor.setRange(section.tailPosition()); + }); +} + +function deleteToEndOfSection(editor) { + let { range } = editor; + if (range.isCollapsed) { + let { head, head: { section } } = range; + range = head.toRange(section.tailPosition()); + } + editor.run(postEditor => { + let nextPosition = postEditor.deleteRange(range); + postEditor.setRange(nextPosition); + }); +} + +const DEFAULT_KEY_COMMANDS = [{ + str: 'META+B', + run(editor) { + editor.toggleMarkup('strong'); + } +}, { + str: 'CTRL+B', + run(editor) { + editor.toggleMarkup('strong'); + } +}, { + str: 'META+I', + run(editor) { + editor.toggleMarkup('em'); + } +}, { + str: 'CTRL+I', + run(editor) { + editor.toggleMarkup('em'); + } +}, { + str: 'META+U', + run(editor) { + editor.toggleMarkup('u'); + } +}, { + str: 'CTRL+U', + run(editor) { + editor.toggleMarkup('u'); + } +}, { + str: 'CTRL+K', + run(editor) { + if (Browser.isMac()) { + return deleteToEndOfSection(editor); + } else if (Browser.isWin()) { + return toggleLink(editor); + } + } +}, { + str: 'CTRL+A', + run(editor) { + if (Browser.isMac()) { + gotoStartOfLine(editor); + } else { + selectAll(editor); + } + } +}, { + str: 'META+A', + run(editor) { + if (Browser.isMac()) { + selectAll(editor); + } + } +}, { + str: 'CTRL+E', + run(editor) { + if (Browser.isMac()) { + gotoEndOfLine(editor); + } + } +}, { + str: 'META+K', + run(editor) { + return toggleLink(editor); + }, + +}, { + str: 'META+Z', + run(editor) { + editor.run(postEditor => { + postEditor.undoLastChange(); + }); + } +}, { + str: 'META+SHIFT+Z', + run(editor) { + editor.run(postEditor => { + postEditor.redoLastChange(); + }); + } +}, { + str: 'CTRL+Z', + run(editor) { + if (Browser.isMac()) { return false; } + editor.run(postEditor => postEditor.undoLastChange()); + } +}, { + str: 'CTRL+SHIFT+Z', + run(editor) { + if (Browser.isMac()) { return false; } + editor.run(postEditor => postEditor.redoLastChange()); + } +}]; + +function modifierNamesToMask(modiferNames) { + let defaultVal = 0; + return reduce(modiferNames, + (sum, name) => { + let modifier = MODIFIERS[name.toUpperCase()]; + assert(`No modifier named "${name}" found`, !!modifier); + return sum + modifier; + }, + defaultVal); +} + +function characterToCode(character) { + const upperCharacter = character.toUpperCase(); + const special = specialCharacterToCode(upperCharacter); + if (special) { + return special; + } else { + assert(`Only 1 character can be used in a key command str (got "${character}")`, + character.length === 1); + return upperCharacter.charCodeAt(0); + } +} + +function buildKeyCommand(keyCommand) { + let { str } = keyCommand; + + if (!str) { + return keyCommand; + } + assert('[deprecation] Key commands no longer use the `modifier` property', + !keyCommand.modifier); + + let [character, ...modifierNames] = str.split('+').reverse(); + + keyCommand.modifierMask = modifierNamesToMask(modifierNames); + keyCommand.code = characterToCode(character); + + return keyCommand; +} + +function validateKeyCommand(keyCommand) { + return !!keyCommand.code && !!keyCommand.run; +} + +function findKeyCommands(keyCommands, keyEvent) { + const key = Key.fromEvent(keyEvent); + + return filter(keyCommands, ({modifierMask, code}) => { + return key.keyCode === code && key.modifierMask === modifierMask; + }); +} + +const MUTATION = { + NODES_CHANGED: 'childList', + CHARACTER_DATA: 'characterData' +}; + +class MutationHandler { + constructor(editor) { + this.editor = editor; + this.logger = editor.loggerFor('mutation-handler'); + this.renderTree = null; + this._isObserving = false; + + this._observer = new MutationObserver((mutations) => { + this._handleMutations(mutations); + }); + } + + init() { + this.startObserving(); + } + + destroy() { + this.stopObserving(); + this._observer = null; + } + + suspendObservation(callback) { + this.stopObserving(); + callback(); + this.startObserving(); + } + + stopObserving() { + if (this._isObserving) { + this._isObserving = false; + this._observer.disconnect(); + } + } + + startObserving() { + if (!this._isObserving) { + let { editor } = this; + assert('Cannot observe un-rendered editor', editor.hasRendered); + + this._isObserving = true; + this.renderTree = editor._renderTree; + + this._observer.observe(editor.element, { + characterData: true, + childList: true, + subtree: true + }); + } + } + + reparsePost() { + this.editor._reparsePost(); + } + + reparseSections(sections) { + this.editor._reparseSections(sections); + } + + /** + * for each mutation: + * * find the target nodes: + * * if nodes changed, target nodes are: + * * added nodes + * * the target from which removed nodes were removed + * * if character data changed + * * target node is the mutation event's target (text node) + * * filter out nodes that are no longer attached (parentNode is null) + * * for each remaining node: + * * find its section, add to sections-to-reparse + * * if no section, reparse all (and break) + */ + _handleMutations(mutations) { + let reparsePost = false; + let sections = new Set(); + + for (let i = 0; i < mutations.length; i++) { + if (reparsePost) { + break; + } + + let nodes = this._findTargetNodes(mutations[i]); + + for (let j=0; j < nodes.length; j++) { + let node = nodes[j]; + let renderNode = this._findRenderNodeFromNode(node); + if (renderNode) { + if (renderNode.reparsesMutationOfChildNode(node)) { + let section = this._findSectionFromRenderNode(renderNode); + if (section) { + sections.add(section); + } else { + reparsePost = true; + } + } + } else { + reparsePost = true; + break; + } + } + } + + if (reparsePost) { + this.logger.log(`reparsePost (${mutations.length} mutations)`); + this.reparsePost(); + } else if (sections.length) { + this.logger.log(`reparse ${sections.length} sections (${mutations.length} mutations)`); + this.reparseSections(sections.toArray()); + } + } + + _findTargetNodes(mutation) { + let nodes = []; + + switch (mutation.type) { + case MUTATION.CHARACTER_DATA: + nodes.push(mutation.target); + break; + case MUTATION.NODES_CHANGED: + forEach(mutation.addedNodes, n => nodes.push(n)); + if (mutation.removedNodes.length) { + nodes.push(mutation.target); + } + break; + } + + let element = this.editor.element; + let attachedNodes = filter(nodes, node => containsNode(element, node)); + return attachedNodes; + } + + _findSectionRenderNodeFromNode(node) { + return this.renderTree.findRenderNodeFromElement(node, (rn) => { + return rn.postNode.isSection; + }); + } + + _findRenderNodeFromNode(node) { + return this.renderTree.findRenderNodeFromElement(node); + } + + _findSectionFromRenderNode(renderNode) { + let sectionRenderNode = this._findSectionRenderNodeFromNode(renderNode.element); + return sectionRenderNode && sectionRenderNode.postNode; + } + +} + +class FixedQueue { + constructor(length=0) { + this._maxLength = length; + this._items = []; + } + + get length() { + return this._items.length; + } + + pop() { + return this._items.pop(); + } + + push(item) { + this._items.push(item); + if (this.length > this._maxLength) { + this._items.shift(); + } + } + + clear() { + this._items = []; + } + + toArray() { + return this._items; + } +} + +function findLeafSectionAtIndex(post, index) { + let section; + post.walkAllLeafSections((_section, _index) => { + if (index === _index) { + section = _section; + } + }); + return section; +} + +class Snapshot { + constructor(takenAt, editor, editAction=null) { + this.mobiledoc = editor.serialize(); + this.editor = editor; + this.editAction = editAction; + this.takenAt = takenAt; + + this.snapshotRange(); + } + + snapshotRange() { + let { range, cursor } = this.editor; + if (cursor.hasCursor() && !range.isBlank) { + let { head, tail } = range; + this.range = { + head: [head.leafSectionIndex, head.offset], + tail: [tail.leafSectionIndex, tail.offset] + }; + } + } + + getRange(post) { + if (this.range) { + let { head, tail } = this.range; + let [headLeafSectionIndex, headOffset] = head; + let [tailLeafSectionIndex, tailOffset] = tail; + let headSection = findLeafSectionAtIndex(post, headLeafSectionIndex); + let tailSection = findLeafSectionAtIndex(post, tailLeafSectionIndex); + + head = headSection.toPosition(headOffset); + tail = tailSection.toPosition(tailOffset); + + return head.toRange(tail); + } + } + + groupsWith(groupingTimeout, editAction, takenAt) { + return ( + editAction !== null && + this.editAction === editAction && + this.takenAt + groupingTimeout > takenAt + ); + } +} + +class EditHistory { + constructor(editor, queueLength, groupingTimeout) { + this.editor = editor; + this._undoStack = new FixedQueue(queueLength); + this._redoStack = new FixedQueue(queueLength); + + this._pendingSnapshot = null; + this._groupingTimeout = groupingTimeout; + } + + snapshot() { + // update the current snapshot with the range read from DOM + if (this._pendingSnapshot) { + this._pendingSnapshot.snapshotRange(); + } + } + + storeSnapshot(editAction=null) { + let now = Date.now(); + // store pending snapshot + let pendingSnapshot = this._pendingSnapshot; + if (pendingSnapshot) { + if (!pendingSnapshot.groupsWith(this._groupingTimeout, editAction, now)) { + this._undoStack.push(pendingSnapshot); + } + this._redoStack.clear(); + } + + // take new pending snapshot to store next time `storeSnapshot` is called + this._pendingSnapshot = new Snapshot(now, this.editor, editAction); + } + + stepBackward(postEditor) { + // Throw away the pending snapshot + this._pendingSnapshot = null; + + let snapshot = this._undoStack.pop(); + if (snapshot) { + this._redoStack.push(new Snapshot(Date.now(), this.editor)); + this._restoreFromSnapshot(snapshot, postEditor); + } + } + + stepForward(postEditor) { + let snapshot = this._redoStack.pop(); + if (snapshot) { + this._undoStack.push(new Snapshot(Date.now(), this.editor)); + this._restoreFromSnapshot(snapshot, postEditor); + } + postEditor.cancelSnapshot(); + } + + _restoreFromSnapshot(snapshot, postEditor) { + let { mobiledoc } = snapshot; + let { editor } = this; + let { builder, post } = editor; + let restoredPost = mobiledocParsers.parse(builder, mobiledoc); + + postEditor.removeAllSections(); + postEditor.migrateSectionsFromPost(restoredPost); + + // resurrect snapshotted range if it exists + let newRange = snapshot.getRange(post); + if (newRange) { + postEditor.setRange(newRange); + } + } +} + +const UL_LI_REGEX = /^\* (.*)$/; +const OL_LI_REGEX = /^\d\.? (.*)$/; +const CR = '\r'; +const LF = '\n'; +const CR_REGEX = new RegExp(CR, 'g'); +const CR_LF_REGEX = new RegExp(CR+LF, 'g'); + +const SECTION_BREAK = LF; + +function normalizeLineEndings(text) { + return text.replace(CR_LF_REGEX, LF) + .replace(CR_REGEX, LF); +} + +class TextParser { + constructor(builder, options) { + this.builder = builder; + this.options = options; + + this.post = this.builder.createPost(); + this.prevSection = null; + } + + /** + * @param {String} text to parse + * @return {Post} a post abstract + */ + parse(text) { + text = normalizeLineEndings(text); + text.split(SECTION_BREAK).forEach(text => { + let section = this._parseSection(text); + this._appendSection(section); + }); + + return this.post; + } + + _parseSection(text) { + let tagName = DEFAULT_TAG_NAME, + type = MARKUP_SECTION_TYPE, + section; + + if (UL_LI_REGEX.test(text)) { + tagName = 'ul'; + type = LIST_SECTION_TYPE; + text = text.match(UL_LI_REGEX)[1]; + } else if (OL_LI_REGEX.test(text)) { + tagName = 'ol'; + type = LIST_SECTION_TYPE; + text = text.match(OL_LI_REGEX)[1]; + } + + let markers = [this.builder.createMarker(text)]; + + switch (type) { + case LIST_SECTION_TYPE: { + let item = this.builder.createListItem(markers); + let list = this.builder.createListSection(tagName, [item]); + section = list; + break; + } + case MARKUP_SECTION_TYPE: + section = this.builder.createMarkupSection(tagName, markers); + break; + default: + assert(`Unknown type encountered ${type}`, false); + } + + return section; + } + + _appendSection(section) { + let isSameListSection = + section.isListSection && + this.prevSection && this.prevSection.isListSection && + this.prevSection.tagName === section.tagName; + + if (isSameListSection) { + section.items.forEach(item => { + this.prevSection.items.append(item.clone()); + }); + } else { + this.post.sections.insertAfter(section, this.prevSection); + this.prevSection = section; + } + } +} + +/* global JSON */ + +const MIME_TEXT_PLAIN = 'text/plain'; +const MIME_TEXT_HTML = 'text/html'; +const NONSTANDARD_IE_TEXT_TYPE = 'Text'; + +const MOBILEDOC_REGEX = new RegExp(/data\-mobiledoc='(.*?)'>/); + +/** + * @return {Post} + * @private + */ +function parsePostFromHTML(html, builder, plugins) { + let post; + + if (MOBILEDOC_REGEX.test(html)) { + let mobiledocString = html.match(MOBILEDOC_REGEX)[1]; + let mobiledoc = JSON.parse(mobiledocString); + post = mobiledocParsers.parse(builder, mobiledoc); + } else { + post = new HTMLParser(builder, {plugins}).parse(html); + } + + return post; +} + +/** + * @return {Post} + * @private + */ +function parsePostFromText(text, builder, plugins) { + let parser = new TextParser(builder, {plugins}); + let post = parser.parse(text); + return post; +} + +/** + * @return {{html: String, text: String}} + * @private + */ +function getContentFromPasteEvent(event, window) { + let html = '', text = ''; + + let { clipboardData } = event; + + if (clipboardData && clipboardData.getData) { + html = clipboardData.getData(MIME_TEXT_HTML); + text = clipboardData.getData(MIME_TEXT_PLAIN); + } else if (window.clipboardData && window.clipboardData.getData) { // IE + // The Internet Explorers (including Edge) have a non-standard way of interacting with the + // Clipboard API (see http://caniuse.com/#feat=clipboard). In short, they expose a global window.clipboardData + // object instead of the per-event event.clipboardData object on the other browsers. + html = window.clipboardData.getData(NONSTANDARD_IE_TEXT_TYPE); + } + + return { html, text }; +} + +/** + * @return {{html: String, text: String}} + * @private + */ +function getContentFromDropEvent(event, logger) { + let html = '', text = ''; + + try { + html = event.dataTransfer.getData(MIME_TEXT_HTML); + text = event.dataTransfer.getData(MIME_TEXT_PLAIN); + } catch (e) { + // FIXME IE11 does not include any data in the 'text/html' or 'text/plain' + // mimetypes. It throws an error 'Invalid argument' when attempting to read + // these properties. + if (logger) { + logger.log('Error getting drop data: ', e); + } + } + + return { html, text }; +} + +/** + * @param {CopyEvent|CutEvent} + * @param {Editor} + * @param {Window} + * @private + */ +function setClipboardData(event, {mobiledoc, html, text}, window) { + if (mobiledoc && html) { + html = `
    ${html}
    `; + } + + let { clipboardData } = event; + let { clipboardData: nonstandardClipboardData } = window; + + if (clipboardData && clipboardData.setData) { + clipboardData.setData(MIME_TEXT_HTML, html); + clipboardData.setData(MIME_TEXT_PLAIN, text); + } else if (nonstandardClipboardData && nonstandardClipboardData.setData) { + // The Internet Explorers (including Edge) have a non-standard way of interacting with the + // Clipboard API (see http://caniuse.com/#feat=clipboard). In short, they expose a global window.clipboardData + // object instead of the per-event event.clipboardData object on the other browsers. + nonstandardClipboardData.setData(NONSTANDARD_IE_TEXT_TYPE, html); + } +} + +/** + * @param {PasteEvent} + * @param {{builder: Builder, _parserPlugins: Array}} options + * @return {Post} + * @private + */ +function parsePostFromPaste(pasteEvent, {builder, _parserPlugins: plugins}, {targetFormat}={targetFormat:'html'}) { + let { html, text } = getContentFromPasteEvent(pasteEvent, window); + + if (targetFormat === 'html' && html && html.length) { + return parsePostFromHTML(html, builder, plugins); + } else if (text && text.length) { + return parsePostFromText(text, builder, plugins); + } +} + +/** + * @param {DropEvent} + * @param {Editor} editor + * @param {Object} [options={}] Can pass a logger + * @return {Post} + * @private + */ +function parsePostFromDrop(dropEvent, editor, {logger}={}) { + let { builder, _parserPlugins: plugins } = editor; + let { html, text } = getContentFromDropEvent(dropEvent, logger); + + if (html && html.length) { + return parsePostFromHTML(html, builder, plugins); + } else if (text && text.length) { + return parsePostFromText(text, builder, plugins); + } +} + +class TextInputHandler { + constructor(editor) { + this.editor = editor; + this._handlers = []; + } + + register(handler) { + assert(`Input Handler is not valid`, this._validateHandler(handler)); + this._handlers.push(handler); + } + + unregister(name) { + let handlers = this._handlers; + for (let i=0; i { + listener.selectionDidChange(...arguments); + }); + } + + destroy() { + this.stop(); + this.listeners = []; + } + + getSelection() { + let selection = window.getSelection(); + let { anchorNode, focusNode, anchorOffset, focusOffset } = selection; + return { anchorNode, focusNode, anchorOffset, focusOffset }; + } + + poll() { + if (this.started) { + this.update(); + this.runNext(() => this.poll()); + } + } + + runNext(fn) { + window.requestAnimationFrame(fn); + } + + update() { + let prevSelection = this.selection; + let curSelection = this.getSelection(); + if (!this.selectionIsEqual(prevSelection, curSelection)) { + this.selection = curSelection; + this.notifyListeners(curSelection, prevSelection); + } + } + + selectionIsEqual(s1, s2) { + return s1.anchorNode === s2.anchorNode && + s1.anchorOffset === s2.anchorOffset && + s1.focusNode === s2.focusNode && + s1.focusOffset === s2.focusOffset; + } +} + +class SelectionManager { + constructor(editor, callback) { + this.editor = editor; + this.callback = callback; + this.started = false; + } + + start() { + if (this.started) { return; } + + SelectionChangeObserver.addListener(this); + this.started = true; + } + + stop() { + this.started = false; + SelectionChangeObserver.removeListener(this); + } + + destroy() { + this.stop(); + } + + selectionDidChange() { + if (this.started) { + this.callback(...arguments); + } + } +} + +const ELEMENT_EVENT_TYPES = [ + 'keydown', 'keyup', 'cut', 'copy', 'paste', 'keypress', 'drop' +]; + +class EventManager { + constructor(editor) { + this.editor = editor; + this.logger = editor.loggerFor('event-manager'); + this._textInputHandler = new TextInputHandler(editor); + this._listeners = []; + this.modifierKeys = { + shift: false + }; + + this._selectionManager = new SelectionManager( + this.editor, this.selectionDidChange.bind(this)); + this.started = true; + } + + init() { + let { editor: { element } } = this; + assert(`Cannot init EventManager without element`, !!element); + + ELEMENT_EVENT_TYPES.forEach(type => { + this._addListener(element, type); + }); + + this._selectionManager.start(); + } + + start() { + this.started = true; + } + + stop() { + this.started = false; + } + + registerInputHandler(inputHandler) { + this._textInputHandler.register(inputHandler); + } + + unregisterInputHandler(name) { + this._textInputHandler.unregister(name); + } + + unregisterAllTextInputHandlers() { + this._textInputHandler.destroy(); + this._textInputHandler = new TextInputHandler(this.editor); + } + + _addListener(context, type) { + assert(`Missing listener for ${type}`, !!this[type]); + + let listener = (event) => this._handleEvent(type, event); + context.addEventListener(type, listener); + this._listeners.push([context, type, listener]); + } + + _removeListeners() { + this._listeners.forEach(([context, type, listener]) => { + context.removeEventListener(type, listener); + }); + this._listeners = []; + } + + // This is primarily useful for programmatically simulating events on the + // editor from the tests. + _trigger(context, type, event) { + forEach( + filter(this._listeners, ([_context, _type]) => { + return _context === context && _type === type; + }), + ([context,, listener]) => { + listener.call(context, event); + } + ); + } + + destroy() { + this._textInputHandler.destroy(); + this._selectionManager.destroy(); + this._removeListeners(); + } + + _handleEvent(type, event) { + let {target: element} = event; + if (!this.started) { + // abort handling this event + return true; + } + + if (!this.isElementAddressable(element)) { + // abort handling this event + return true; + } + + this[type](event); + } + + isElementAddressable(element) { + return this.editor.cursor.isAddressable(element); + } + + selectionDidChange(selection /*, prevSelection */) { + let shouldNotify = true; + let { anchorNode } = selection; + if (!this.isElementAddressable(anchorNode)) { + if (!this.editor.range.isBlank) { + // Selection changed from something addressable to something + // not-addressable -- e.g., blur event, user clicked outside editor, + // etc + shouldNotify = true; + } else { + // selection changes wholly outside the editor should not trigger + // change notifications + shouldNotify = false; + } + } + + if (shouldNotify) { + this.editor._readRangeFromDOM(); + } + } + + keypress(event) { + let { editor, _textInputHandler } = this; + if (!editor.hasCursor()) { return; } + + let key = Key.fromEvent(event); + if (!key.isPrintable()) { + return; + } else { + event.preventDefault(); + } + + _textInputHandler.handle(key.toString()); + } + + keydown(event) { + let { editor } = this; + if (!editor.hasCursor()) { return; } + if (!editor.isEditable) { return; } + + let key = Key.fromEvent(event); + this._updateModifiersFromKey(key, {isDown:true}); + + if (editor.handleKeyCommand(event)) { return; } + + if (editor.post.isBlank) { + editor._insertEmptyMarkupSectionAtCursor(); + } + + let range = editor.range; + + switch(true) { + // FIXME This should be restricted to only card/atom boundaries + case key.isHorizontalArrowWithoutModifiersOtherThanShift(): { + let newRange; + if (key.isShift()) { + newRange = range.extend(key.direction * 1); + } else { + newRange = range.move(key.direction); + } + + editor.selectRange(newRange); + event.preventDefault(); + break; + } + case key.isDelete(): { + let { direction } = key; + let unit = 'char'; + if (key.altKey && Browser.isMac()) { + unit = 'word'; + } else if (key.ctrlKey && !Browser.isMac()) { + unit = 'word'; + } + editor.performDelete({direction, unit}); + event.preventDefault(); + break; + } + case key.isEnter(): + this._textInputHandler.handleNewLine(); + editor.handleNewline(event); + break; + case key.isTab(): + // Handle tab here because it does not fire a `keypress` event + event.preventDefault(); + this._textInputHandler.handle(key.toString()); + break; + } + } + + keyup(event) { + let { editor } = this; + if (!editor.hasCursor()) { return; } + let key = Key.fromEvent(event); + this._updateModifiersFromKey(key, {isDown:false}); + } + + cut(event) { + event.preventDefault(); + + this.copy(event); + this.editor.performDelete(); + } + + copy(event) { + event.preventDefault(); + + let { editor, editor: { range, post } } = this; + post = post.trimTo(range); + + let data = { + html: editor.serializePost(post, 'html'), + text: editor.serializePost(post, 'text'), + mobiledoc: editor.serializePost(post, 'mobiledoc') + }; + + setClipboardData(event, data, window); + } + + paste(event) { + event.preventDefault(); + + let { editor } = this; + let range = editor.range; + + if (!range.isCollapsed) { + editor.performDelete(); + } + + if (editor.post.isBlank) { + editor._insertEmptyMarkupSectionAtCursor(); + } + + let position = editor.range.head; + let targetFormat = this.modifierKeys.shift ? 'text' : 'html'; + let pastedPost = parsePostFromPaste(event, editor, {targetFormat}); + + editor.run(postEditor => { + let nextPosition = postEditor.insertPost(position, pastedPost); + postEditor.setRange(nextPosition); + }); + } + + drop(event) { + event.preventDefault(); + + let { clientX: x, clientY: y } = event; + let { editor } = this; + + let position = editor.positionAtPoint(x, y); + if (!position) { + this.logger.log('Could not find drop position'); + return; + } + + let post = parsePostFromDrop(event, editor, {logger: this.logger}); + if (!post) { + this.logger.log('Could not determine post from drop event'); + return; + } + + editor.run(postEditor => { + let nextPosition = postEditor.insertPost(position, post); + postEditor.setRange(nextPosition); + }); + } + + _updateModifiersFromKey(key, {isDown}) { + if (key.isShiftKey()) { + this.modifierKeys.shift = isDown; + } + } + +} + +/** + * Used by {@link Editor} to manage its current state (cursor, active markups + * and active sections). + * @private + */ +class EditState { + constructor(editor) { + this.editor = editor; + + let defaultState = { + range: Range.blankRange(), + activeMarkups: [], + activeSections: [], + activeSectionTagNames: [], + activeSectionAttributes: {} + }; + + this.prevState = this.state = defaultState; + } + + updateRange(newRange) { + this.prevState = this.state; + this.state = this._readState(newRange); + } + + destroy() { + this.editor = null; + this.prevState = this.state = null; + } + + /** + * @return {Boolean} + */ + rangeDidChange() { + let { state: { range } , prevState: {range: prevRange} } = this; + + return !prevRange.isEqual(range); + } + + /** + * @return {Boolean} Whether the input mode (active markups or active section tag names) + * has changed. + */ + inputModeDidChange() { + let { state, prevState } = this; + return (!isArrayEqual(state.activeMarkups, prevState.activeMarkups) || + !isArrayEqual(state.activeSectionTagNames, prevState.activeSectionTagNames) || + !isArrayEqual(objectToSortedKVArray(state.activeSectionAttributes), objectToSortedKVArray(prevState.activeSectionAttributes))); + } + + /** + * @return {Range} + */ + get range() { + return this.state.range; + } + + /** + * @return {Section[]} + */ + get activeSections() { + return this.state.activeSections; + } + + + /** + * @return {Object} + */ + get activeSectionAttributes() { + return this.state.activeSectionAttributes; + } + + /** + * @return {Markup[]} + */ + get activeMarkups() { + return this.state.activeMarkups; + } + + /** + * Update the editor's markup state. This is used when, e.g., + * a user types meta+B when the editor has a cursor but no selected text; + * in this case the editor needs to track that it has an active "b" markup + * and apply it to the next text the user types. + */ + toggleMarkupState(markup) { + if (contains(this.activeMarkups, markup)) { + this._removeActiveMarkup(markup); + } else { + this._addActiveMarkup(markup); + } + } + + _readState(range) { + let state = { + range, + activeMarkups: this._readActiveMarkups(range), + activeSections: this._readActiveSections(range) + }; + // Section objects are 'live', so to check that they changed, we + // need to map their tagNames now (and compare to mapped tagNames later). + // In addition, to catch changes from ul -> ol, we keep track of the + // un-nested tag names (otherwise we'd only see li -> li change) + state.activeSectionTagNames = state.activeSections.map(s => { + return s.isNested ? s.parent.tagName : s.tagName; + }); + state.activeSectionAttributes = this._readSectionAttributes(state.activeSections); + return state; + } + + _readActiveSections(range) { + let { head, tail } = range; + let { editor: { post } } = this; + if (range.isBlank) { + return []; + } else { + return post.sections.readRange(head.section, tail.section); + } + } + + _readActiveMarkups(range) { + let { editor: { post } } = this; + return post.markupsInRange(range); + } + + _readSectionAttributes(sections) { + return sections.reduce((sectionAttributes, s) => { + let attributes = s.isNested ? s.parent.attributes : s.attributes; + Object.keys(attributes || {}).forEach(attrName => { + let camelizedAttrName = attrName.replace(/^data-md-/, ''); + let attrValue = attributes[attrName]; + sectionAttributes[camelizedAttrName] = sectionAttributes[camelizedAttrName] || []; + if (!contains(sectionAttributes[camelizedAttrName], attrValue)) { + sectionAttributes[camelizedAttrName].push(attrValue); + } + }); + return sectionAttributes; + }, {}); + } + + _removeActiveMarkup(markup) { + let index = this.state.activeMarkups.indexOf(markup); + this.state.activeMarkups.splice(index, 1); + } + + _addActiveMarkup(markup) { + this.state.activeMarkups.push(markup); + } +} + +function addHTMLSpaces(text) { + let nbsp = '\u00A0'; + return text.replace(/ /g, ' ' + nbsp); +} + +function createTextNode(dom, text) { + return dom.createTextNode(addHTMLSpaces(text)); +} + +function normalizeTagName$1(tagName) { + return tagName.toLowerCase(); +} + +var RENDER_TYPE = 'dom'; + +var ImageCard$1 = { + name: 'image', + type: RENDER_TYPE, + render({payload, env: {dom}}) { + let img = dom.createElement('img'); + img.src = payload.src; + return img; + } +}; + +const MARKUP_SECTION_TYPE$1 = 1; +const IMAGE_SECTION_TYPE$1 = 2; +const LIST_SECTION_TYPE$1 = 3; +const CARD_SECTION_TYPE = 10; + +const MARKUP_SECTION_TAG_NAMES = [ + 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'pull-quote', 'aside' +].map(normalizeTagName$1); + +const MARKUP_SECTION_ELEMENT_NAMES$1 = [ + 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'aside' +].map(normalizeTagName$1); + +const LIST_SECTION_TAG_NAMES = [ + 'ul', 'ol' +].map(normalizeTagName$1); + +const MARKUP_TYPES = [ + 'b', 'i', 'strong', 'em', 'a', 'u', 'sub', 'sup', 's', 'code' +].map(normalizeTagName$1); + +function contains$1(array, item) { + return array.indexOf(item) !== -1; +} + +function isValidSectionTagName(tagName, sectionType) { + tagName = normalizeTagName$1(tagName); + + switch (sectionType) { + case MARKUP_SECTION_TYPE$1: + return contains$1(MARKUP_SECTION_TAG_NAMES, tagName); + case LIST_SECTION_TYPE$1: + return contains$1(LIST_SECTION_TAG_NAMES, tagName); + default: + throw new Error(`Cannot validate tagName for unknown section type "${sectionType}"`); + } +} + +function isMarkupSectionElementName(tagName) { + tagName = normalizeTagName$1(tagName); + return contains$1(MARKUP_SECTION_ELEMENT_NAMES$1, tagName); +} + +function isValidMarkerType(type) { + type = normalizeTagName$1(type); + return contains$1(MARKUP_TYPES, type); +} + +function includes(array, detectValue) { + for (let i=0;i < array.length;i++) { + let value = array[i]; + if (value === detectValue) { + return true; + } + } + return false; +} + +const PROTOCOL_REGEXP = /^([a-z0-9.+-]+:)/i; + +const badProtocols = [ + 'javascript:', // jshint ignore:line + 'vbscript:' // jshint ignore:line +]; + +function getProtocol(url) { + let matches = url && url.match(PROTOCOL_REGEXP); + let protocol = (matches && matches[0]) || ':'; + return protocol; +} + +function sanitizeHref(url) { + let protocol = getProtocol(url).toLowerCase(); + if (includes(badProtocols, protocol)) { + return `unsafe:${url}`; + } + return url; +} + +/** + * @param attributes array + * @return obj with normalized attribute names (lowercased) + */ +function reduceAttributes(attributes) { + let obj = {}; + for (let i = 0; i < attributes.length; i += 2) { + let key = attributes[i]; + let val = attributes[i+1]; + obj[key.toLowerCase()] = val; + } + return obj; +} + +const VALID_ATTRIBUTES$2 = [ + 'data-md-text-align' +]; + +function _isValidAttribute(attr) { + return VALID_ATTRIBUTES$2.indexOf(attr) !== -1; +} + +function handleMarkupSectionAttribute(element, attributeKey, attributeValue) { + if (!_isValidAttribute(attributeKey)) { + throw new Error(`Cannot use attribute: ${attributeKey}`); + } + + element.setAttribute(attributeKey, attributeValue); +} + +function defaultSectionElementRenderer(tagName, dom, attrsObj = {}) { + let element; + if (isMarkupSectionElementName(tagName)) { + element = dom.createElement(tagName); + + Object.keys(attrsObj).forEach(k => { + handleMarkupSectionAttribute(element, k, attrsObj[k]); + }); + } else { + element = dom.createElement('div'); + element.setAttribute('class', tagName); + } + + return element; +} + +function sanitizeAttribute(tagName, attrName, attrValue) { + if (tagName === 'a' && attrName === 'href') { + return sanitizeHref(attrValue); + } else { + return attrValue; + } +} + +function defaultMarkupElementRenderer(tagName, dom, attrsObj) { + let element = dom.createElement(tagName); + Object.keys(attrsObj).forEach(attrName => { + let attrValue = attrsObj[attrName]; + attrValue = sanitizeAttribute(tagName, attrName, attrValue); + element.setAttribute(attrName, attrValue); + }); + return element; +} + +const MOBILEDOC_VERSION$5 = '0.2.0'; + +const IMAGE_SECTION_TAG_NAME = 'img'; + +function validateVersion(version) { + if (version !== MOBILEDOC_VERSION$5) { + throw new Error(`Unexpected Mobiledoc version "${version}"`); + } +} + +class Renderer$1 { + constructor(mobiledoc, options) { + let { + cards, + cardOptions, + unknownCardHandler, + markupElementRenderer, + sectionElementRenderer, + dom + } = options; + let { + version, + sections: sectionData + } = mobiledoc; + validateVersion(version); + + const [markerTypes, sections] = sectionData; + + this.dom = dom; + this.root = dom.createDocumentFragment(); + this.markerTypes = markerTypes; + this.sections = sections; + this.cards = cards; + this.cardOptions = cardOptions; + this.unknownCardHandler = unknownCardHandler || this._defaultUnknownCardHandler; + + this.sectionElementRenderer = { + '__default__': defaultSectionElementRenderer + }; + Object.keys(sectionElementRenderer).forEach(key => { + this.sectionElementRenderer[key.toLowerCase()] = sectionElementRenderer[key]; + }); + + this.markupElementRenderer = { + '__default__': defaultMarkupElementRenderer + }; + Object.keys(markupElementRenderer).forEach(key => { + this.markupElementRenderer[key.toLowerCase()] = markupElementRenderer[key]; + }); + + this._renderCallbacks = []; + this._teardownCallbacks = []; + this._renderedChildNodes = []; + } + + get _defaultUnknownCardHandler() { + return ({env: {name}}) => { + throw new Error(`Card "${name}" not found but no unknownCardHandler was registered`); + }; + } + + render() { + this.sections.forEach(section => { + let rendered = this.renderSection(section); + if (rendered) { + this.root.appendChild(rendered); + } + }); + for (let i = 0; i < this._renderCallbacks.length; i++) { + this._renderCallbacks[i](); + } + // maintain a reference to child nodes so they can be cleaned up later by teardown + this._renderedChildNodes = []; + let node = this.root.firstChild; + while (node) { + this._renderedChildNodes.push(node); + node = node.nextSibling; + } + return { result: this.root, teardown: () => this.teardown() }; + } + + teardown() { + for (let i=0; i < this._teardownCallbacks.length; i++) { + this._teardownCallbacks[i](); + } + for (let i=0; i < this._renderedChildNodes.length; i++) { + let node = this._renderedChildNodes[i]; + if (node.parentNode) { + node.parentNode.removeChild(node); + } + } + } + + renderSection(section) { + const [type] = section; + switch (type) { + case MARKUP_SECTION_TYPE$1: + return this.renderMarkupSection(section); + case IMAGE_SECTION_TYPE$1: + return this.renderImageSection(section); + case LIST_SECTION_TYPE$1: + return this.renderListSection(section); + case CARD_SECTION_TYPE: + return this.renderCardSection(section); + default: + throw new Error(`Cannot render mobiledoc section of type "${type}"`); + } + } + + renderMarkersOnElement(element, markers) { + let elements = [element]; + let currentElement = element; + + let pushElement = (openedElement) => { + currentElement.appendChild(openedElement); + elements.push(openedElement); + currentElement = openedElement; + }; + + for (let i=0, l=markers.length; i { + element.appendChild(this.renderListItem(li)); + }); + return element; + } + + renderImageSection([type, src]) { + let element = this.dom.createElement(IMAGE_SECTION_TAG_NAME); + element.src = src; + return element; + } + + findCard(name) { + for (let i=0; i < this.cards.length; i++) { + if (this.cards[i].name === name) { + return this.cards[i]; + } + } + if (name === ImageCard$1.name) { + return ImageCard$1; + } + return this._createUnknownCard(name); + } + + _createUnknownCard(name) { + return { + name, + type: RENDER_TYPE, + render: this.unknownCardHandler + }; + } + + _createCardArgument(card, payload={}) { + let env = { + name: card.name, + isInEditor: false, + dom: this.dom, + didRender: (callback) => this._registerRenderCallback(callback), + onTeardown: (callback) => this._registerTeardownCallback(callback) + }; + + let options = this.cardOptions; + + return { env, options, payload }; + } + + _registerRenderCallback(callback) { + this._renderCallbacks.push(callback); + } + + _registerTeardownCallback(callback) { + this._teardownCallbacks.push(callback); + } + + renderCardSection([type, name, payload]) { + let card = this.findCard(name); + + let cardArg = this._createCardArgument(card, payload); + let rendered = card.render(cardArg); + + this._validateCardRender(rendered, card.name); + + return rendered; + } + + _validateCardRender(rendered, cardName) { + if (!rendered) { + return; + } + + if (typeof rendered !== 'object') { + throw new Error(`Card "${cardName}" must render ${RENDER_TYPE}, but result was "${rendered}"`); + } + } + + renderMarkupSection([type, tagName, markers]) { + tagName = tagName.toLowerCase(); + if (!isValidSectionTagName(tagName, MARKUP_SECTION_TYPE$1)) { + return; + } + + let renderer = this.sectionElementRendererFor(tagName); + let element = renderer(tagName, this.dom); + + this.renderMarkersOnElement(element, markers); + return element; + } + + sectionElementRendererFor(tagName) { + return this.sectionElementRenderer[tagName] || + this.sectionElementRenderer.__default__; + } +} + +const MARKUP_MARKER_TYPE = 0; +const ATOM_MARKER_TYPE = 1; + +const MOBILEDOC_VERSION_0_3_0 = '0.3.0'; +const MOBILEDOC_VERSION_0_3_1 = '0.3.1'; +const MOBILEDOC_VERSION_0_3_2 = '0.3.2'; + +const IMAGE_SECTION_TAG_NAME$1 = 'img'; + +function validateVersion$1(version) { + switch (version) { + case MOBILEDOC_VERSION_0_3_0: + case MOBILEDOC_VERSION_0_3_1: + case MOBILEDOC_VERSION_0_3_2: + return; + default: + throw new Error(`Unexpected Mobiledoc version "${version}"`); + } +} + +class Renderer$2 { + constructor(mobiledoc, state) { + + let { + cards, + cardOptions, + atoms, + unknownCardHandler, + unknownAtomHandler, + markupElementRenderer, + sectionElementRenderer, + dom + } = state; + let { + version, + sections, + atoms: atomTypes, + cards: cardTypes, + markups: markerTypes + } = mobiledoc; + validateVersion$1(version); + + this.dom = dom; + this.root = this.dom.createDocumentFragment(); + this.sections = sections; + this.atomTypes = atomTypes; + this.cardTypes = cardTypes; + this.markerTypes = markerTypes; + this.cards = cards; + this.atoms = atoms; + this.cardOptions = cardOptions; + this.unknownCardHandler = unknownCardHandler || this._defaultUnknownCardHandler; + this.unknownAtomHandler = unknownAtomHandler || this._defaultUnknownAtomHandler; + + this.sectionElementRenderer = { + '__default__': defaultSectionElementRenderer + }; + Object.keys(sectionElementRenderer).forEach(key => { + this.sectionElementRenderer[key.toLowerCase()] = sectionElementRenderer[key]; + }); + + this.markupElementRenderer = { + '__default__': defaultMarkupElementRenderer + }; + Object.keys(markupElementRenderer).forEach(key => { + this.markupElementRenderer[key.toLowerCase()] = markupElementRenderer[key]; + }); + + this._renderCallbacks = []; + this._teardownCallbacks = []; + } + + get _defaultUnknownCardHandler() { + return ({env: {name}}) => { + throw new Error(`Card "${name}" not found but no unknownCardHandler was registered`); + }; + } + + get _defaultUnknownAtomHandler() { + return ({env: {name}}) => { + throw new Error(`Atom "${name}" not found but no unknownAtomHandler was registered`); + }; + } + + render() { + this.sections.forEach(section => { + let rendered = this.renderSection(section); + if (rendered) { + this.root.appendChild(rendered); + } + }); + for (let i=0; i < this._renderCallbacks.length; i++) { + this._renderCallbacks[i](); + } + // maintain a reference to child nodes so they can be cleaned up later by teardown + this._renderedChildNodes = Array.prototype.slice.call(this.root.childNodes); + return { result: this.root, teardown: () => this.teardown() }; + } + + teardown() { + for (let i=0; i < this._teardownCallbacks.length; i++) { + this._teardownCallbacks[i](); + } + for (let i=0; i < this._renderedChildNodes.length; i++) { + let node = this._renderedChildNodes[i]; + if (node.parentNode) { + node.parentNode.removeChild(node); + } + } + } + + renderSection(section) { + const [type] = section; + switch (type) { + case MARKUP_SECTION_TYPE$1: + return this.renderMarkupSection(section); + case IMAGE_SECTION_TYPE$1: + return this.renderImageSection(section); + case LIST_SECTION_TYPE$1: + return this.renderListSection(section); + case CARD_SECTION_TYPE: + return this.renderCardSection(section); + default: + throw new Error(`Cannot render mobiledoc section of type "${type}"`); + } + } + + renderMarkersOnElement(element, markers) { + let elements = [element]; + let currentElement = element; + + let pushElement = (openedElement) => { + currentElement.appendChild(openedElement); + elements.push(openedElement); + currentElement = openedElement; + }; + + for (let i=0, l=markers.length; i { + element.appendChild(this.renderListItem(li)); + }); + return element; + } + + renderImageSection([type, src]) { + let element = this.dom.createElement(IMAGE_SECTION_TAG_NAME$1); + element.src = src; + return element; + } + + findCard(name) { + for (let i=0; i < this.cards.length; i++) { + if (this.cards[i].name === name) { + return this.cards[i]; + } + } + if (name === ImageCard$1.name) { + return ImageCard$1; + } + return this._createUnknownCard(name); + } + + _findCardByIndex(index) { + let cardType = this.cardTypes[index]; + if (!cardType) { + throw new Error(`No card definition found at index ${index}`); + } + + let [ name, payload ] = cardType; + let card = this.findCard(name); + + return { + card, + payload + }; + } + + _createUnknownCard(name) { + return { + name, + type: RENDER_TYPE, + render: this.unknownCardHandler + }; + } + + _createCardArgument(card, payload={}) { + let env = { + name: card.name, + isInEditor: false, + dom: this.dom, + didRender: (callback) => this._registerRenderCallback(callback), + onTeardown: (callback) => this._registerTeardownCallback(callback) + }; + + let options = this.cardOptions; + + return { env, options, payload }; + } + + _registerTeardownCallback(callback) { + this._teardownCallbacks.push(callback); + } + + _registerRenderCallback(callback) { + this._renderCallbacks.push(callback); + } + + renderCardSection([type, index]) { + let { card, payload } = this._findCardByIndex(index); + + let cardArg = this._createCardArgument(card, payload); + let rendered = card.render(cardArg); + + this._validateCardRender(rendered, card.name); + + return rendered; + } + + _validateCardRender(rendered, cardName) { + if (!rendered) { + return; + } + + if (typeof rendered !== 'object') { + throw new Error(`Card "${cardName}" must render ${RENDER_TYPE}, but result was "${rendered}"`); + } + } + + findAtom(name) { + for (let i=0; i < this.atoms.length; i++) { + if (this.atoms[i].name === name) { + return this.atoms[i]; + } + } + return this._createUnknownAtom(name); + } + + _createUnknownAtom(name) { + return { + name, + type: RENDER_TYPE, + render: this.unknownAtomHandler + }; + } + + _createAtomArgument(atom, value, payload) { + let env = { + name: atom.name, + isInEditor: false, + dom: this.dom, + onTeardown: (callback) => this._registerTeardownCallback(callback) + }; + + let options = this.cardOptions; + + return { env, options, value, payload }; + } + + _validateAtomRender(rendered, atomName) { + if (!rendered) { + return; + } + + if (typeof rendered !== 'object') { + throw new Error(`Atom "${atomName}" must render ${RENDER_TYPE}, but result was "${rendered}"`); + } + } + + _findAtomByIndex(index) { + let atomType = this.atomTypes[index]; + if (!atomType) { + throw new Error(`No atom definition found at index ${index}`); + } + + let [ name, value, payload ] = atomType; + let atom = this.findAtom(name); + + return { + atom, + value, + payload + }; + } + + _renderAtom(index) { + let { atom, value, payload } = this._findAtomByIndex(index); + + let atomArg = this._createAtomArgument(atom, value, payload); + let rendered = atom.render(atomArg); + + this._validateAtomRender(rendered, atom.name); + + return rendered || createTextNode(this.dom, ''); + } + + renderMarkupSection([type, tagName, markers, attributes = []]) { + tagName = tagName.toLowerCase(); + if (!isValidSectionTagName(tagName, MARKUP_SECTION_TYPE$1)) { + return; + } + + let attrsObj = reduceAttributes(attributes); + let renderer = this.sectionElementRendererFor(tagName); + let element = renderer(tagName, this.dom, attrsObj); + + this.renderMarkersOnElement(element, markers); + return element; + } + + sectionElementRendererFor(tagName) { + return this.sectionElementRenderer[tagName] || + this.sectionElementRenderer.__default__; + } +} + +/** + * runtime DOM renderer + * renders a mobiledoc to DOM + * + * input: mobiledoc + * output: DOM + */ + + function validateCards$1(cards) { + if (!Array.isArray(cards)) { + throw new Error('`cards` must be passed as an array'); + } + for (let i=0; i < cards.length; i++) { + let card = cards[i]; + if (card.type !== RENDER_TYPE) { + throw new Error(`Card "${card.name}" must be of type "${RENDER_TYPE}", was "${card.type}"`); + } + if (!card.render) { + throw new Error(`Card "${card.name}" must define \`render\``); + } + } + } + + function validateAtoms$1(atoms) { + if (!Array.isArray(atoms)) { + throw new Error('`atoms` must be passed as an array'); + } + for (let i=0; i < atoms.length; i++) { + let atom = atoms[i]; + if (atom.type !== RENDER_TYPE) { + throw new Error(`Atom "${atom.name}" must be type "${RENDER_TYPE}", was "${atom.type}"`); + } + if (!atom.render) { + throw new Error(`Atom "${atom.name}" must define \`render\``); + } + } + } + + class RendererFactory { + constructor({ + cards=[], + atoms=[], + cardOptions={}, + unknownCardHandler, + unknownAtomHandler, + markupElementRenderer={}, + sectionElementRenderer={}, + dom, + markupSanitizer=null + }={}) { + validateCards$1(cards); + validateAtoms$1(atoms); + + if (!dom) { + if (typeof window === 'undefined') { + throw new Error('A `dom` option must be provided to the renderer when running without window.document'); + } + dom = window.document; + } + + this.options = { + cards, + atoms, + cardOptions, + unknownCardHandler, + unknownAtomHandler, + markupElementRenderer, + sectionElementRenderer, + dom, + markupSanitizer + }; + } + + render(mobiledoc) { + let { version } = mobiledoc; + switch (version) { + case MOBILEDOC_VERSION$5: + case undefined: + case null: + return new Renderer$1(mobiledoc, this.options).render(); + case MOBILEDOC_VERSION_0_3_0: + case MOBILEDOC_VERSION_0_3_1: + case MOBILEDOC_VERSION_0_3_2: + return new Renderer$2(mobiledoc, this.options).render(); + default: + throw new Error(`Unexpected Mobiledoc version "${version}"`); + } + } + } + +var ImageCard$2 = { + name: 'image-card', + type: 'text', + render() {} +}; + +var RENDER_TYPE$1 = 'text'; + +const MARKUP_SECTION_TYPE$2 = 1; +const IMAGE_SECTION_TYPE$2 = 2; +const LIST_SECTION_TYPE$2 = 3; +const CARD_SECTION_TYPE$1 = 10; + +/** + * runtime Text renderer + * renders a mobiledoc to Text + * + * input: mobiledoc + * output: Text (string) + */ + +const LINE_BREAK = '\n'; + +const MOBILEDOC_VERSION$6 = '0.2.0'; + +function validateVersion$2(version) { + if (version !== MOBILEDOC_VERSION$6) { + throw new Error(`Unexpected Mobiledoc version "${version}"`); + } +} + +class Renderer$3 { + constructor(mobiledoc, state) { + let { cards, cardOptions, atoms, unknownCardHandler } = state; + let { version, sections: sectionData } = mobiledoc; + validateVersion$2(version); + + let [, sections] = sectionData; + + this.root = []; + this.sections = sections; + this.cards = cards; + this.atoms = atoms; + this.cardOptions = cardOptions; + this.unknownCardHandler = unknownCardHandler || this._defaultUnknownCardHandler; + + this._teardownCallbacks = []; + } + + render() { + this.sections.forEach(section => { + this.root.push(this.renderSection(section)); + }); + + let result = this.root.join(LINE_BREAK); + return { result, teardown: () => this.teardown() }; + } + + teardown() { + for (let i=0; i < this._teardownCallbacks.length; i++) { + this._teardownCallbacks[i](); + } + } + + get _defaultUnknownCardHandler() { + return () => { + // for the text renderer, a missing card is a no-op + }; + } + + renderSection(section) { + const [type] = section; + switch (type) { + case MARKUP_SECTION_TYPE$2: + return this.renderMarkupSection(section); + case IMAGE_SECTION_TYPE$2: + return this.renderImageSection(section); + case LIST_SECTION_TYPE$2: + return this.renderListSection(section); + case CARD_SECTION_TYPE$1: + return this.renderCardSection(section); + default: + throw new Error('Unimplemented renderer for type ' + type); + } + } + + renderImageSection() { + return ''; + } + + renderListSection([type, tagName, items]) { + return items.map( + li => this.renderListItem(li) + ).join(LINE_BREAK); + } + + renderListItem(markers) { + return this.renderMarkers(markers); + } + + findCard(name) { + for (let i=0; i < this.cards.length; i++) { + if (this.cards[i].name === name) { + return this.cards[i]; + } + } + if (name === ImageCard$2.name) { + return ImageCard$2; + } + return this._createUnknownCard(name); + } + + _createUnknownCard(name) { + return { + name, + type: RENDER_TYPE$1, + render: this.unknownCardHandler + }; + } + + renderCardSection([type, name, payload]) { + let card = this.findCard(name); + + let cardArg = this._createCardArgument(card, payload); + let rendered = card.render(cardArg); + + this._validateCardRender(rendered, card.name); + + return rendered || ''; + } + + _validateCardRender(rendered, cardName) { + if (!rendered) { + return; + } + + if (typeof rendered !== 'string') { + throw new Error(`Card "${cardName}" must render ${RENDER_TYPE$1}, but result was ${typeof rendered}"`); + } + } + + _registerTeardownCallback(callback) { + this._teardownCallbacks.push(callback); + } + + _createCardArgument(card, payload={}) { + let env = { + name: card.name, + isInEditor: false, + onTeardown: (callback) => this._registerTeardownCallback(callback) + }; + + let options = this.cardOptions; + + return { env, options, payload }; + } + + renderMarkupSection([type, tagName, markers]) { + return this.renderMarkers(markers); + } + + renderMarkers(markers) { + let str = ''; + markers.forEach(m => { + let [, , text] = m; + str += text; + }); + return str; + } +} + +const MARKUP_MARKER_TYPE$1 = 0; +const ATOM_MARKER_TYPE$1 = 1; + +/** + * runtime Text renderer + * renders a mobiledoc to Text + * + * input: mobiledoc + * output: Text (string) + */ + +const LINE_BREAK$1 = '\n'; + +const MOBILEDOC_VERSION_0_3 = '0.3.0'; +const MOBILEDOC_VERSION_0_3_1$1 = '0.3.1'; +const MOBILEDOC_VERSION_0_3_2$1 = '0.3.2'; + +function validateVersion$3(version) { + if ( + version !== MOBILEDOC_VERSION_0_3 && + version !== MOBILEDOC_VERSION_0_3_1$1 && + version !== MOBILEDOC_VERSION_0_3_2$1 + ) { + throw new Error(`Unexpected Mobiledoc version "${version}"`); + } +} + +class Renderer$4 { + constructor(mobiledoc, state) { + + let { cards, cardOptions, atoms, unknownCardHandler, unknownAtomHandler } = state; + let { version, sections, atoms: atomTypes, cards: cardTypes } = mobiledoc; + validateVersion$3(version); + + this.root = []; + this.sections = sections; + this.atomTypes = atomTypes; + this.cardTypes = cardTypes; + this.cards = cards; + this.atoms = atoms; + this.cardOptions = cardOptions; + this.unknownCardHandler = unknownCardHandler || this._defaultUnknownCardHandler; + this.unknownAtomHandler = unknownAtomHandler || this._defaultUnknownAtomHandler; + + this._teardownCallbacks = []; + } + + render() { + this.sections.forEach(section => { + this.root.push(this.renderSection(section)); + }); + + let result = this.root.join(LINE_BREAK$1); + return { result, teardown: () => this.teardown() }; + } + + teardown() { + for (let i=0; i < this._teardownCallbacks.length; i++) { + this._teardownCallbacks[i](); + } + } + + get _defaultUnknownCardHandler() { + return () => { + // for the text renderer, a missing card is a no-op + }; + } + + get _defaultUnknownAtomHandler() { + return ({ value }) => { + return value || ''; + }; + } + + renderSection(section) { + const [type] = section; + switch (type) { + case MARKUP_SECTION_TYPE$2: + return this.renderMarkupSection(section); + case IMAGE_SECTION_TYPE$2: + return this.renderImageSection(section); + case LIST_SECTION_TYPE$2: + return this.renderListSection(section); + case CARD_SECTION_TYPE$1: + return this.renderCardSection(section); + default: + throw new Error('Unimplemented renderer for type ' + type); + } + } + + renderImageSection() { + return ''; + } + + renderListSection([type, tagName, items]) { + return items.map( + li => this.renderListItem(li) + ).join(LINE_BREAK$1); + } + + renderListItem(markers) { + return this.renderMarkers(markers); + } + + findCard(name) { + for (let i=0; i < this.cards.length; i++) { + if (this.cards[i].name === name) { + return this.cards[i]; + } + } + if (name === ImageCard$2.name) { + return ImageCard$2; + } + return this._createUnknownCard(name); + } + + _findCardByIndex(index) { + let cardType = this.cardTypes[index]; + if (!cardType) { + throw new Error(`No card definition found at index ${index}`); + } + + let [ name, payload ] = cardType; + let card = this.findCard(name); + + return { + card, + payload + }; + } + + _createUnknownCard(name) { + return { + name, + type: RENDER_TYPE$1, + render: this.unknownCardHandler + }; + } + + renderCardSection([type, index]) { + let { card, payload } = this._findCardByIndex(index); + + let cardArg = this._createCardArgument(card, payload); + let rendered = card.render(cardArg); + + this._validateCardRender(rendered, card.name); + + return rendered || ''; + } + + _validateCardRender(rendered, cardName) { + if (!rendered) { + return; + } + + if (typeof rendered !== 'string') { + throw new Error(`Card "${cardName}" must render ${RENDER_TYPE$1}, but result was ${typeof rendered}"`); + } + } + + _registerTeardownCallback(callback) { + this._teardownCallbacks.push(callback); + } + + _createCardArgument(card, payload={}) { + let env = { + name: card.name, + isInEditor: false, + onTeardown: (callback) => this._registerTeardownCallback(callback) + }; + + let options = this.cardOptions; + + return { env, options, payload }; + } + + renderMarkupSection([type, tagName, markers]) { + return this.renderMarkers(markers); + } + + findAtom(name) { + for (let i=0; i < this.atoms.length; i++) { + if (this.atoms[i].name === name) { + return this.atoms[i]; + } + } + return this._createUnknownAtom(name); + } + + _createUnknownAtom(name) { + return { + name, + type: RENDER_TYPE$1, + render: this.unknownAtomHandler + }; + } + + _createAtomArgument(atom, value, payload) { + let env = { + name: atom.name, + onTeardown: (callback) => this._registerTeardownCallback(callback) + }; + + let options = this.cardOptions; + + return { env, options, value, payload }; + } + + _validateAtomRender(rendered, atomName) { + if (!rendered) { + return; + } + + if (typeof rendered !== 'string') { + throw new Error(`Atom "${atomName}" must render ${RENDER_TYPE$1}, but result was ${typeof rendered}"`); + } + } + + _findAtomByIndex(index) { + let atomType = this.atomTypes[index]; + if (!atomType) { + throw new Error(`No atom definition found at index ${index}`); + } + + let [ name, value, payload ] = atomType; + let atom = this.findAtom(name); + + return { + atom, + value, + payload + }; + } + + _renderAtom(index) { + let { atom, value, payload } = this._findAtomByIndex(index); + + let atomArg = this._createAtomArgument(atom, value, payload); + let rendered = atom.render(atomArg); + + this._validateAtomRender(rendered, atom.name); + + return rendered || ''; + } + + renderMarkers(markers) { + let str = ''; + markers.forEach(m => { + let [type, , , value] = m; + switch (type) { + case MARKUP_MARKER_TYPE$1: + str += value; + break; + case ATOM_MARKER_TYPE$1: + str += this._renderAtom(value); + break; + default: + throw new Error(`Unknown markup type (${type})`); + } + }); + return str; + } +} + +/** + * runtime Text renderer + * renders a mobiledoc to Text + * + * input: mobiledoc + * output: Text (string) + */ + +function validateCards$2(cards) { + if (!Array.isArray(cards)) { + throw new Error('`cards` must be passed as an array'); + } + for (let i=0; i < cards.length; i++) { + let card = cards[i]; + if (card.type !== RENDER_TYPE$1) { + throw new Error(`Card "${card.name}" must be type "${RENDER_TYPE$1}", was "${card.type}"`); + } + if (!card.render) { + throw new Error(`Card "${card.name}" must define \`render\``); + } + } +} + +function validateAtoms$2(atoms) { + if (!Array.isArray(atoms)) { + throw new Error('`atoms` must be passed as an array'); + } + for (let i=0; i < atoms.length; i++) { + let atom = atoms[i]; + if (atom.type !== RENDER_TYPE$1) { + throw new Error(`Atom "${atom.name}" must be type "${RENDER_TYPE$1}", was "${atom.type}"`); + } + if (!atom.render) { + throw new Error(`Atom "${atom.name}" must define \`render\``); + } + } +} + +class RendererFactory$1 { + constructor({cards, atoms, cardOptions, unknownCardHandler, unknownAtomHandler}={}) { + cards = cards || []; + validateCards$2(cards); + atoms = atoms || []; + validateAtoms$2(atoms); + cardOptions = cardOptions || {}; + + this.state = {cards, atoms, cardOptions, unknownCardHandler, unknownAtomHandler}; + } + + render(mobiledoc) { + let { version } = mobiledoc; + switch (version) { + case MOBILEDOC_VERSION$6: + return new Renderer$3(mobiledoc, this.state).render(); + case undefined: + case null: + case MOBILEDOC_VERSION_0_3: + case MOBILEDOC_VERSION_0_3_1$1: + case MOBILEDOC_VERSION_0_3_2$1: + return new Renderer$4(mobiledoc, this.state).render(); + default: + throw new Error(`Unexpected Mobiledoc version "${version}"`); + } + } +} + +class Logger { + constructor(type, manager) { + this.type = type; + this.manager = manager; + } + + isEnabled() { + return this.manager.isEnabled(this.type); + } + + log(...args) { + args.unshift(`[${this.type}]`); + if (this.isEnabled()) { + window.console.log(...args); + } + } +} + +class LogManager { + constructor() { + this.enabledTypes = []; + this.allEnabled = false; + } + + for(type) { + return new Logger(type, this); + } + + enableAll() { + this.allEnabled = true; + } + + enableTypes(types) { + this.enabledTypes = this.enabledTypes.concat(types); + } + + disable() { + this.enabledTypes = []; + this.allEnabled = false; + } + + isEnabled(type) { + return this.allEnabled || this.enabledTypes.indexOf(type) !== -1; + } +} + +const defaults = { + placeholder: 'Write here...', + spellcheck: true, + autofocus: true, + showLinkTooltips: true, + undoDepth: 5, + undoBlockTimeout: 5000, // ms for an undo event + cards: [], + atoms: [], + cardOptions: {}, + unknownCardHandler: ({env}) => { + throw new MobiledocError(`Unknown card encountered: ${env.name}`); + }, + unknownAtomHandler: ({env}) => { + throw new MobiledocError(`Unknown atom encountered: ${env.name}`); + }, + mobiledoc: null, + html: null +}; + +const CALLBACK_QUEUES$1 = { + DID_UPDATE: 'didUpdate', + WILL_RENDER: 'willRender', + DID_RENDER: 'didRender', + WILL_DELETE: 'willDelete', + DID_DELETE: 'didDelete', + WILL_HANDLE_NEWLINE: 'willHandleNewline', + CURSOR_DID_CHANGE: 'cursorDidChange', + DID_REPARSE: 'didReparse', + POST_DID_CHANGE: 'postDidChange', + INPUT_MODE_DID_CHANGE: 'inputModeDidChange' +}; + +/** + * The Editor is a core component of mobiledoc-kit. After instantiating + * an editor, use {@link Editor#render} to display the editor on the web page. + * + * An editor uses a {@link Post} internally to represent the displayed document. + * The post can be serialized as mobiledoc using {@link Editor#serialize}. Mobiledoc + * is the transportable "over-the-wire" format (JSON) that is suited for persisting + * and sharing between editors and renderers (for display, e.g.), whereas the Post + * model is better suited for programmatic editing. + * + * The editor will call registered callbacks for certain state changes. These are: + * * {@link Editor#cursorDidChange} -- The cursor position or selection changed. + * * {@link Editor#postDidChange} -- The contents of the post changed due to user input or + * programmatic editing. This hook can be used with {@link Editor#serialize} + * to auto-save a post as it is being edited. + * * {@link Editor#inputModeDidChange} -- The active section(s) or markup(s) at the current cursor + * position or selection have changed. This hook can be used with + * {@link Editor#activeMarkups} and {@link Editor#activeSections} to implement + * a custom toolbar. + * * {@link Editor#onTextInput} -- Register callbacks when the user enters text + * that matches a given string or regex. + * * {@link Editor#beforeToggleMarkup} -- Register callbacks that will be run before + * applying changes from {@link Editor#toggleMarkup} + */ +class Editor { + /** + * @param {Object} [options] + * @param {Object} [options.mobiledoc] The mobiledoc to load into the editor. + * Supersedes `options.html`. + * @param {String|DOM} [options.html] The html (as a string or DOM fragment) + * to parse and load into the editor. + * Will be ignored if `options.mobiledoc` is also passed. + * @param {Array} [options.parserPlugins=[]] + * @param {Array} [options.cards=[]] The cards that the editor may render. + * @param {Array} [options.atoms=[]] The atoms that the editor may render. + * @param {Function} [options.unknownCardHandler] Invoked by the editor's renderer + * whenever it encounters an unknown card. + * @param {Function} [options.unknownAtomHandler] Invoked by the editor's renderer + * whenever it encounters an unknown atom. + * @param {String} [options.placeholder] Default text to show before user starts typing. + * @param {Boolean} [options.spellcheck=true] Whether to enable spellcheck + * @param {Boolean} [options.autofocus=true] Whether to focus the editor when it is first rendered. + * @param {Boolean} [options.showLinkTooltips=true] Whether to show the url tooltip for links + * @param {number} [options.undoDepth=5] How many undo levels will be available. + * Set to 0 to disable undo/redo functionality. + * @return {Editor} + * @public + */ + constructor(options={}) { + assert('editor create accepts an options object. For legacy usage passing an element for the first argument, consider the `html` option for loading DOM or HTML posts. For other cases call `editor.render(domNode)` after editor creation', + (options && !options.nodeType)); + this._views = []; + this.isEditable = true; + this._parserPlugins = options.parserPlugins || []; + + // FIXME: This should merge onto this.options + mergeWithOptions(this, defaults, options); + this.cards.push(ImageCard); + + DEFAULT_KEY_COMMANDS.forEach(kc => this.registerKeyCommand(kc)); + + this._logManager = new LogManager(); + this._parser = new DOMParser(this.builder); + let {cards, atoms, unknownCardHandler, unknownAtomHandler, cardOptions} = this; + this._renderer = new Renderer(this, cards, atoms, unknownCardHandler, unknownAtomHandler, cardOptions); + + this.post = this.loadPost(); + this._renderTree = new RenderTree(this.post); + + this._editHistory = new EditHistory(this, this.undoDepth, this.undoBlockTimeout); + this._eventManager = new EventManager(this); + this._mutationHandler = new MutationHandler(this); + this._editState = new EditState(this); + this._callbacks = new LifecycleCallbacks(values(CALLBACK_QUEUES$1)); + this._beforeHooks = { toggleMarkup: [] }; + + DEFAULT_TEXT_INPUT_HANDLERS.forEach(handler => this.onTextInput(handler)); + + this.hasRendered = false; + } + + /** + * Turns on verbose logging for the editor. + * @param {Array} [logTypes=[]] If present, only the given log types will be logged. + * @public + */ + enableLogging(logTypes=[]) { + if (logTypes.length === 0) { + this._logManager.enableAll(); + } else { + this._logManager.enableTypes(logTypes); + } + } + + /** + * Disable all logging + * @public + */ + disableLogging() { + this._logManager.disable(); + } + + /** + * @private + */ + loggerFor(type) { + return this._logManager.for(type); + } + + /** + * The editor's instance of a post node builder. + * @type {PostNodeBuilder} + */ + get builder() { + if (!this._builder) { this._builder = new PostNodeBuilder(); } + return this._builder; + } + + loadPost() { + let {mobiledoc, html} = this; + if (mobiledoc) { + return mobiledocParsers.parse(this.builder, mobiledoc); + } else if (html) { + if (typeof html === 'string') { + let options = {plugins: this._parserPlugins}; + return new HTMLParser(this.builder, options).parse(this.html); + } else { + let dom = html; + return this._parser.parse(dom); + } + } else { + return this.builder.createPost(); + } + } + + rerender() { + let postRenderNode = this.post.renderNode; + + // if we haven't rendered this post's renderNode before, mark it dirty + if (!postRenderNode.element) { + assert('Must call `render` before `rerender` can be called', + this.hasRendered); + postRenderNode.element = this.element; + postRenderNode.markDirty(); + } + + this.runCallbacks(CALLBACK_QUEUES$1.WILL_RENDER); + this._mutationHandler.suspendObservation(() => { + this._renderer.render(this._renderTree); + }); + this.runCallbacks(CALLBACK_QUEUES$1.DID_RENDER); + } + + /** + * @param {Element} element The DOM element to render into. + * Its contents will be replaced by the editor's rendered post. + * @public + */ + render(element) { + assert('Cannot render an editor twice. Use `rerender` to update the ' + + 'rendering of an existing editor instance.', + !this.hasRendered); + + element.spellcheck = this.spellcheck; + + clearChildNodes(element); + + this.element = element; + + if (this.showLinkTooltips) { + this._addTooltip(); + } + + // A call to `run` will trigger the didUpdatePostCallbacks hooks with a + // postEditor. + this.run(() => {}); + + // Only set `hasRendered` to true after calling `run` to ensure that + // no cursorDidChange or other callbacks get fired before the editor is + // done rendering + this.hasRendered = true; + this.rerender(); + + this._mutationHandler.init(); + this._eventManager.init(); + + if (this.isEditable === false) { + this.disableEditing(); + } else { + this.enableEditing(); + } + + if (this.autofocus) { + this.selectRange(this.post.headPosition()); + } + } + + _addTooltip() { + this.addView(new Tooltip({ + rootElement: this.element, + showForTag: 'a' + })); + } + + get keyCommands() { + if (!this._keyCommands) { this._keyCommands = []; } + return this._keyCommands; + } + + /** + * @param {Object} keyCommand The key command to register. It must specify a + * modifier key (meta, ctrl, etc), a string representing the ascii key, and + * a `run` method that will be passed the editor instance when the key command + * is invoked + * @public + */ + registerKeyCommand(rawKeyCommand) { + const keyCommand = buildKeyCommand(rawKeyCommand); + assert('Key Command is not valid', validateKeyCommand(keyCommand)); + this.keyCommands.unshift(keyCommand); + } + + /** + * @param {String} name If the keyCommand event has a name attribute it can be removed. + * @public + */ + unregisterKeyCommands(name) { + for(let i = this.keyCommands.length-1; i > -1; i--) { + let keyCommand = this.keyCommands[i]; + + if(keyCommand.name === name) { + this.keyCommands.splice(i,1); + } + } + } + + /** + * Convenience for {@link PostEditor#deleteAtPosition}. Deletes and puts the + * cursor in the new position. + * @public + */ + deleteAtPosition(position, direction, {unit}) { + this.run(postEditor => { + let nextPosition = postEditor.deleteAtPosition(position, direction, {unit}); + postEditor.setRange(nextPosition); + }); + } + + /** + * Convenience for {@link PostEditor#deleteRange}. Deletes and puts the + * cursor in the new position. + * @param {Range} range + * @public + */ + deleteRange(range) { + this.run(postEditor => { + let nextPosition = postEditor.deleteRange(range); + postEditor.setRange(nextPosition); + }); + } + + /** + * @private + */ + performDelete({direction, unit}={direction: DIRECTION.BACKWARD, unit: 'char'}) { + let { range } = this; + + this.runCallbacks(CALLBACK_QUEUES$1.WILL_DELETE, [range, direction, unit]); + if (range.isCollapsed) { + this.deleteAtPosition(range.head, direction, {unit}); + } else { + this.deleteRange(range); + } + this.runCallbacks(CALLBACK_QUEUES$1.DID_DELETE, [range, direction, unit]); + } + + handleNewline(event) { + if (!this.hasCursor()) { return; } + + event.preventDefault(); + + let { range } = this; + this.run(postEditor => { + let cursorSection; + if (!range.isCollapsed) { + let nextPosition = postEditor.deleteRange(range); + cursorSection = nextPosition.section; + if (cursorSection && cursorSection.isBlank) { + postEditor.setRange(cursorSection.headPosition()); + return; + } + } + + // Above logic might delete redundant range, so callback must run after it. + let defaultPrevented = false; + const event = { preventDefault() { defaultPrevented = true; } }; + this.runCallbacks(CALLBACK_QUEUES$1.WILL_HANDLE_NEWLINE, [event]); + if (defaultPrevented) { return; } + + cursorSection = postEditor.splitSection(range.head)[1]; + postEditor.setRange(cursorSection.headPosition()); + }); + } + + /** + * Notify the editor that the post did change, and run associated + * callbacks. + * @private + */ + _postDidChange() { + this.runCallbacks(CALLBACK_QUEUES$1.POST_DID_CHANGE); + } + + /** + * Selects the given range or position. If given a collapsed range or a position, this positions the cursor + * at the range's position. Otherwise a selection is created in the editor + * surface encompassing the range. + * @param {Range|Position} range + */ + selectRange(range) { + range = toRange(range); + + this.cursor.selectRange(range); + this.range = range; + } + + get cursor() { + return new Cursor(this); + } + + /** + * Return the current range for the editor (may be cached). + * @return {Range} + */ + get range() { + return this._editState.range; + } + + set range(newRange) { + this._editState.updateRange(newRange); + + if (this._editState.rangeDidChange()) { + this._rangeDidChange(); + } + + if (this._editState.inputModeDidChange()) { + this._inputModeDidChange(); + } + } + + _readRangeFromDOM() { + this.range = this.cursor.offsets; + } + + setPlaceholder(placeholder) { + setData(this.element, 'placeholder', placeholder); + } + + _reparsePost() { + let post = this._parser.parse(this.element); + this.run(postEditor => { + postEditor.removeAllSections(); + postEditor.migrateSectionsFromPost(post); + postEditor.setRange(Range.blankRange()); + }); + + this.runCallbacks(CALLBACK_QUEUES$1.DID_REPARSE); + this._postDidChange(); + } + + _reparseSections(sections=[]) { + let currentRange; + sections.forEach(section => { + this._parser.reparseSection(section, this._renderTree); + }); + this._removeDetachedSections(); + + if (this._renderTree.isDirty) { + currentRange = this.range; + } + + // force the current snapshot's range to remain the same rather than + // rereading it from DOM after the new character is applied and the browser + // updates the cursor position + let range = this._editHistory._pendingSnapshot.range; + this.run(() => { + this._editHistory._pendingSnapshot.range = range; + }); + this.rerender(); + if (currentRange) { + this.selectRange(currentRange); + } + + this.runCallbacks(CALLBACK_QUEUES$1.DID_REPARSE); + this._postDidChange(); + } + + // FIXME this should be able to be removed now -- if any sections are detached, + // it's due to a bug in the code. + _removeDetachedSections() { + forEach( + filter(this.post.sections, s => !s.renderNode.isAttached()), + s => s.renderNode.scheduleForRemoval() + ); + } + + /** + * The sections from the cursor's selection start to the selection end + * @type {Section[]} + */ + get activeSections() { + return this._editState.activeSections; + } + + get activeSection() { + const { activeSections } = this; + return activeSections[activeSections.length - 1]; + } + + get activeSectionAttributes() { + return this._editState.activeSectionAttributes; + } + + detectMarkupInRange(range, markupTagName) { + let markups = this.post.markupsInRange(range); + return detect(markups, markup => { + return markup.hasTag(markupTagName); + }); + } + + /** + * @type {Markup[]} + * @public + */ + get activeMarkups() { + return this._editState.activeMarkups; + } + + /** + * @param {Markup|String} markup A markup instance, or a string (e.g. "b") + * @return {boolean} + */ + hasActiveMarkup(markup) { + let matchesFn; + if (typeof markup === 'string') { + let tagName = normalizeTagName(markup); + matchesFn = (m) => m.tagName === tagName; + } else { + matchesFn = (m) => m === markup; + } + + return !!detect(this.activeMarkups, matchesFn); + } + + /** + * @param {String} version The mobiledoc version to serialize to. + * @return {Mobiledoc} Serialized mobiledoc + * @public + */ + serialize(version=MOBILEDOC_VERSION$4) { + return this.serializePost(this.post, 'mobiledoc', {version}); + } + + /** + * Serialize the editor's post to the requested format. + * Note that only mobiledoc format is lossless. If cards or atoms are present + * in the post, the html and text formats will omit them in output because + * the editor does not have access to the html and text versions of the + * cards/atoms. + * @param {string} format The format to serialize ('mobiledoc', 'text', 'html') + * @return {Object|String} The editor's post, serialized to {format} + * @public + */ + serializeTo(format) { + let post = this.post; + return this.serializePost(post, format); + } + + /** + * @param {Post} + * @param {String} format Same as {serializeTo} + * @param {Object} [options] + * @param {String} [options.version=MOBILEDOC_VERSION] version to serialize to + * @return {Object|String} + * @private + */ + serializePost(post, format, options={}) { + const validFormats = ['mobiledoc', 'html', 'text']; + assert(`Unrecognized serialization format ${format}`, + contains(validFormats, format)); + + if (format === 'mobiledoc') { + let version = options.version || MOBILEDOC_VERSION$4; + return mobiledocRenderers.render(post, version); + } else { + let rendered; + let mobiledoc = this.serializePost(post, 'mobiledoc'); + let unknownCardHandler = () => {}; + let unknownAtomHandler = () => {}; + let rendererOptions = { unknownCardHandler, unknownAtomHandler }; + + switch (format) { + case 'html': { + let result; + if (Environment.hasDOM()) { + rendered = new RendererFactory(rendererOptions).render(mobiledoc); + result = `
    ${serializeHTML(rendered.result)}
    `; + } else { + // Fallback to text serialization + result = this.serializePost(post, 'text', options); + } + return result; + } + case 'text': + rendered = new RendererFactory$1(rendererOptions).render(mobiledoc); + return rendered.result; + } + } + } + + addView(view) { + this._views.push(view); + } + + removeAllViews() { + this._views.forEach((v) => v.destroy()); + this._views = []; + } + + /** + * Whether the editor has a cursor (or a selected range). + * It is possible for the editor to be focused but not have a selection. + * In this case, key events will fire but the editor will not be able to + * determine a cursor position, so they will be ignored. + * @return {boolean} + * @public + */ + hasCursor() { + return this.cursor.hasCursor(); + } + + /** + * Tears down the editor's attached event listeners and views. + * @public + */ + destroy() { + this.isDestroyed = true; + if (this._hasSelection()) { + this.cursor.clearSelection(); + } + if (this._hasFocus()) { + this.element.blur(); // FIXME This doesn't blur the element on IE11 + } + this._mutationHandler.destroy(); + this._eventManager.destroy(); + this.removeAllViews(); + this._renderer.destroy(); + this._editState.destroy(); + } + + /** + * Keep the user from directly editing the post using the keyboard and mouse. + * Modification via the programmatic API is still permitted. + * @see Editor#enableEditing + * @public + */ + disableEditing() { + this.isEditable = false; + if (this.hasRendered) { + this._eventManager.stop(); + this.element.setAttribute('contentEditable', false); + this.setPlaceholder(''); + this.selectRange(Range.blankRange()); + } + } + + /** + * Allow the user to directly interact with editing a post via keyboard and mouse input. + * Editor instances are editable by default. Use this method to re-enable + * editing after disabling it. + * @see Editor#disableEditing + * @public + */ + enableEditing() { + this.isEditable = true; + if (this.hasRendered) { + this._eventManager.start(); + this.element.setAttribute('contentEditable', true); + this.setPlaceholder(this.placeholder); + } + } + + /** + * Change a cardSection into edit mode + * If called before the card has been rendered, it will be marked so that + * it is rendered in edit mode when it gets rendered. + * @param {CardSection} cardSection + * @public + */ + editCard(cardSection) { + this._setCardMode(cardSection, CARD_MODES.EDIT); + } + + /** + * Change a cardSection into display mode + * If called before the card has been rendered, it will be marked so that + * it is rendered in display mode when it gets rendered. + * @param {CardSection} cardSection + * @return undefined + * @public + */ + displayCard(cardSection) { + this._setCardMode(cardSection, CARD_MODES.DISPLAY); + } + + /** + * Run a new post editing session. Yields a block with a new {@link PostEditor} + * instance. This instance can be used to interact with the post abstract. + * Rendering will be deferred until after the callback is completed. + * + * Usage: + * ``` + * let markerRange = this.range; + * editor.run((postEditor) => { + * postEditor.deleteRange(markerRange); + * // editing surface not updated yet + * postEditor.schedule(() => { + * console.log('logs during rerender flush'); + * }); + * // logging not yet flushed + * }); + * // editing surface now updated. + * // logging now flushed + * ``` + * + * @param {Function} callback Called with an instance of + * {@link PostEditor} as its argument. + * @return {Mixed} The return value of `callback`. + * @public + */ + run(callback) { + const postEditor = new PostEditor(this); + postEditor.begin(); + this._editHistory.snapshot(); + const result = callback(postEditor); + this.runCallbacks(CALLBACK_QUEUES$1.DID_UPDATE, [postEditor]); + postEditor.complete(); + this._readRangeFromDOM(); + + if (postEditor._shouldCancelSnapshot) { + this._editHistory._pendingSnapshot = null; + } + this._editHistory.storeSnapshot(postEditor.editActionTaken); + + return result; + } + + /** + * @param {Function} callback Called with `postEditor` as its argument. + * @public + */ + didUpdatePost(callback) { + this.addCallback(CALLBACK_QUEUES$1.DID_UPDATE, callback); + } + + /** + * @param {Function} callback Called when the post has changed, either via + * user input or programmatically. Use with {@link Editor#serialize} to + * retrieve the post in portable mobiledoc format. + */ + postDidChange(callback) { + this.addCallback(CALLBACK_QUEUES$1.POST_DID_CHANGE, callback); + } + + /** + * Register a handler that will be invoked by the editor after the user enters + * matching text. + * @param {Object} inputHandler + * @param {String} inputHandler.name Required. Used by identifying handlers. + * @param {String} [inputHandler.text] Required if `match` is not provided + * @param {RegExp} [inputHandler.match] Required if `text` is not provided + * @param {Function} inputHandler.run This callback is invoked with the {@link Editor} + * instance and an array of matches. If `text` was provided, + * the matches array will equal [`text`], and if a `match` + * regex was provided the matches array will be the result of + * `match.exec` on the matching text. The callback is called + * after the matching text has been inserted. + * @public + */ + onTextInput(inputHandler) { + this._eventManager.registerInputHandler(inputHandler); + } + + /** + * Unregister all text input handlers + * + * @public + */ + unregisterAllTextInputHandlers() { + this._eventManager.unregisterAllTextInputHandlers(); + } + + /** + * Unregister text input handler by name + * @param {String} name The name of handler to be removed + * + * @public + */ + unregisterTextInputHandler(name) { + this._eventManager.unregisterInputHandler(name); + } + + /** + * @param {Function} callback Called when the editor's state (active markups or + * active sections) has changed, either via user input or programmatically + */ + inputModeDidChange(callback) { + this.addCallback(CALLBACK_QUEUES$1.INPUT_MODE_DID_CHANGE, callback); + } + + /** + * @param {Function} callback This callback will be called before the editor + * is rendered. + * @public + */ + willRender(callback) { + this.addCallback(CALLBACK_QUEUES$1.WILL_RENDER, callback); + } + + /** + * @param {Function} callback This callback will be called after the editor + * is rendered. + * @public + */ + didRender(callback) { + this.addCallback(CALLBACK_QUEUES$1.DID_RENDER, callback); + } + + /** + * @param {Function} callback This callback will be called before deleting. + * @public + */ + willDelete(callback) { + this.addCallback(CALLBACK_QUEUES$1.WILL_DELETE, callback); + } + + /** + * @param {Function} callback This callback will be called after deleting. + * @public + */ + didDelete(callback) { + this.addCallback(CALLBACK_QUEUES$1.DID_DELETE, callback); + } + + /** + * @param {Function} callback This callback will be called before handling new line. + * @public + */ + willHandleNewline(callback) { + this.addCallback(CALLBACK_QUEUES$1.WILL_HANDLE_NEWLINE, callback); + } + + /** + * @param {Function} callback This callback will be called every time the cursor + * position (or selection) changes. + * @public + */ + cursorDidChange(callback) { + this.addCallback(CALLBACK_QUEUES$1.CURSOR_DID_CHANGE, callback); + } + + _rangeDidChange() { + if (this.hasRendered) { + this.runCallbacks(CALLBACK_QUEUES$1.CURSOR_DID_CHANGE); + } + } + + _inputModeDidChange() { + this.runCallbacks(CALLBACK_QUEUES$1.INPUT_MODE_DID_CHANGE); + } + + _insertEmptyMarkupSectionAtCursor() { + this.run(postEditor => { + const section = postEditor.builder.createMarkupSection('p'); + postEditor.insertSectionBefore(this.post.sections, section); + postEditor.setRange(section.toRange()); + }); + } + + /** + * @callback editorBeforeCallback + * @param { Object } details + * @param { Markup } details.markup + * @param { Range } details.range + * @param { boolean } details.willAdd Whether the markup will be applied + */ + + /** + * Register a callback that will be run before {@link Editor#toggleMarkup} is applied. + * If any callback returns literal `false`, the toggling of markup will be canceled. + * Note this only applies to calling `editor#toggleMarkup`. Using `editor.run` and + * modifying markup with the `postEditor` will skip any `beforeToggleMarkup` callbacks. + * @param {editorBeforeCallback} + */ + beforeToggleMarkup(callback) { + this._beforeHooks.toggleMarkup.push(callback); + } + + /** + * Toggles the given markup at the editor's current {@link Range}. + * If the range is collapsed this changes the editor's state so that the + * next characters typed will be affected. If there is text selected + * (aka a non-collapsed range), the selections' markup will be toggled. + * If the editor is not focused and has no active range, nothing happens. + * Hooks added using #beforeToggleMarkup will be run before toggling, + * and if any of them returns literal false, toggling the markup will be canceled + * and no change will be applied. + * @param {String} markup E.g. "b", "em", "a" + * @param {Object} [attributes={}] E.g. {href: "http://bustle.com"} + * @public + * @see PostEditor#toggleMarkup + */ + toggleMarkup(markup, attributes={}) { + markup = this.builder.createMarkup(markup, attributes); + let { range } = this; + let willAdd = !this.detectMarkupInRange(range, markup.tagName); + let shouldCancel = this._runBeforeHooks('toggleMarkup', {markup, range, willAdd}); + if (shouldCancel) { return; } + + if (range.isCollapsed) { + this._editState.toggleMarkupState(markup); + this._inputModeDidChange(); + + // when clicking a button to toggle markup, the button can end up being focused, + // so ensure the editor is focused + this._ensureFocus(); + } else { + this.run(postEditor => postEditor.toggleMarkup(markup, range)); + } + } + + // If the editor has a selection but is not focused, focus it + _ensureFocus() { + if (this._hasSelection() && !this._hasFocus()) { + this.focus(); + } + } + + focus() { + this.element.focus(); + } + + /** + * Whether there is a selection inside the editor's element. + * It's possible to have a selection but not have focus. + * @see #_hasFocus + * @return {Boolean} + */ + _hasSelection() { + let { cursor } = this; + return this.hasRendered && (cursor._hasCollapsedSelection() || cursor._hasSelection()); + } + + /** + * Whether the editor's element is focused + * It's possible to be focused but have no selection + * @see #_hasSelection + * @return {Boolean} + */ + _hasFocus() { + return document.activeElement === this.element; + } + + /** + * Toggles the tagName for the current active section(s). This will skip + * non-markerable sections. E.g. if the editor's range includes a "P" MarkupSection + * and a CardSection, only the MarkupSection will be toggled. + * @param {String} tagName The new tagname to change to. + * @public + * @see PostEditor#toggleSection + */ + toggleSection(tagName) { + this.run(postEditor => postEditor.toggleSection(tagName, this.range)); + } + + /** + * Sets an attribute for the current active section(s). + * + * @param {String} key The attribute. The only valid attribute is 'text-align'. + * @param {String} value The value of the attribute. + * @public + * @see PostEditor#setAttribute + */ + setAttribute(key, value) { + this.run(postEditor => postEditor.setAttribute(key, value, this.range)); + } + + /** + * Removes an attribute from the current active section(s). + * + * @param {String} key The attribute. The only valid attribute is 'text-align'. + * @public + * @see PostEditor#removeAttribute + */ + removeAttribute(key) { + this.run(postEditor => postEditor.removeAttribute(key, this.range)); + } + + /** + * Finds and runs the first matching key command for the event + * + * If multiple commands are bound to a key combination, the + * first matching one is run. + * + * If a command returns `false` then the next matching command + * is run instead. + * + * @param {Event} event The keyboard event triggered by the user + * @return {Boolean} true when a command was successfully run + * @private + */ + handleKeyCommand(event) { + const keyCommands = findKeyCommands(this.keyCommands, event); + for (let i=0; i { + if (!range.isCollapsed) { + position = postEditor.deleteRange(range); + } + + postEditor.insertTextWithMarkup(position, text, activeMarkups); + }); + } + + /** + * Inserts an atom at the current cursor position. If the editor has + * no current cursor position, nothing will be inserted. If the editor's + * range is not collapsed, it will be deleted before insertion. + * @param {String} atomName + * @param {String} [atomText=''] + * @param {Object} [atomPayload={}] + * @return {Atom} The inserted atom. + * @public + */ + insertAtom(atomName, atomText='', atomPayload={}) { + if (!this.hasCursor()) { return; } + if (this.post.isBlank) { + this._insertEmptyMarkupSectionAtCursor(); + } + + let atom; + let { range } = this; + this.run(postEditor => { + let position = range.head; + + atom = postEditor.builder.createAtom(atomName, atomText, atomPayload); + if (!range.isCollapsed) { + position = postEditor.deleteRange(range); + } + + postEditor.insertMarkers(position, [atom]); + }); + return atom; + } + + /** + * Inserts a card at the section after the current cursor position. If the editor has + * no current cursor position, nothing will be inserted. If the editor's + * range is not collapsed, it will be deleted before insertion. If the cursor is in + * a blank section, it will be replaced with a card section. + * The editor's cursor will be placed at the end of the inserted card. + * @param {String} cardName + * @param {Object} [cardPayload={}] + * @param {Boolean} [inEditMode=false] Whether the card should be inserted in edit mode. + * @return {Card} The inserted Card section. + * @public + */ + insertCard(cardName, cardPayload={}, inEditMode=false) { + if (!this.hasCursor()) { return; } + if (this.post.isBlank) { + this._insertEmptyMarkupSectionAtCursor(); + } + + let card; + let { range } = this; + this.run(postEditor => { + let position = range.tail; + card = postEditor.builder.createCardSection(cardName, cardPayload); + if (inEditMode) { + this.editCard(card); + } + + if (!range.isCollapsed) { + position = postEditor.deleteRange(range); + } + + let section = position.section; + if (section.isNested) { section = section.parent; } + + if (section.isBlank) { + postEditor.replaceSection(section, card); + } else { + let collection = this.post.sections; + postEditor.insertSectionBefore(collection, card, section.next); + } + + // It is important to explicitly set the range to the end of the card. + // Otherwise it is possible to create an inconsistent state in the + // browser. For instance, if the user clicked a button that + // called `editor.insertCard`, the editor surface may retain + // the selection but lose focus, and the next keystroke by the user + // will cause an unexpected DOM mutation (which can wipe out the + // card). + // See: https://github.com/bustle/mobiledoc-kit/issues/286 + postEditor.setRange(card.tailPosition()); + }); + return card; + } + + /** + * @param {integer} x x-position in viewport + * @param {integer} y y-position in viewport + * @return {Position|null} + */ + positionAtPoint(x, y) { + return Position$1.atPoint(x, y, this); + } + + /** + * @private + */ + _setCardMode(cardSection, mode) { + const renderNode = cardSection.renderNode; + if (renderNode && renderNode.isRendered) { + const cardNode = renderNode.cardNode; + cardNode[mode](); + } else { + cardSection.setInitialMode(mode); + } + } + + triggerEvent(context, eventName, event) { + this._eventManager._trigger(context, eventName, event); + } + + addCallback(...args) { + this._callbacks.addCallback(...args); + } + + addCallbackOnce(...args) { + this._callbacks.addCallbackOnce(...args); + } + + runCallbacks(...args) { + if (this.isDestroyed) { + // TODO warn that callback attempted after editor was destroyed + return; + } + this._callbacks.runCallbacks(...args); + } + + /** + * Runs each callback for the given hookName. + * Only the hookName 'toggleMarkup' is currently supported + * @return {Boolean} shouldCancel Whether the action in `hookName` should be canceled + * @private + */ + _runBeforeHooks(hookName, ...args) { + let hooks = this._beforeHooks[hookName] || []; + for (let i = 0; i < hooks.length; i++) { + if (hooks[i](...args) === false) { + return true; + } + } + } +} + +var version = '##VERSION##'; + +export { Editor, MobiledocError as Error, ImageCard, MOBILEDOC_VERSION$4 as MOBILEDOC_VERSION, Position$1 as Position, Range, ui as UI, version as VERSION }; +//# sourceMappingURL=mobiledoc.js.map diff --git a/broccoli/demo.js b/broccoli/demo.js deleted file mode 100644 index f1f16a03f..000000000 --- a/broccoli/demo.js +++ /dev/null @@ -1,8 +0,0 @@ -var funnel = require('broccoli-funnel'); -var babel = require('broccoli-babel-transpiler'); - -module.exports = function() { - return funnel(babel('assets/demo/'), { - destDir: 'demo' - }); -}; diff --git a/broccoli/jquery.js b/broccoli/jquery.js deleted file mode 100644 index 2253316fc..000000000 --- a/broccoli/jquery.js +++ /dev/null @@ -1,21 +0,0 @@ -var funnel = require('broccoli-funnel'); -var mergeTrees = require('broccoli-merge-trees'); - -module.exports = { - /** - * @param {Tree} tree existing tree to mix jquery into - * @param {String} destDir the destination directory for 'jquery.js' to go into - * @return {Tree} A tree with jquery mixed into it at the location requested - */ - build: function(tree, destDir) { - var path = require('path'); - var jqueryPath = path.dirname( - require.resolve('jquery') - ); - var jqueryTree = funnel(jqueryPath, { - include: ['jquery.js'], - destDir: destDir - }); - return mergeTrees([tree, jqueryTree]); - } -}; diff --git a/package.json b/package.json index 0b107f0ea..8714470b5 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,13 @@ "description": "A toolkit for building WYSIWYG editors with Mobiledoc", "repository": "https://github.com/bustle/mobiledoc-kit", "module": "dist/mobiledoc.js", + "main": "dist/mobiledoc.cjs", + "exports": { + "import": "./dist/mobiledoc.js", + "require": "./dist/mobiledoc.cjs" + }, "scripts": { - "start": "yarn build --watch", + "start": "rollup -c --watch", "test:ci": "yarn build:docs && yarn build && testem ci -f testem-ci.js", "test": "yarn build && testem ci -f testem.js", "build": "rollup -c", @@ -14,7 +19,7 @@ "deploy:website": "./bin/deploy-website.sh", "update-changelog": "conventional-changelog -i CHANGELOG.md -r 0 -s", "version": "yarn update-changelog && git add CHANGELOG.md", - "prepublish": "yarn build" + "prepublishOnly": "yarn build" }, "keywords": [ "html", @@ -24,7 +29,9 @@ "contenteditable" ], "files": [ - "dist/mobiledoc.js" + "dist/mobiledoc.js", + "dist/mobiledoc.cjs", + "dist/mobiledoc.css" ], "author": "Garth Poitras (http://garthpoitras.com/)", "contributors": [ @@ -42,28 +49,18 @@ "@rollup/plugin-alias": "^3.0.0", "@rollup/plugin-commonjs": "^11.0.1", "@rollup/plugin-node-resolve": "^7.0.0", - "broccoli": "^1.1.3", - "broccoli-babel-transpiler": "^6.1.2", - "broccoli-cli": "^1.0.0", - "broccoli-funnel": "^2.0.0", - "broccoli-livereload": "^1.0.0", - "broccoli-merge-trees": "^3.0.0", - "broccoli-multi-builder": "^0.3.0", - "broccoli-sane-watcher": "^1.1.4", - "broccoli-string-replace": "^0.1.1", - "broccoli-test-builder": "^0.4.0", "conventional-changelog": "^1.1.3", "conventional-changelog-cli": "^1.3.2", "jquery": "^3.4.1", "jsdoc": "^3.5.4", "qunit": "^2.9.3", "rollup": "^1.31.0", + "rollup-plugin-copy": "^3.3.0", "rollup-plugin-glob-import": "^0.4.5", "rollup-plugin-serve": "^1.0.1", "saucie": "^3.3.2", "testem": "^2.17.0" }, - "main": "dist/commonjs/mobiledoc-kit/index.js", "volta": { "node": "12.14.1", "yarn": "1.21.1" diff --git a/rollup.config.js b/rollup.config.js index ebd91c293..87237a4b1 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,21 +1,20 @@ +import path from 'path'; import globImport from 'rollup-plugin-glob-import'; import alias from '@rollup/plugin-alias'; import resolve from '@rollup/plugin-node-resolve'; import commonjs from '@rollup/plugin-commonjs'; import serve from 'rollup-plugin-serve'; -import path from 'path'; +import copy from 'rollup-plugin-copy'; -export default [ +export default args => [ { input: 'src/js/index.js', plugins: [ resolve(), - commonjs(), alias({ entries: [ { find: 'mobiledoc-kit', - // eslint-disable-next-line no-undef replacement: path.join(__dirname, 'src/js') } ] @@ -27,6 +26,25 @@ export default [ sourcemap: true } }, + { + input: 'src/js/index.js', + plugins: [ + resolve(), + alias({ + entries: [ + { + find: 'mobiledoc-kit', + replacement: path.join(__dirname, 'src/js') + } + ] + }) + ], + output: { + file: 'dist/mobiledoc.cjs', + format: 'cjs', + sourcemap: true + } + }, { input: 'tests/index.js', plugins: [ @@ -36,13 +54,19 @@ export default [ entries: [ { find: 'mobiledoc-kit', - // eslint-disable-next-line no-undef replacement: path.join(__dirname, 'src/js') } ] }), globImport(), - serve({ + copy({ + targets: [ + { src: 'dist/mobiledoc.js', dest: 'assets/demo' }, + { src: 'src/css/mobiledoc-kit.css', dest: 'dist', rename: 'mobiledoc.css' }, + { src: 'src/css/mobiledoc-kit.css', dest: 'assets/demo/', rename: 'mobiledoc.css' } + ] + }), + args.watch && serve({ contentBase: '', port: process.env.PORT || 4200 }) diff --git a/src/js/editor/ui.js b/src/js/editor/ui.js index 20250f29e..7169329f2 100644 --- a/src/js/editor/ui.js +++ b/src/js/editor/ui.js @@ -64,3 +64,7 @@ export function toggleLink(editor, showPrompt=defaultShowPrompt) { }); } } + +export default { + toggleLink +}; diff --git a/src/js/index.js b/src/js/index.js index 0217d93af..404201f10 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -1,9 +1,10 @@ import Editor from './editor/editor'; import ImageCard from './cards/image'; +import UI from './editor/ui'; import Range from './utils/cursor/range'; import Position from './utils/cursor/position'; import Error from './utils/mobiledoc-error'; import VERSION from './version'; import { MOBILEDOC_VERSION } from './renderers/mobiledoc'; -export { Editor, ImageCard, Range, Position, Error, VERSION, MOBILEDOC_VERSION }; +export { Editor, UI, ImageCard, Range, Position, Error, VERSION, MOBILEDOC_VERSION }; diff --git a/testem-ci.js b/testem-ci.js index fd6bed886..ce9a70668 100644 --- a/testem-ci.js +++ b/testem-ci.js @@ -5,7 +5,7 @@ module.exports = { "disable_watching": true, "timeout": 600, "browser_start_timeout": 90, - "test_page": "dist/tests/index.html?hidepassed", + "test_page": "tests/index.html?hidepassed", "on_start": "./sauce_labs/saucie-connect.js", "on_exit": "./sauce_labs/saucie-disconnect.js", "port": 8080, diff --git a/testem.js b/testem.js index 4f7b47b79..ec897e231 100644 --- a/testem.js +++ b/testem.js @@ -2,7 +2,7 @@ module.exports = { "framework": "qunit", "browser_start_timeout": 120, - "test_page": "tests/index.rollup.html?hidepassed", + "test_page": "tests/index.html?hidepassed", "src_files": [ "tests/**/*.js", "src/**/*.js" diff --git a/yarn.lock b/yarn.lock index 375743e61..a129ac758 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,6 +7,27 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.3.tgz#790874091d2001c9be6ec426c2eed47bc7679081" integrity sha512-/V72F4Yp/qmHaTALizEm9Gf2eQHV3QyTL3K0cNfijwnMnb1L+LDlAubb/ZnSdGAVzVSWakujHYs1I26x66sMeQ== +"@nodelib/fs.scandir@2.1.3": + version "2.1.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b" + integrity sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw== + dependencies: + "@nodelib/fs.stat" "2.0.3" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.3", "@nodelib/fs.stat@^2.0.2": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz#34dc5f4cabbc720f4e60f75a747e7ecd6c175bd3" + integrity sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.4" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz#011b9202a70a6366e436ca5c065844528ab04976" + integrity sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ== + dependencies: + "@nodelib/fs.scandir" "2.1.3" + fastq "^1.6.0" + "@rollup/plugin-alias@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-alias/-/plugin-alias-3.0.0.tgz#3ac4952d05d5a700b0d918b18f312db0aa2b006f" @@ -53,6 +74,32 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== +"@types/events@*": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + +"@types/fs-extra@^8.0.1": + version "8.1.0" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-8.1.0.tgz#1114834b53c3914806cd03b3304b37b3bd221a4d" + integrity sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg== + dependencies: + "@types/node" "*" + +"@types/glob@^7.1.1": + version "7.1.1" + resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.1.tgz#aa59a1c6e3fbc421e07ccd31a944c30eba521575" + integrity sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w== + dependencies: + "@types/events" "*" + "@types/minimatch" "*" + "@types/node" "*" + +"@types/minimatch@*": + version "3.0.3" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" + integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + "@types/node@*": version "13.5.0" resolved "https://registry.yarnpkg.com/@types/node/-/node-13.5.0.tgz#4e498dbf355795a611a87ae5ef811a8660d42662" @@ -81,23 +128,6 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - integrity sha1-r9+UiPsezvyDSPb7IvRk4ypYs2s= - 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" - integrity sha1-ReN/s56No/JbruP/U2niu18iAXo= - -acorn@^5.2.1, acorn@^5.5.0: - version "5.7.3" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.7.3.tgz#67aa231bf8812974b85235a96771eb6bd07ea279" - integrity sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw== - acorn@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c" @@ -125,21 +155,6 @@ agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" -ajv-keywords@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" - integrity sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I= - -ajv@^5.2.3, ajv@^5.3.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU= - 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.5.5: version "6.11.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9" @@ -150,32 +165,6 @@ ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - integrity sha1-DNkKVhCT810KmSVsIrcGlDP60Rc= - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - -alter@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/alter/-/alter-0.2.0.tgz#c7588808617572034aae62480af26b1d4d1cb3cd" - integrity sha1-x1iICGF1cgNKrmJICvJrHU0cs80= - dependencies: - stable "~0.1.3" - -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= - -ansi-escapes@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b" - integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ== - ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -186,42 +175,6 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -anymatch@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.2.tgz#553dcb8f91e3c889845dfdba34c77721b90b9d7a" - integrity sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA== - dependencies: - micromatch "^2.1.5" - normalize-path "^2.0.0" - -aot-test-generators@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/aot-test-generators/-/aot-test-generators-0.1.0.tgz#43f0f615f97cb298d7919c1b0b4e6b7310b03cd0" - integrity sha1-Q/D2Ffl8spjXkZwbC05rcxCwPNA= - dependencies: - jsesc "^2.5.0" - -applause@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/applause/-/applause-1.2.2.tgz#a8468579e81f67397bb5634c29953bedcd0f56c0" - integrity sha1-qEaFeegfZzl7tWNMKZU77c0PVsA= - dependencies: - cson-parser "^1.1.0" - js-yaml "^3.3.0" - lodash "^3.10.0" - aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -271,33 +224,6 @@ argparse@^1.0.7: 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" - integrity sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8= - 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" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -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" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" - integrity sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM= - 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" @@ -313,15 +239,10 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha1-nlKHYrSpBmrRY6aWKjZEGOlibs4= -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - integrity sha1-odl8yvy8JiXMcPrc6zalDFiwGlM= - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== arraybuffer.slice@~0.0.7: version "0.0.7" @@ -345,63 +266,12 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -ast-traverse@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ast-traverse/-/ast-traverse-0.1.1.tgz#69cf2b8386f19dcda1bb1e05d68fe359d8897de6" - integrity sha1-ac8rg4bxnc2hux4F1o/jWdiJfeY= - -ast-types@0.8.12: - version "0.8.12" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.12.tgz#a0d90e4351bb887716c83fd637ebf818af4adfcc" - integrity sha1-oNkOQ1G7iHcWyD/WN+v4GK9K38w= - -ast-types@0.8.15: - version "0.8.15" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.8.15.tgz#8eef0827f04dff0ec8857ba925abe3fea6194e52" - integrity sha1-ju8IJ/BN/w7IhXupJavj/qYZTlI= - -ast-types@0.9.6: - version "0.9.6" - resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" - integrity sha1-ECyenpAF0+fjgpvwxPok7oYu6bk= - -async-disk-cache@^1.2.1: - version "1.3.5" - resolved "https://registry.yarnpkg.com/async-disk-cache/-/async-disk-cache-1.3.5.tgz#cc6206ed79bb6982b878fc52e0505e4f52b62a02" - integrity sha512-VZpqfR0R7CEOJZ/0FOTgWq70lCrZyS1rkI8PXugDUkTKyyAUgZ2zQ09gLhMkEn+wN8LYeUTPxZdXtlX/kmbXKQ== - dependencies: - debug "^2.1.3" - heimdalljs "^0.2.3" - istextorbinary "2.1.0" - mkdirp "^0.5.0" - rimraf "^2.5.3" - rsvp "^3.0.18" - username-sync "^1.0.2" - -async-each@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== - async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== -async-promise-queue@^1.0.3: - version "1.0.5" - resolved "https://registry.yarnpkg.com/async-promise-queue/-/async-promise-queue-1.0.5.tgz#cb23bce9fce903a133946a700cc85f27f09ea49d" - integrity sha512-xi0aQ1rrjPWYmqbwr18rrSKbSaXIeIwSd1J4KAgVfkq8utNbdZoht7GfvfY6swFUAMJ9obkc4WPJmtGwl+B8dw== - dependencies: - async "^2.4.1" - debug "^2.6.8" - -async@^2.0.0, async@^2.1.2, async@^2.4.1, async@^2.6.3: +async@^2.0.0, async@^2.1.2, async@^2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== @@ -418,11 +288,6 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -433,267 +298,6 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== -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" - integrity sha1-Y/1D99weO7fONZR9uP42mj9Yx0s= - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - -babel-core@^5.0.0: - version "5.8.38" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-5.8.38.tgz#1fcaee79d7e61b750b00b8e54f6dfc9d0af86558" - integrity sha1-H8ruedfmG3ULALjlT238nQr4ZVg= - dependencies: - babel-plugin-constant-folding "^1.0.1" - babel-plugin-dead-code-elimination "^1.0.2" - babel-plugin-eval "^1.0.1" - babel-plugin-inline-environment-variables "^1.0.1" - babel-plugin-jscript "^1.0.4" - babel-plugin-member-expression-literals "^1.0.1" - babel-plugin-property-literals "^1.0.1" - babel-plugin-proto-to-assign "^1.0.3" - babel-plugin-react-constant-elements "^1.0.3" - babel-plugin-react-display-name "^1.0.3" - babel-plugin-remove-console "^1.0.1" - babel-plugin-remove-debugger "^1.0.1" - babel-plugin-runtime "^1.0.7" - babel-plugin-undeclared-variables-check "^1.0.2" - babel-plugin-undefined-to-void "^1.1.6" - babylon "^5.8.38" - bluebird "^2.9.33" - chalk "^1.0.0" - convert-source-map "^1.1.0" - core-js "^1.0.0" - debug "^2.1.1" - detect-indent "^3.0.0" - esutils "^2.0.0" - fs-readdir-recursive "^0.1.0" - globals "^6.4.0" - home-or-tmp "^1.0.0" - is-integer "^1.0.4" - js-tokens "1.0.1" - json5 "^0.4.0" - lodash "^3.10.0" - minimatch "^2.0.3" - output-file-sync "^1.1.0" - path-exists "^1.0.0" - path-is-absolute "^1.0.0" - private "^0.1.6" - regenerator "0.8.40" - regexpu "^1.3.0" - repeating "^1.1.2" - resolve "^1.1.6" - shebang-regex "^1.0.0" - slash "^1.0.0" - source-map "^0.5.0" - source-map-support "^0.2.10" - to-fast-properties "^1.0.0" - trim-right "^1.0.0" - try-resolve "^1.0.0" - -babel-core@^6.26.0: - version "6.26.3" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.3.tgz#b2e2f09e342d0f0c88e2f02e067794125e75c207" - integrity sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA== - 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" - integrity sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA== - 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-helpers@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" - integrity sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI= - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-messages@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" - integrity sha1-8830cDhYA1sqKVHG7F7fbGLyYw4= - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-constant-folding@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-constant-folding/-/babel-plugin-constant-folding-1.0.1.tgz#8361d364c98e449c3692bdba51eff0844290aa8e" - integrity sha1-g2HTZMmORJw2kr26Ue/whEKQqo4= - -babel-plugin-dead-code-elimination@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/babel-plugin-dead-code-elimination/-/babel-plugin-dead-code-elimination-1.0.2.tgz#5f7c451274dcd7cccdbfbb3e0b85dd28121f0f65" - integrity sha1-X3xFEnTc18zNv7s+C4XdKBIfD2U= - -babel-plugin-eval@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-eval/-/babel-plugin-eval-1.0.1.tgz#a2faed25ce6be69ade4bfec263f70169195950da" - integrity sha1-ovrtJc5r5preS/7CY/cBaRlZUNo= - -babel-plugin-inline-environment-variables@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-inline-environment-variables/-/babel-plugin-inline-environment-variables-1.0.1.tgz#1f58ce91207ad6a826a8bf645fafe68ff5fe3ffe" - integrity sha1-H1jOkSB61qgmqL9kX6/mj/X+P/4= - -babel-plugin-jscript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/babel-plugin-jscript/-/babel-plugin-jscript-1.0.4.tgz#8f342c38276e87a47d5fa0a8bd3d5eb6ccad8fcc" - integrity sha1-jzQsOCduh6R9X6CovT1etsytj8w= - -babel-plugin-member-expression-literals@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-member-expression-literals/-/babel-plugin-member-expression-literals-1.0.1.tgz#cc5edb0faa8dc927170e74d6d1c02440021624d3" - integrity sha1-zF7bD6qNyScXDnTW0cAkQAIWJNM= - -babel-plugin-property-literals@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-property-literals/-/babel-plugin-property-literals-1.0.1.tgz#0252301900192980b1c118efea48ce93aab83336" - integrity sha1-AlIwGQAZKYCxwRjv6kjOk6q4MzY= - -babel-plugin-proto-to-assign@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/babel-plugin-proto-to-assign/-/babel-plugin-proto-to-assign-1.0.4.tgz#c49e7afd02f577bc4da05ea2df002250cf7cd123" - integrity sha1-xJ56/QL1d7xNoF6i3wAiUM980SM= - dependencies: - lodash "^3.9.3" - -babel-plugin-react-constant-elements@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/babel-plugin-react-constant-elements/-/babel-plugin-react-constant-elements-1.0.3.tgz#946736e8378429cbc349dcff62f51c143b34e35a" - integrity sha1-lGc26DeEKcvDSdz/YvUcFDs041o= - -babel-plugin-react-display-name@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/babel-plugin-react-display-name/-/babel-plugin-react-display-name-1.0.3.tgz#754fe38926e8424a4e7b15ab6ea6139dee0514fc" - integrity sha1-dU/jiSboQkpOexWrbqYTne4FFPw= - -babel-plugin-remove-console@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-remove-console/-/babel-plugin-remove-console-1.0.1.tgz#d8f24556c3a05005d42aaaafd27787f53ff013a7" - integrity sha1-2PJFVsOgUAXUKqqv0neH9T/wE6c= - -babel-plugin-remove-debugger@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-plugin-remove-debugger/-/babel-plugin-remove-debugger-1.0.1.tgz#fd2ea3cd61a428ad1f3b9c89882ff4293e8c14c7" - integrity sha1-/S6jzWGkKK0fO5yJiC/0KT6MFMc= - -babel-plugin-runtime@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/babel-plugin-runtime/-/babel-plugin-runtime-1.0.7.tgz#bf7c7d966dd56ecd5c17fa1cb253c9acb7e54aaf" - integrity sha1-v3x9lm3Vbs1cF/ocslPJrLflSq8= - -babel-plugin-undeclared-variables-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/babel-plugin-undeclared-variables-check/-/babel-plugin-undeclared-variables-check-1.0.2.tgz#5cf1aa539d813ff64e99641290af620965f65dee" - integrity sha1-XPGqU52BP/ZOmWQSkK9iCWX2Xe4= - dependencies: - leven "^1.0.2" - -babel-plugin-undefined-to-void@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/babel-plugin-undefined-to-void/-/babel-plugin-undefined-to-void-1.1.6.tgz#7f578ef8b78dfae6003385d8417a61eda06e2f81" - integrity sha1-f1eO+LeN+uYAM4XYQXph7aBuL4E= - -babel-register@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" - integrity sha1-btAhFz4vy0htestFxgCahW9kcHE= - 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.22.0, babel-runtime@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= - 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" - integrity sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI= - 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.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" - integrity sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4= - 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.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" - integrity sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc= - dependencies: - babel-runtime "^6.26.0" - esutils "^2.0.2" - lodash "^4.17.4" - to-fast-properties "^1.0.3" - -babylon@^5.8.38: - version "5.8.38" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-5.8.38.tgz#ec9b120b11bf6ccd4173a18bf217e60b79859ffd" - integrity sha1-7JsSCxG/bM1Bc6GL8hfmC3mFn/0= - -babylon@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" - integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== - backbone@^1.1.2: version "1.4.0" resolved "https://registry.yarnpkg.com/backbone/-/backbone-1.4.0.tgz#54db4de9df7c3811c3f032f34749a4cd27f3bd12" @@ -726,19 +330,6 @@ base64id@2.0.0: resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - 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" - bcrypt-pbkdf@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" @@ -753,23 +344,6 @@ better-assert@~1.0.0: dependencies: callsite "1.0.0" -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - -"binaryextensions@1 || 2": - version "2.2.0" - resolved "https://registry.yarnpkg.com/binaryextensions/-/binaryextensions-2.2.0.tgz#e7c6ba82d4f5f5758c26078fe8eea28881233311" - integrity sha512-bHhs98rj/7i/RZpCSJ3uk55pLXOItjIrh2sRQZSM6OoktScX+LxJzvlU+FELp9j3TdcddTmmYArLSGptCTwjuw== - -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - bl@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88" @@ -777,21 +351,11 @@ bl@^3.0.0: dependencies: readable-stream "^3.0.1" -blank-object@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/blank-object/-/blank-object-1.0.2.tgz#f990793fbe9a8c8dd013fb3219420bec81d5f4b9" - integrity sha1-+ZB5P76ajI3QE/syGUIL7IHV9Lk= - blob@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== -bluebird@^2.9.33: - version "2.11.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" - integrity sha1-U0uQM8AiyVecVro7Plpcqvu2UOE= - bluebird@^3.1.1, bluebird@^3.4.6, bluebird@^3.5.4: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" @@ -813,7 +377,7 @@ body-parser@1.19.0: raw-body "2.4.0" type-is "~1.6.17" -brace-expansion@^1.0.0, brace-expansion@^1.1.7: +brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== @@ -821,369 +385,18 @@ brace-expansion@^1.0.0, brace-expansion@^1.1.7: 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" - integrity sha1-uneWLhLf+WnWt2cR6RS3N4V79qc= - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - -braces@^2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - 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" - -breakable@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/breakable/-/breakable-1.0.0.tgz#784a797915a38ead27bad456b5572cb4bbaa78c1" - integrity sha1-eEp5eRWjjq0nutRWtVcstLuqeME= - -broccoli-amd-loader@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/broccoli-amd-loader/-/broccoli-amd-loader-0.2.0.tgz#13b122648f8c4ae037d06cfe995ad22cba5a5a9b" - integrity sha1-E7EiZI+MSuA30Gz+mVrSLLpaWps= - dependencies: - broccoli-funnel "^1.1.0" - broccoli-merge-trees "^2.0.0" - loader.js "^4.2.3" - -broccoli-babel-transpiler@^5.6.2: - version "5.7.4" - resolved "https://registry.yarnpkg.com/broccoli-babel-transpiler/-/broccoli-babel-transpiler-5.7.4.tgz#2b0611ce9e5d98b8d8d2b49ae1219af2f52767e3" - integrity sha512-gI14Pqc4qbmn5RW4SuAmybLiOoYW59D+HzQyhY6WdaGMAjikKBwJN0p17phyvafQ+kvG0mUiMd83lgHLeATnEA== - dependencies: - babel-core "^5.0.0" - broccoli-funnel "^1.0.0" - broccoli-merge-trees "^1.0.0" - broccoli-persistent-filter "^1.4.2" - clone "^0.2.0" - hash-for-dep "^1.0.2" - heimdalljs-logger "^0.1.7" - json-stable-stringify "^1.0.0" - rsvp "^3.5.0" - workerpool "^2.3.0" - -broccoli-babel-transpiler@^6.1.2: - version "6.5.1" - resolved "https://registry.yarnpkg.com/broccoli-babel-transpiler/-/broccoli-babel-transpiler-6.5.1.tgz#a4afc8d3b59b441518eb9a07bd44149476e30738" - integrity sha512-w6GcnkxvHcNCte5FcLGEG1hUdQvlfvSN/6PtGWU/otg69Ugk8rUk51h41R0Ugoc+TNxyeFG1opRt2RlA87XzNw== - dependencies: - babel-core "^6.26.0" - broccoli-funnel "^2.0.1" - broccoli-merge-trees "^2.0.0" - broccoli-persistent-filter "^1.4.3" - clone "^2.0.0" - hash-for-dep "^1.2.3" - heimdalljs-logger "^0.1.7" - json-stable-stringify "^1.0.0" - rsvp "^4.8.2" - workerpool "^2.3.0" - -broccoli-cli@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/broccoli-cli/-/broccoli-cli-1.0.0.tgz#69444521a5e631569300fbc196e85e18097b22a4" - integrity sha1-aURFIaXmMVaTAPvBluheGAl7IqQ= - dependencies: - resolve "~0.6.1" - -broccoli-concat@^3.2.0, broccoli-concat@^3.2.2: - version "3.7.5" - resolved "https://registry.yarnpkg.com/broccoli-concat/-/broccoli-concat-3.7.5.tgz#223beda8c1184252cf08ae020a3d45ffa6a48218" - integrity sha512-rDs1Mej3Ej0Cy5yIO9oIQq5+BCv0opAwS2NW7M0BeCsAMeFM42Z/zacDUC6jKc5OV5wiHvGTyCPLnZkMe0h6kQ== - dependencies: - broccoli-debug "^0.6.5" - broccoli-kitchen-sink-helpers "^0.3.1" - broccoli-plugin "^1.3.0" - ensure-posix-path "^1.0.2" - fast-sourcemap-concat "^1.4.0" - find-index "^1.1.0" - fs-extra "^4.0.3" - fs-tree-diff "^0.5.7" - lodash.merge "^4.6.2" - lodash.omit "^4.1.0" - lodash.uniq "^4.2.0" - walk-sync "^0.3.2" - -broccoli-debug@^0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/broccoli-debug/-/broccoli-debug-0.6.5.tgz#164a5cdafd8936e525e702bf8f91f39d758e2e78" - integrity sha512-RIVjHvNar9EMCLDW/FggxFRXqpjhncM/3qq87bn/y+/zR9tqEkHvTqbyOc4QnB97NO2m6342w4wGkemkaeOuWg== - dependencies: - broccoli-plugin "^1.2.1" - fs-tree-diff "^0.5.2" - heimdalljs "^0.2.1" - heimdalljs-logger "^0.1.7" - symlink-or-copy "^1.1.8" - tree-sync "^1.2.2" - -broccoli-filter@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/broccoli-filter/-/broccoli-filter-1.3.0.tgz#71e3a8e32a17f309e12261919c5b1006d6766de6" - integrity sha512-VXJXw7eBfG82CFxaBDjYmyN7V72D4In2zwLVQJd/h3mBfF3CMdRTsv2L20lmRTtCv1sAHcB+LgMso90e/KYiLw== - dependencies: - broccoli-kitchen-sink-helpers "^0.3.1" - broccoli-plugin "^1.0.0" - copy-dereference "^1.0.0" - debug "^2.2.0" - mkdirp "^0.5.1" - promise-map-series "^0.2.1" - rsvp "^3.0.18" - symlink-or-copy "^1.0.1" - walk-sync "^0.3.1" - -broccoli-funnel@^1.0.0, broccoli-funnel@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/broccoli-funnel/-/broccoli-funnel-1.2.0.tgz#cddc3afc5ff1685a8023488fff74ce6fb5a51296" - integrity sha1-zdw6/F/xaFqAI0iP/3TOb7WlEpY= - dependencies: - array-equal "^1.0.0" - blank-object "^1.0.1" - broccoli-plugin "^1.3.0" - debug "^2.2.0" - exists-sync "0.0.4" - fast-ordered-set "^1.0.0" - fs-tree-diff "^0.5.3" - heimdalljs "^0.2.0" - minimatch "^3.0.0" - mkdirp "^0.5.0" - path-posix "^1.0.0" - rimraf "^2.4.3" - symlink-or-copy "^1.0.0" - walk-sync "^0.3.1" - -broccoli-funnel@^2.0.0, broccoli-funnel@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/broccoli-funnel/-/broccoli-funnel-2.0.2.tgz#0edf629569bc10bd02cc525f74b9a38e71366a75" - integrity sha512-/vDTqtv7ipjEZQOVqO4vGDVAOZyuYzQ/EgGoyewfOgh1M7IQAToBKZI0oAQPgMBeFPPlIbfMuAngk+ohPBuaHQ== - dependencies: - array-equal "^1.0.0" - blank-object "^1.0.1" - broccoli-plugin "^1.3.0" - debug "^2.2.0" - fast-ordered-set "^1.0.0" - fs-tree-diff "^0.5.3" - heimdalljs "^0.2.0" - minimatch "^3.0.0" - mkdirp "^0.5.0" - path-posix "^1.0.0" - rimraf "^2.4.3" - symlink-or-copy "^1.0.0" - walk-sync "^0.3.1" - -broccoli-kitchen-sink-helpers@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/broccoli-kitchen-sink-helpers/-/broccoli-kitchen-sink-helpers-0.3.1.tgz#77c7c18194b9664163ec4fcee2793444926e0c06" - integrity sha1-d8fBgZS5ZkFj7E/O4nk0RJJuDAY= - dependencies: - glob "^5.0.10" - mkdirp "^0.5.1" - -broccoli-lint-eslint@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/broccoli-lint-eslint/-/broccoli-lint-eslint-4.2.1.tgz#f780dc083a7357a9746a9cfa8f76feb092777477" - integrity sha512-Jvm06UvuMPa5gEH+9/Sb+QpoIodDAYzbyIUEqxniPCdA6JJooa91hQDCTJc32RUV46JNMcLhb3Dl55BdA8v5mw== - dependencies: - aot-test-generators "^0.1.0" - broccoli-concat "^3.2.2" - broccoli-persistent-filter "^1.4.3" - eslint "^4.0.0" - json-stable-stringify "^1.0.1" - lodash.defaultsdeep "^4.6.0" - md5-hex "^2.0.0" - -broccoli-livereload@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/broccoli-livereload/-/broccoli-livereload-1.4.0.tgz#edd5873857d6e419d427b2ba3e1f08869a6ce57e" - integrity sha512-cKHsFUPj70ygm+TT6qX3/cnj+99vTCJYO8YJHvWeSOe2eWcoj4pDvcjv9XWbcq1/LwrI/l5M1puVZQFanO/VMA== - dependencies: - broccoli-filter "^1.3.0" - livereload "^0.7.0" - node-html-light "^1.1.0" - -broccoli-merge-trees@^1.0.0: - version "1.2.4" - resolved "https://registry.yarnpkg.com/broccoli-merge-trees/-/broccoli-merge-trees-1.2.4.tgz#a001519bb5067f06589d91afa2942445a2d0fdb5" - integrity sha1-oAFRm7UGfwZYnZGvopQkRaLQ/bU= - dependencies: - broccoli-plugin "^1.3.0" - can-symlink "^1.0.0" - fast-ordered-set "^1.0.2" - fs-tree-diff "^0.5.4" - heimdalljs "^0.2.1" - heimdalljs-logger "^0.1.7" - rimraf "^2.4.3" - symlink-or-copy "^1.0.0" - -broccoli-merge-trees@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/broccoli-merge-trees/-/broccoli-merge-trees-2.0.1.tgz#14d4b7fc1a90318c12b16f843e6ba2693808100c" - integrity sha512-WjaexJ+I8BxP5V5RNn6um/qDRSmKoiBC/QkRi79FT9ClHfldxRyCDs9mcV7mmoaPlsshmmPaUz5jdtcKA6DClQ== - dependencies: - broccoli-plugin "^1.3.0" - merge-trees "^1.0.1" - -broccoli-merge-trees@^3.0.0: +braces@^3.0.1: version "3.0.2" - resolved "https://registry.yarnpkg.com/broccoli-merge-trees/-/broccoli-merge-trees-3.0.2.tgz#f33b451994225522b5c9bcf27d59decfd8ba537d" - integrity sha512-ZyPAwrOdlCddduFbsMyyFzJUrvW6b04pMvDiAQZrCwghlvgowJDY+EfoXn+eR1RRA5nmGHJ+B68T63VnpRiT1A== - dependencies: - broccoli-plugin "^1.3.0" - merge-trees "^2.0.0" - -broccoli-multi-builder@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/broccoli-multi-builder/-/broccoli-multi-builder-0.3.0.tgz#3ab4b957fcf3ee04c01159a4ea373e171e98da3f" - integrity sha1-OrS5V/zz7gTAEVmk6jc+Fx6Y2j8= - dependencies: - broccoli-amd-loader "^0.2.0" - broccoli-babel-transpiler "^5.6.2" - broccoli-concat "^3.2.0" - broccoli-funnel "^1.1.0" - broccoli-merge-trees "^2.0.0" - exists-sync "0.0.4" - lodash "^3.10.0" - -broccoli-node-info@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/broccoli-node-info/-/broccoli-node-info-1.1.0.tgz#3aa2e31e07e5bdb516dd25214f7c45ba1c459412" - integrity sha1-OqLjHgflvbUW3SUhT3xFuhxFlBI= - -broccoli-persistent-filter@^1.1.5, broccoli-persistent-filter@^1.2.0, broccoli-persistent-filter@^1.4.2, broccoli-persistent-filter@^1.4.3: - version "1.4.6" - resolved "https://registry.yarnpkg.com/broccoli-persistent-filter/-/broccoli-persistent-filter-1.4.6.tgz#80762d19000880a77da33c34373299c0f6a3e615" - integrity sha512-0RejLwoC95kv4kta8KAa+FmECJCK78Qgm8SRDEK7YyU0N9Cx6KpY3UCDy9WELl3mCXLN8TokNxc7/hp3lL4lfw== - dependencies: - async-disk-cache "^1.2.1" - async-promise-queue "^1.0.3" - broccoli-plugin "^1.0.0" - fs-tree-diff "^0.5.2" - hash-for-dep "^1.0.2" - heimdalljs "^0.2.1" - heimdalljs-logger "^0.1.7" - mkdirp "^0.5.1" - promise-map-series "^0.2.1" - rimraf "^2.6.1" - rsvp "^3.0.18" - symlink-or-copy "^1.0.1" - walk-sync "^0.3.1" - -broccoli-plugin@^1.0.0, broccoli-plugin@^1.2.1, broccoli-plugin@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/broccoli-plugin/-/broccoli-plugin-1.3.1.tgz#a26315732fb99ed2d9fb58f12a1e14e986b4fabd" - integrity sha512-DW8XASZkmorp+q7J4EeDEZz+LoyKLAd2XZULXyD9l4m9/hAKV3vjHmB1kiUshcWAYMgTP1m2i4NnqCE/23h6AQ== - dependencies: - promise-map-series "^0.2.1" - quick-temp "^0.1.3" - rimraf "^2.3.4" - symlink-or-copy "^1.1.8" - -broccoli-replace@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/broccoli-replace/-/broccoli-replace-0.12.0.tgz#36460a984c45c61731638c53068b0ab12ea8fdb7" - integrity sha1-NkYKmExFxhcxY4xTBosKsS6o/bc= - dependencies: - applause "1.2.2" - broccoli-persistent-filter "^1.2.0" - minimatch "^3.0.0" - -broccoli-sane-watcher@^1.1.4: - version "1.1.5" - resolved "https://registry.yarnpkg.com/broccoli-sane-watcher/-/broccoli-sane-watcher-1.1.5.tgz#f2b0af9cf0afb74c7a49cd88eb11c6869ee8c0c0" - integrity sha1-8rCvnPCvt0x6Sc2I6xHGhp7owMA= + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: - broccoli-slow-trees "^1.1.0" - debug "^2.1.0" - rsvp "^3.0.18" - sane "^1.1.1" - -broccoli-slow-trees@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/broccoli-slow-trees/-/broccoli-slow-trees-2.0.0.tgz#9741afe992787add64aec7f7c8211dfcc058278d" - integrity sha1-l0Gv6ZJ4et1krsf3yCEd/MBYJ40= - -broccoli-slow-trees@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/broccoli-slow-trees/-/broccoli-slow-trees-1.1.0.tgz#426c5724e008107e4573f73e8a9ca702916b78f7" - integrity sha1-QmxXJOAIEH5Fc/c+ipynApFrePc= - -broccoli-source@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/broccoli-source/-/broccoli-source-1.1.0.tgz#54f0e82c8b73f46580cbbc4f578f0b32fca8f809" - integrity sha1-VPDoLItz9GWAy7xPV48LMvyo+Ak= - -broccoli-string-replace@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/broccoli-string-replace/-/broccoli-string-replace-0.1.2.tgz#1ed92f85680af8d503023925e754e4e33676b91f" - integrity sha1-HtkvhWgK+NUDAjkl51Tk4zZ2uR8= - dependencies: - broccoli-persistent-filter "^1.1.5" - minimatch "^3.0.3" - -broccoli-test-builder@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/broccoli-test-builder/-/broccoli-test-builder-0.4.0.tgz#7f35f1cd7a75acfe640db642bc87319c52aa2090" - integrity sha512-rpykQGlr8W4aJCUpHfs4bg0g55Mk9CZ1W7viJe5KZF+Sy4Jor00GS+/UkPPDwXVvV5IbqJWdm5f4hJ+60iV1SQ== - dependencies: - broccoli-amd-loader "^0.2.0" - broccoli-babel-transpiler "^5.6.2" - broccoli-concat "^3.2.0" - broccoli-funnel "^1.1.0" - broccoli-lint-eslint "^4.0.0" - broccoli-merge-trees "^2.0.0" - broccoli-replace "^0.12.0" - qunitjs "^2.1.1" - -broccoli@^1.1.3: - version "1.1.4" - resolved "https://registry.yarnpkg.com/broccoli/-/broccoli-1.1.4.tgz#b023b028b866f447ed14341007961efd03f7251c" - integrity sha1-sCOwKLhm9EftFDQQB5Ye/QP3JRw= - dependencies: - broccoli-node-info "1.1.0" - broccoli-slow-trees "2.0.0" - broccoli-source "^1.1.0" - commander "^2.5.0" - connect "^3.3.3" - copy-dereference "^1.0.0" - findup-sync "^1.0.0" - handlebars "^4.0.4" - heimdalljs-logger "^0.1.7" - mime "^1.2.11" - rimraf "^2.4.3" - rsvp "^3.5.0" - sane "^1.4.1" - tmp "0.0.31" - underscore.string "^3.2.2" - -bser@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" - integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== - dependencies: - node-int64 "^0.4.0" + fill-range "^7.0.1" buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - buffer@^5.1.0: version "5.4.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz#3fbc9c69eb713d323e3fc1a895eee0710c072115" @@ -1207,38 +420,11 @@ bytes@3.1.0: resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - 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" - -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - integrity sha1-lAhe9jWB7NPaqSREqP6U6CV3dR8= - dependencies: - callsites "^0.2.0" - callsite@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" - integrity sha1-r6uWJikQp/M8GaV3WCXGnzTjUMo= - camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" @@ -1256,11 +442,6 @@ camelcase-keys@^4.0.0: map-obj "^2.0.0" quick-lru "^1.0.0" -camelcase@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - integrity sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk= - camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" @@ -1271,13 +452,6 @@ camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0= -can-symlink@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/can-symlink/-/can-symlink-1.0.0.tgz#97b607d8a84bb6c6e228b902d864ecb594b9d219" - integrity sha1-l7YH2KhLtsbiKLkC2GTstZS50hk= - dependencies: - tmp "0.0.28" - caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -1290,39 +464,6 @@ catharsis@^0.8.11: dependencies: lodash "^4.17.14" -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - integrity sha1-qg0yYptu6XIgBBHL1EYckHvCt60= - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" - -chalk@^1.0.0, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - 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.1.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chardet@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" - integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= - charm@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/charm/-/charm-1.0.2.tgz#8add367153a6d9a581331052c4090991da995e35" @@ -1330,123 +471,15 @@ charm@^1.0.0: dependencies: inherits "^2.0.1" -chokidar@1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" - integrity sha1-L0RHq16W5Q+z14n9kNTHLg5McMI= - dependencies: - anymatch "^1.3.0" - async-each "^1.0.0" - glob-parent "^2.0.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^2.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - optionalDependencies: - fsevents "^1.0.0" - -chokidar@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.7.0.tgz#798e689778151c8076b4b360e5edd28cda2bb468" - integrity sha1-eY5ol3gVHIB2tLNg5e3SjNortGg= - dependencies: - anymatch "^1.3.0" - async-each "^1.0.0" - glob-parent "^2.0.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^2.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - optionalDependencies: - fsevents "^1.0.0" - -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" - integrity sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A== - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -clean-up-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clean-up-path/-/clean-up-path-1.0.0.tgz#de9e8196519912e749c9eaf67c13d64fac72a3e5" - integrity sha512-PHGlEF0Z6976qQyN6gM7kKH6EH0RdfZcc8V+QhFe36eRxV0SMH5OUBZG7Bxa9YcreNzyNbK63cGiZxdSZgosRw== - -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU= - 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" - integrity sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk= - -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - integrity sha1-S0dXYP+AJkx2LDoXGQMukcf+oNE= - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" - -clone@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" - integrity sha1-xhJqkK1Pctv1rNskPMN3JP6T/B8= - -clone@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" - integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= - 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" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -coffee-script@^1.10.0: - version "1.12.7" - resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.7.tgz#c05dae0cb79591d05b3070a8433a98c9a89ccc53" - integrity sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw== - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +colorette@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.1.0.tgz#1f943e5a357fac10b4e0f5aaef3b14cdc1af6ec7" + integrity sha512-6S062WDQUXi6hOfkO/sBPVwE5ASXY4G2+b4atvhJfSsuUUhIaUKlkjLe9692Ipyt5/a+IPF5aVTu3V5gvXq5cg== combined-stream@^1.0.6, combined-stream@~1.0.6: version "1.0.8" @@ -1460,33 +493,11 @@ commander@2.12.2: resolved "https://registry.yarnpkg.com/commander/-/commander-2.12.2.tgz#0f5946c427ed9ec0d91a46bb9def53e54650e555" integrity sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA== -commander@2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" - integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q= - dependencies: - graceful-readlink ">= 1.0.0" - -commander@^2.5.0, commander@^2.6.0, commander@^2.9.0, commander@~2.20.3: +commander@^2.6.0, commander@^2.9.0, commander@~2.20.3: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commoner@~0.10.3: - version "0.10.8" - resolved "https://registry.yarnpkg.com/commoner/-/commoner-0.10.8.tgz#34fc3672cd24393e8bb47e70caa0293811f4f2c5" - integrity sha1-NPw2cs0kOT6LtH5wyqApOBH08sU= - dependencies: - commander "^2.5.0" - detective "^4.3.1" - glob "^5.0.15" - graceful-fs "^4.1.2" - iconv-lite "^0.4.5" - mkdirp "^0.5.0" - private "^0.1.6" - q "^1.1.2" - recast "^0.11.17" - compare-func@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-1.3.2.tgz#99dd0ba457e1f9bc722b12c08ec33eeab31fa648" @@ -1505,11 +516,6 @@ component-emitter@1.2.1: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - component-inherit@0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" @@ -1550,26 +556,6 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.6.0: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -connect@^3.3.3: - version "3.7.0" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" - integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== - dependencies: - debug "2.6.9" - finalhandler "1.1.2" - parseurl "~1.3.3" - utils-merge "1.0.1" - 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" @@ -1748,13 +734,6 @@ conventional-commits-parser@^2.1.7: through2 "^2.0.0" trim-off-newlines "^1.0.0" -convert-source-map@^1.1.0, convert-source-map@^1.5.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" - cookie-signature@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" @@ -1770,26 +749,6 @@ cookie@0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== -copy-dereference@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/copy-dereference/-/copy-dereference-1.0.0.tgz#6b131865420fd81b413ba994b44d3655311152b6" - integrity sha1-axMYZUIP2BtBO6mUtE02VTERUrY= - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -core-js@^1.0.0: - version "1.2.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" - integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY= - -core-js@^2.4.0, core-js@^2.5.0: - version "2.6.11" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" - integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== - core-util-is@1.0.2, 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" @@ -1810,15 +769,6 @@ crc@^3.4.4: dependencies: buffer "^5.1.0" -cross-spawn@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -1830,13 +780,6 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" -cson-parser@^1.1.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/cson-parser/-/cson-parser-1.3.5.tgz#7ec675e039145533bf2a6a856073f1599d9c2d24" - integrity sha1-fsZ14DkUVTO/KmqFYHPxWZ2cLSQ= - dependencies: - coffee-script "^1.10.0" - currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -1863,7 +806,7 @@ dateformat@^3.0.0: resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae" integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q== -debug@2.6.9, debug@^2.1.0, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: +debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -1899,64 +842,11 @@ decamelize-keys@^1.0.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.0.0, decamelize@^1.1.0, decamelize@^1.1.2: +decamelize@^1.1.0, decamelize@^1.1.2: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= -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" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - 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" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - 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" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - 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" - integrity sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM= - -defs@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/defs/-/defs-1.1.1.tgz#b22609f2c7a11ba7a3db116805c139b1caffa9d2" - integrity sha1-siYJ8sehG6ej2xFoBcE5scr/qdI= - dependencies: - alter "~0.2.0" - ast-traverse "~0.1.1" - breakable "~1.0.0" - esprima-fb "~15001.1001.0-dev-harmony-fb" - simple-fmt "~0.1.0" - simple-is "~0.2.0" - stringmap "~0.2.2" - stringset "~0.2.1" - tryor "~0.1.2" - yargs "~3.27.0" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -1977,76 +867,12 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= -detect-file@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-0.1.0.tgz#4935dedfd9488648e006b0129566e9386711ea63" - integrity sha1-STXe39lIhkjgBrASlWbpOGcR6mM= - dependencies: - fs-exists-sync "^0.1.0" - -detect-indent@^3.0.0: +dir-glob@^3.0.1: version "3.0.1" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-3.0.1.tgz#9dc5e5ddbceef8325764b9451b02bc6d54084f75" - integrity sha1-ncXl3bzu+DJXZLlFGwK8bVQIT3U= - dependencies: - get-stdin "^4.0.1" - minimist "^1.1.0" - repeating "^1.1.0" - -detect-indent@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" - integrity sha1-920GQ1LN9Docts5hnE7jqUdd4gg= - dependencies: - repeating "^2.0.0" - -detective@^4.3.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/detective/-/detective-4.7.1.tgz#0eca7314338442febb6d65da54c10bb1c82b246e" - integrity sha512-H6PmeeUcZloWtdt4DAkFyzFL94arpHr3NOwwmVILFiy+9Qd4JTxxXrzfyGk/lmct2qVGBwTSwSXagqu2BxmWig== - dependencies: - acorn "^5.2.1" - defined "^1.0.0" - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -dom-serializer@0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - -domelementtype@1, domelementtype@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domelementtype@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" - integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== - -domhandler@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" - integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== - dependencies: - domelementtype "1" - -domutils@^1.5.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== dependencies: - dom-serializer "0" - domelementtype "1" + path-type "^4.0.0" dot-prop@^3.0.0: version "3.0.0" @@ -2063,11 +889,6 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -editions@^1.1.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/editions/-/editions-1.3.4.tgz#3662cb592347c3168eb8e498a0ff73271d67f50b" - integrity sha512-gzao+mxnYDzIysXKMQi/+M1mjy/rjestjg6OPoYTtI+3Izp23oiGZitsl9lPDPiTGXbcSIk1iJWhliSaglxnUg== - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -2125,21 +946,11 @@ engine.io@~3.4.0: engine.io-parser "~2.2.0" ws "^7.1.2" -ensure-posix-path@^1.0.0, ensure-posix-path@^1.0.2: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ensure-posix-path/-/ensure-posix-path-1.1.1.tgz#3c62bdb19fa4681544289edb2b382adc029179ce" - integrity sha512-VWU0/zXzVbeJNXvME/5EmLuEj2TauvoaTz6aFYK1Z92JCBlDlZ3Gu0tuGR42kpW1754ywTs+QB0g5TP0oj9Zaw== - -entities@^1.1.1, entities@~1.1.1: +entities@~1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== -entities@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" - integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== - error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -2164,120 +975,16 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -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" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - escape-string-regexp@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== -eslint-scope@^3.7.1: - version "3.7.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" - integrity sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-visitor-keys@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" - integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== - -eslint@^4.0.0: - version "4.19.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" - integrity sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ== - 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" - integrity sha512-yAcIQxtmMiB/jL32dzEp2enBeidsB7xWPLNiw3IIkpVds1P+h7qF9YwJq1yUNzp2OKXgAprs4F61ih66UsoD1A== - dependencies: - acorn "^5.5.0" - acorn-jsx "^3.0.0" - -esprima-fb@~15001.1001.0-dev-harmony-fb: - version "15001.1001.0-dev-harmony-fb" - resolved "https://registry.yarnpkg.com/esprima-fb/-/esprima-fb-15001.1001.0-dev-harmony-fb.tgz#43beb57ec26e8cf237d3dd8b33e42533577f2659" - integrity sha1-Q761fsJujPI3092LM+QlM1d/Jlk= - -esprima@^2.6.0: - version "2.7.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" - integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE= - esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esprima@~3.1.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" - integrity sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM= - -esquery@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.1.tgz#406c51658b1f5991a5f9b62b1dc25b00e3e5c708" - integrity sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA== - 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" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== - dependencies: - estraverse "^4.1.0" - -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - estree-walker@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" @@ -2288,11 +995,6 @@ estree-walker@^1.0.1: resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== -esutils@^2.0.0, esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - etag@~1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" @@ -2308,13 +1010,6 @@ events-to-array@^1.0.1: resolved "https://registry.yarnpkg.com/events-to-array/-/events-to-array-1.1.2.tgz#2d41f563e1fe400ed4962fe1a4d5c6a7539df7f6" integrity sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y= -exec-sh@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.2.2.tgz#2a5e7ffcbd7d0ba2755bdecb16e5a427dfbdec36" - integrity sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw== - dependencies: - merge "^1.2.0" - execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -2328,50 +1023,6 @@ execa@^1.0.0: signal-exit "^3.0.0" strip-eof "^1.0.0" -exists-stat@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/exists-stat/-/exists-stat-1.0.0.tgz#0660e3525a2e89d9e446129440c272edfa24b529" - integrity sha1-BmDjUlouidnkRhKUQMJy7foktSk= - -exists-sync@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/exists-sync/-/exists-sync-0.0.4.tgz#9744c2c428cc03b01060db454d4b12f0ef3c8879" - integrity sha1-l0TCxCjMA7AQYNtFTUsS8O88iHk= - -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - integrity sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s= - 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" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - 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" - integrity sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc= - dependencies: - fill-range "^2.1.0" - -expand-tilde@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-1.2.2.tgz#0b81eba897e5a3d31d1c3d102f8f01441e559449" - integrity sha1-C4HrqJflo9MdHD0QL48BRB5VlEk= - dependencies: - os-homedir "^1.0.1" - express@^4.10.7: version "4.17.1" resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" @@ -2408,56 +1059,11 @@ express@^4.10.7: utils-merge "1.0.1" vary "~1.1.2" -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - 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" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -external-editor@^2.0.4: - version "2.2.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" - integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== - dependencies: - chardet "^0.4.0" - iconv-lite "^0.4.17" - tmp "^0.0.33" - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - integrity sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE= - 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" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - 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" - extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -2468,101 +1074,43 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= -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" - integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ= - fast-deep-equal@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4" integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA== +fast-glob@^3.0.3: + version "3.2.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.2.tgz#ade1a9d91148965d4bf7c51f72e1ca662d32e63d" + integrity sha512-UDV82o4uQyljznxwMxyVRJgZZt3O5wENYojjzbaGEGZgeOxkLFf+V4cnUD+krzb2F72E18RhamkMZ7AdeggF7A== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.0" + merge2 "^1.3.0" + micromatch "^4.0.2" + picomatch "^2.2.1" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= - -fast-ordered-set@^1.0.0, fast-ordered-set@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/fast-ordered-set/-/fast-ordered-set-1.0.3.tgz#3fbb36634f7be79e4f7edbdb4a357dee25d184eb" - integrity sha1-P7s2Y097555PftvbSjV97iXRhOs= - dependencies: - blank-object "^1.0.1" - -fast-sourcemap-concat@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/fast-sourcemap-concat/-/fast-sourcemap-concat-1.4.0.tgz#122c330d4a2afaff16ad143bc9674b87cd76c8ad" - integrity sha512-x90Wlx/2C83lfyg7h4oguTZN4MyaVfaiUSJQNpU+YEA0Odf9u659Opo44b0LfoVg9G/bOE++GdID/dkyja+XcA== - dependencies: - chalk "^2.0.0" - fs-extra "^5.0.0" - heimdalljs-logger "^0.1.9" - memory-streams "^0.1.3" - mkdirp "^0.5.0" - source-map "^0.4.2" - source-map-url "^0.3.0" - sourcemap-validator "^1.1.0" - -fb-watchman@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" - integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== - dependencies: - bser "2.1.1" - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI= - 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" - integrity sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E= - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -filename-regex@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" - integrity sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY= - -fill-range@^2.1.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.4.tgz#eb1e773abb056dcd8df2bfdf6af59b8b3a936565" - integrity sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q== +fastq@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2" + integrity sha512-jmxqQ3Z/nXoeyDmWAzF9kH1aGZSis6e/SbfPmJpUnyZ0ogr6iscHQaml4wsEepEWSdtmpy+eVXmCRIMpxaXqOA== dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^3.0.0" - repeat-element "^1.1.2" - repeat-string "^1.5.2" + reusify "^1.0.0" -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" + to-regex-range "^5.0.1" -finalhandler@1.1.2, finalhandler@~1.1.2: +finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== @@ -2575,11 +1123,6 @@ finalhandler@1.1.2, finalhandler@~1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-index@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/find-index/-/find-index-1.1.1.tgz#4b221f8d46b7f8bea33d8faed953f3ca7a081cbc" - integrity sha512-XYKutXMrIK99YMUPf91KX5QVJoG31/OsgftD6YoTPAObfQIxM4ziA9f0J1AsqKhJmo+IeaIPP0CFopTD4bdUBw== - find-up@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" @@ -2595,26 +1138,6 @@ find-up@^2.0.0: dependencies: locate-path "^2.0.0" -findup-sync@0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-0.4.3.tgz#40043929e7bc60adf0b7f4827c4c6e75a0deca12" - integrity sha1-QAQ5Kee8YK3wt/SCfExudaDeyhI= - dependencies: - detect-file "^0.1.0" - is-glob "^2.0.1" - micromatch "^2.3.7" - resolve-dir "^0.1.0" - -findup-sync@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-1.0.0.tgz#6f7e4b57b6ee3a4037b4414eaedea3f58f71e0ec" - integrity sha1-b35LV7buOkA3tEFOrt6j9Y9x4Ow= - dependencies: - detect-file "^0.1.0" - is-glob "^2.0.1" - micromatch "^2.3.7" - resolve-dir "^0.1.0" - fireworm@^0.7.0: version "0.7.1" resolved "https://registry.yarnpkg.com/fireworm/-/fireworm-0.7.1.tgz#ccf20f7941f108883fcddb99383dbe6e1861c758" @@ -2626,16 +1149,6 @@ fireworm@^0.7.0: lodash.flatten "^3.0.2" minimatch "^3.0.2" -flat-cache@^1.2.1: - version "1.3.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.4.tgz#2c2ef77525cc2929007dfffa1dd314aa9c9dee6f" - integrity sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg== - dependencies: - circular-json "^0.3.1" - graceful-fs "^4.1.2" - rimraf "~2.6.2" - write "^0.2.1" - follow-redirects@^1.0.0: version "1.10.0" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb" @@ -2643,18 +1156,6 @@ follow-redirects@^1.0.0: dependencies: debug "^3.0.0" -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" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -for-own@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= - dependencies: - for-in "^1.0.1" - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -2674,13 +1175,6 @@ forwarded@~0.1.2: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -2691,73 +1185,20 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== -fs-exists-sync@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/fs-exists-sync/-/fs-exists-sync-0.1.0.tgz#982d6893af918e72d08dec9e8673ff2b5a8d6add" - integrity sha1-mC1ok6+RjnLQjeyehnP/K1qNat0= - -fs-extra@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" - integrity sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg== +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" - integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== - dependencies: - graceful-fs "^4.1.2" + graceful-fs "^4.2.0" jsonfile "^4.0.0" universalify "^0.1.0" -fs-readdir-recursive@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-0.1.2.tgz#315b4fb8c1ca5b8c47defef319d073dad3568059" - integrity sha1-MVtPuMHKW4xH3v7zGdBz2tNWgFk= - -fs-tree-diff@^0.5.2, fs-tree-diff@^0.5.3, fs-tree-diff@^0.5.4, fs-tree-diff@^0.5.6, fs-tree-diff@^0.5.7: - version "0.5.9" - resolved "https://registry.yarnpkg.com/fs-tree-diff/-/fs-tree-diff-0.5.9.tgz#a4ec6182c2f5bd80b9b83c8e23e4522e6f5fd946" - integrity sha512-872G8ax0kHh01m9n/2KDzgYwouKza0Ad9iFltBpNykvROvf2AGtoOzPJgGx125aolGPER3JuC7uZFrQ7bG1AZw== - dependencies: - heimdalljs-logger "^0.1.7" - object-assign "^4.1.0" - path-posix "^1.0.0" - symlink-or-copy "^1.1.8" - -fs-updater@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/fs-updater/-/fs-updater-1.0.4.tgz#2329980f99ae9176e9a0e84f7637538a182ce63b" - integrity sha512-0pJX4mJF/qLsNEwTct8CdnnRdagfb+LmjRPJ8sO+nCnAZLW0cTmz4rTgU25n+RvTuWSITiLKrGVJceJPBIPlKg== - dependencies: - can-symlink "^1.0.0" - clean-up-path "^1.0.0" - heimdalljs "^0.2.5" - heimdalljs-logger "^0.1.9" - rimraf "^2.6.2" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@^1.0.0: - version "1.2.11" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.11.tgz#67bf57f4758f02ede88fb2a1712fef4d15358be3" - integrity sha512-+ux3lx6peh0BpvY0JebGyZoiR4D+oYzdPZMKJwkZ+sFkNJzpL7tXc/wehS49gUAxg3tmMHPHZkA8JU2rhhgDHw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - -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" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - gauge@~2.7.3: version "2.7.4" resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" @@ -2795,11 +1236,6 @@ get-stream@^4.0.0: dependencies: pump "^3.0.0" -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" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - getpass@^0.1.1: version "0.1.7" resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" @@ -2841,33 +1277,14 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - integrity sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q= - 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" - integrity sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg= - dependencies: - is-glob "^2.0.0" - -glob@^5.0.10, glob@^5.0.15: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - integrity sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= +glob-parent@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" + integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" + is-glob "^4.0.1" -glob@^7.0.4, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: +glob@^7.0.4, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -2879,55 +1296,31 @@ glob@^7.0.4, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -global-modules@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-0.2.3.tgz#ea5a3bed42c6d6ce995a4f8a1269b5dae223828d" - integrity sha1-6lo77ULG1s6ZWk+KEmm12uIjgo0= +globby@10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22" + integrity sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A== dependencies: - global-prefix "^0.1.4" - is-windows "^0.2.0" + "@types/glob" "^7.1.1" + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.0.3" + glob "^7.1.3" + ignore "^5.1.1" + merge2 "^1.2.3" + slash "^3.0.0" -global-prefix@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-0.1.5.tgz#8d3bc6b8da3ca8112a160d8d496ff0462bfef78f" - integrity sha1-jTvGuNo8qBEqFg2NSW/wRiv+948= - dependencies: - homedir-polyfill "^1.0.0" - ini "^1.3.4" - is-windows "^0.2.0" - which "^1.2.12" - -globals@^11.0.1: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^6.4.0: - version "6.4.1" - resolved "https://registry.yarnpkg.com/globals/-/globals-6.4.1.tgz#8498032b3b6d1cc81eebc5f79690d8fe29fabf4f" - integrity sha1-hJgDKzttHMge68X3lpDY/in6v08= - -globals@^9.18.0: - version "9.18.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" - integrity sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ== - -graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.4, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0: version "4.2.3" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" - integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= - growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -handlebars@^4.0.2, handlebars@^4.0.4: +handlebars@^4.0.2: version "4.7.2" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.2.tgz#01127b3840156a0927058779482031afe0e730d7" integrity sha512-4PwqDL2laXtTWZghzzCtunQUTLbo31pcCJrd/B/9JP8XbhVzpS5ZXuKqlOzsd1rtcaLo4KqAn8nl8mkknS4MHw== @@ -2948,136 +1341,31 @@ har-validator@~5.1.0: resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.3.tgz#1ef89ebd3e4996557675eed9893110dc350fa080" integrity sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g== dependencies: - ajv "^6.5.5" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-binary2@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" - integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== - dependencies: - isarray "2.0.1" - -has-cors@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" - integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - 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" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - 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" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -hash-for-dep@^1.0.2, hash-for-dep@^1.2.3: - version "1.5.1" - resolved "https://registry.yarnpkg.com/hash-for-dep/-/hash-for-dep-1.5.1.tgz#497754b39bee2f1c4ade4521bfd2af0a7c1196e3" - integrity sha512-/dQ/A2cl7FBPI2pO0CANkvuuVi/IFS5oTyJ0PsOb6jW6WbVW1js5qJXMJTNbWHXBIPdFTWFbabjB+mE0d+gelw== - dependencies: - broccoli-kitchen-sink-helpers "^0.3.1" - heimdalljs "^0.2.3" - heimdalljs-logger "^0.1.7" - path-root "^0.1.1" - resolve "^1.10.0" - resolve-package-path "^1.0.11" - -heimdalljs-logger@^0.1.7, heimdalljs-logger@^0.1.9: - version "0.1.10" - resolved "https://registry.yarnpkg.com/heimdalljs-logger/-/heimdalljs-logger-0.1.10.tgz#90cad58aabb1590a3c7e640ddc6a4cd3a43faaf7" - integrity sha512-pO++cJbhIufVI/fmB/u2Yty3KJD0TqNPecehFae0/eps0hkZ3b4Zc/PezUMOpYuHFQbA7FxHZxa305EhmjLj4g== - dependencies: - debug "^2.2.0" - heimdalljs "^0.2.6" - -heimdalljs@^0.2.0, heimdalljs@^0.2.1, heimdalljs@^0.2.3, heimdalljs@^0.2.5, heimdalljs@^0.2.6: - version "0.2.6" - resolved "https://registry.yarnpkg.com/heimdalljs/-/heimdalljs-0.2.6.tgz#b0eebabc412813aeb9542f9cc622cb58dbdcd9fe" - integrity sha512-o9bd30+5vLBvBtzCPwwGqpry2+n0Hi6H1+qwt6y+0kwRHGGF8TFIhJPmnuM0xO97zaKrDZMwO/V56fAnn8m/tA== - dependencies: - rsvp "~3.2.1" + ajv "^6.5.5" + har-schema "^2.0.0" -home-or-tmp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-1.0.0.tgz#4b9f1e40800c3e50c6c27f781676afcce71f3985" - integrity sha1-S58eQIAMPlDGwn94FnavzOcfOYU= +has-binary2@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" + integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== dependencies: - os-tmpdir "^1.0.1" - user-home "^1.1.1" + isarray "2.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" - integrity sha1-42w/LSyufXRqhX440Y1fMqeILbg= - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.1" +has-cors@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" + integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= -homedir-polyfill@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk= hosted-git-info@^2.1.4: version "2.8.5" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c" integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg== -htmlparser2@^3.10.0: - version "3.10.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" - integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== - dependencies: - domelementtype "^1.3.1" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^3.1.1" - http-errors@1.7.2: version "1.7.2" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" @@ -3126,7 +1414,7 @@ https-proxy-agent@^3.0.0: agent-base "^4.3.0" debug "^3.1.0" -iconv-lite@0.4.24, iconv-lite@^0.4.17, iconv-lite@^0.4.5: +iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -3138,15 +1426,10 @@ ieee754@^1.1.4: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== -ignore@^3.3.3: - version "3.3.10" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" - integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= +ignore@^5.1.1: + version "5.1.4" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" + integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== indent-string@^2.1.0: version "2.1.0" @@ -3173,7 +1456,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -3183,139 +1466,25 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= -ini@^1.3.2, ini@^1.3.4: +ini@^1.3.2: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== -inquirer@^3.0.6: - version "3.3.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" - integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== - 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" - -invariant@^2.2.2: - version "2.2.4" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6" - integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== - 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" - integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= - ipaddr.js@1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65" integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA== -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" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - 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" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.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" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -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" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= - dependencies: - binary-extensions "^1.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -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" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - 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" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - 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" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-dotfile@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - integrity sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE= - -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" - integrity sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ= - 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" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - 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" - integrity sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA= +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= is-finite@^1.0.0: version "1.0.2" @@ -3336,43 +1505,22 @@ is-fullwidth-code-point@^2.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= -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" - integrity sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM= - dependencies: - is-extglob "^1.0.0" - -is-integer@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-integer/-/is-integer-1.0.7.tgz#6bde81aacddf78b659b6629d629cadc51a886d5c" - integrity sha1-a96Bqs3feLZZtmKdYpytxRqIbVw= +is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: - is-finite "^1.0.0" + is-extglob "^2.1.1" is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= -is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - integrity sha1-Afy7s5NGOlSPL0ZszhbezknbkI8= - 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" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - 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" - integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-obj@^1.0.0: version "1.0.1" @@ -3384,27 +1532,12 @@ is-plain-obj@^1.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= -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" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== +is-plain-object@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-3.0.0.tgz#47bfc5da1b5d50d64110806c199359482e75a928" + integrity sha512-tZIpofR+P05k8Aocp7UI/2UTa9lTJSebCXpFFoR9aibpokDj/uXBsJ8luUu0tTVYKkMU6URDUuOfJZ7koewXvg== 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" - integrity sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q= - -is-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU= - -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o= + isobject "^4.0.0" is-reference@^1.1.2: version "1.1.4" @@ -3413,11 +1546,6 @@ is-reference@^1.1.2: dependencies: "@types/estree" "0.0.39" -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - is-stream@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" @@ -3452,98 +1580,47 @@ is-utf8@^0.2.0: resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= -is-windows@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-0.2.0.tgz#de1aa6d63ea29dd248737b69f1ff8b8002d2108c" - integrity sha1-3hqm1j6indJIc3tp8f+LgALSEIw= - -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= - -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - isarray@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= + isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - 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" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +isobject@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" + integrity sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA== isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= -istextorbinary@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/istextorbinary/-/istextorbinary-2.1.0.tgz#dbed2a6f51be2f7475b68f89465811141b758874" - integrity sha1-2+0qb1G+L3R1to+JRlgRFBt1iHQ= - dependencies: - binaryextensions "1 || 2" - editions "^1.1.1" - textextensions "1 || 2" - jquery@^3.4.1: version "3.4.1" resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.4.1.tgz#714f1f8d9dde4bdfa55764ba37ef214630d80ef2" integrity sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw== -js-reporters@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/js-reporters/-/js-reporters-1.2.0.tgz#7cf2cb698196684790350d0c4ca07f4aed9ec17e" - integrity sha1-fPLLaYGWaEeQNQ0MTKB/Su2ewX4= - js-reporters@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/js-reporters/-/js-reporters-1.2.1.tgz#f88c608e324a3373a95bcc45ad305e5c979c459b" integrity sha1-+IxgjjJKM3OpW8xFrTBeXJecRZs= -js-tokens@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-1.0.1.tgz#cc435a5c8b94ad15acb7983140fc80182c89aeae" - integrity sha1-zENaXIuUrRWst5gxQPyAGCyJrq4= - -"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" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls= - -js-yaml@^3.2.5, js-yaml@^3.2.7, js-yaml@^3.3.0, js-yaml@^3.9.1: +js-yaml@^3.2.5, js-yaml@^3.2.7: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -3583,36 +1660,11 @@ jsdoc@^3.5.4: taffydb "2.6.2" underscore "~1.9.1" -jsesc@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" - integrity sha1-RsP+yMGJKxKwgz25vHYiF226s0s= - -jsesc@^2.5.0: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@~0.3.x: - version "0.3.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.3.0.tgz#1bf5ee63b4539fe2e26d0c1e99c240b97a457972" - integrity sha1-G/XuY7RTn+LibQwemcJAuXpFeXI= - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= - json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== -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" - integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A= - 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" @@ -3623,33 +1675,11 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= -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" - integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= - -json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - integrity sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8= - dependencies: - jsonify "~0.0.0" - json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json5@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.4.0.tgz#054352e4c4c80c86c0923877d449de176a732c8d" - integrity sha1-BUNS5MTIDIbAkjh31EneF2pzLI0= - -json5@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" - integrity sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE= - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -3657,11 +1687,6 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= - jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" @@ -3677,30 +1702,6 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -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" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - 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" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - 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" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - klaw@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/klaw/-/klaw-3.0.0.tgz#b11bec9cf2492f06756d6e809ab73a2910259146" @@ -3708,11 +1709,6 @@ klaw@^3.0.0: dependencies: graceful-fs "^4.1.9" -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= - lazystream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" @@ -3720,26 +1716,6 @@ lazystream@^1.0.0: dependencies: readable-stream "^2.0.5" -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= - dependencies: - invert-kv "^1.0.0" - -leven@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/leven/-/leven-1.0.2.tgz#9144b6eebca5f1d0680169f1a6770dcea60b75c3" - integrity sha1-kUS27ryl8dBoAWnxpncNzqYLdcM= - -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4= - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - linkify-it@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf" @@ -3747,15 +1723,6 @@ linkify-it@^2.0.0: dependencies: uc.micro "^1.0.1" -livereload@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/livereload/-/livereload-0.7.0.tgz#38238dd155ffb251191697f737b6b13f471da115" - integrity sha512-PHnIGczQEvmCctDvRTWylA+1wSwE0/eFm+LkNhlmlAFus/aCRlVE97UOLOf6TUGLmZyfg7z7twG37ZiOgNJAyQ== - dependencies: - chokidar "^1.7.0" - opts ">= 1.2.0" - ws "^1.1.5" - load-json-file@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" @@ -3777,11 +1744,6 @@ load-json-file@^4.0.0: pify "^3.0.0" strip-bom "^3.0.0" -loader.js@^4.2.3: - version "4.7.0" - resolved "https://registry.yarnpkg.com/loader.js/-/loader.js-4.7.0.tgz#a1a52902001c83631efde9688b8ab3799325ef1f" - integrity sha512-9M2KvGT6duzGMgkOcTkWb+PR/Q2Oe54df/tLgHGVmFpAmtqJ553xJh6N63iFYI2yjo2PeJXbS5skHi/QpJq4vA== - locate-path@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" @@ -3840,11 +1802,6 @@ lodash.defaults@^4.2.0: resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw= -lodash.defaultsdeep@^4.6.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/lodash.defaultsdeep/-/lodash.defaultsdeep-4.6.1.tgz#512e9bd721d272d94e3d3a63653fa17516741ca6" - integrity sha512-3j8wdDzYuWO3lM3Reg03MuQR957t287Rpcxp1njpEa8oDrikb+FwGdW3n+FELh/A6qib6yPit0j/pv9G/yeAqA== - lodash.difference@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c" @@ -3868,11 +1825,6 @@ lodash.flatten@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= -lodash.foreach@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" - integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM= - lodash.isarguments@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" @@ -3888,17 +1840,7 @@ lodash.isplainobject@^4.0.6: resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash.omit@^4.1.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.omit/-/lodash.omit-4.5.0.tgz#6eb19ae5a1ee1dd9df0b969e66ce0b7fa30b5e60" - integrity sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA= - -lodash.template@^4.0.2, lodash.template@^4.5.0: +lodash.template@^4.0.2: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab" integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A== @@ -3918,38 +1860,16 @@ lodash.union@^4.6.0: resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg= -lodash.uniq@^4.2.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= - lodash.uniqby@^4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz#d99c07a669e9e6d24e1362dfe266c67616af1302" integrity sha1-2ZwHpmnp5tJOE2Lf4mbGdhavEwI= -lodash@^3.10.0, lodash@^3.9.3: - version "3.10.1" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" - integrity sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y= - -lodash@^4.0.0, lodash@^4.16.6, lodash@^4.17.14, lodash@^4.17.4, lodash@^4.2.1, lodash@^4.3.0: +lodash@^4.0.0, lodash@^4.16.6, lodash@^4.17.14, lodash@^4.2.1: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - integrity sha1-MKCy2jj3N3DoKUoNIuZiXtd9AJc= - -loose-envify@^1.0.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - 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" @@ -3958,14 +1878,6 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -lru-cache@^4.0.1: - version "4.1.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" - integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - magic-string@^0.25.2: version "0.25.6" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.6.tgz#5586387d1242f919c6d223579cc938bf1420795e" @@ -3973,18 +1885,6 @@ magic-string@^0.25.2: dependencies: sourcemap-codec "^1.4.4" -makeerror@1.0.x: - version "1.0.11" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" - integrity sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw= - dependencies: - tmpl "1.0.x" - -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - map-obj@^1.0.0, map-obj@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" @@ -3995,13 +1895,6 @@ map-obj@^2.0.0: resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-2.0.0.tgz#a65cd29087a92598b8791257a523e021222ac1f9" integrity sha1-plzSkIepJZi4eRJXpSPgISIqwfk= -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - markdown-it-anchor@^5.0.2: version "5.2.5" resolved "https://registry.yarnpkg.com/markdown-it-anchor/-/markdown-it-anchor-5.2.5.tgz#dbf13cfcdbffd16a510984f1263e1d479a47d27a" @@ -4023,30 +1916,6 @@ marked@^0.7.0: resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e" integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg== -matcher-collection@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/matcher-collection/-/matcher-collection-1.1.2.tgz#1076f506f10ca85897b53d14ef54f90a5c426838" - integrity sha512-YQ/teqaOIIfUHedRam08PB3NK7Mjct6BvzRnJmpGDm8uFXpNr1sbY4yuflI5JcEs6COpYA0FpRQhSDBf1tT95g== - dependencies: - minimatch "^3.0.2" - -math-random@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.4.tgz#5dd6943c938548267016d4e34f057583080c514c" - integrity sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A== - -md5-hex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/md5-hex/-/md5-hex-2.0.0.tgz#d0588e9f1c74954492ecd24ac0ac6ce997d92e33" - integrity sha1-0FiOnxx0lUSS7NJKwKxs6ZfZLjM= - dependencies: - md5-o-matic "^0.1.1" - -md5-o-matic@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/md5-o-matic/-/md5-o-matic-0.1.1.tgz#822bccd65e117c514fab176b25945d54100a03c3" - integrity sha1-givM1l4RfFFPqxdrJZRdVBAKA8M= - mdurl@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" @@ -4057,13 +1926,6 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= -memory-streams@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/memory-streams/-/memory-streams-0.1.3.tgz#d9b0017b4b87f1d92f55f2745c9caacb1dc93ceb" - integrity sha512-qVQ/CjkMyMInPaaRMrwWNDvf6boRZXaT/DbQeMYcCWuXPEBf1v8qChOc9OlEVQp2uOvRXa1Qu30fLmKhY6NipA== - dependencies: - readable-stream "~1.0.2" - meow@^3.3.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -4100,73 +1962,23 @@ merge-descriptors@1.0.1: resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= -merge-trees@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-trees/-/merge-trees-1.0.1.tgz#ccbe674569787f9def17fd46e6525f5700bbd23e" - integrity sha1-zL5nRWl4f53vF/1G5lJfVwC70j4= - dependencies: - can-symlink "^1.0.0" - fs-tree-diff "^0.5.4" - heimdalljs "^0.2.1" - heimdalljs-logger "^0.1.7" - rimraf "^2.4.3" - symlink-or-copy "^1.0.0" - -merge-trees@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-trees/-/merge-trees-2.0.0.tgz#a560d796e566c5d9b2c40472a2967cca48d85161" - integrity sha512-5xBbmqYBalWqmhYm51XlohhkmVOua3VAUrrWh8t9iOkaLpS6ifqm/UVuUjQCeDVJ9Vx3g2l6ihfkbLSTeKsHbw== - dependencies: - fs-updater "^1.0.4" - heimdalljs "^0.2.5" - -merge@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" - integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== +merge2@^1.2.3, merge2@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.3.0.tgz#5b366ee83b2f1582c48f87e47cf1a9352103ca81" + integrity sha512-2j4DAdlBOkiSZIsaXk4mTE3sRS02yBHAtfy127xRV3bQUFqXkjHCHLW6Scv7DwNRbIWNHH8zpnz9zMaKXIdvYw== methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micromatch@^2.1.5, micromatch@^2.3.7: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - integrity sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU= - 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: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - 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" +micromatch@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" + integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== + dependencies: + braces "^3.0.1" + picomatch "^2.0.5" mime-db@1.43.0, "mime-db@>= 1.43.0 < 2": version "1.43.0" @@ -4180,7 +1992,7 @@ mime-types@^2.1.12, mime-types@~2.1.19, mime-types@~2.1.24: dependencies: mime-db "1.43.0" -mime@1.6.0, mime@^1.2.11: +mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== @@ -4190,25 +2002,13 @@ mime@>=2.0.3: resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== - -"minimatch@2 || 3", minimatch@3.0.4, minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: +minimatch@3.0.4, minimatch@^3.0.2, minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" -minimatch@^2.0.3: - version "2.0.10" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.10.tgz#8d087c39c6b38c001b97fca7ce6d0e1e80afbac7" - integrity sha1-jQh8OcazjAAbl/ynzm0OHoCvusc= - dependencies: - brace-expansion "^1.0.0" - minimist-options@^3.0.1: version "3.0.2" resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-3.0.2.tgz#fba4c8191339e13ecf4d61beb03f070103f3d954" @@ -4222,7 +2022,7 @@ minimist@0.0.8: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3: +minimist@^1.1.3: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= @@ -4240,26 +2040,13 @@ minipass@^2.2.0: safe-buffer "^5.1.2" yallist "^3.0.0" -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@^0.5.0, mkdirp@^0.5.1: +mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= dependencies: minimist "0.0.8" -mktemp@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/mktemp/-/mktemp-0.4.0.tgz#6d0515611c8a8c84e484aa2000129b98e981ff0b" - integrity sha1-bQUVYRyKjITkhKogABKbmOmB/ws= - mobiledoc-dom-renderer@0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/mobiledoc-dom-renderer/-/mobiledoc-dom-renderer-0.7.0.tgz#53ab5f14dd612b16f03513390e5cbcc2b89f6979" @@ -4295,38 +2082,6 @@ mustache@^3.0.0: resolved "https://registry.yarnpkg.com/mustache/-/mustache-3.2.1.tgz#89e78a9d207d78f2799b1e95764a25bf71a28322" integrity sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA== -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= - -nan@^2.12.1: - version "2.14.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" - integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - 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" - integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= - negotiator@0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" @@ -4342,18 +2097,6 @@ nice-try@^1.0.4: resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -node-html-light@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/node-html-light/-/node-html-light-1.1.0.tgz#667f8cb4fac96cf13a7951af64e7dabbd7d56337" - integrity sha512-FF1szX5zPxDgL3MRC/VkfhbUdvgAV8d6HnfzdbrqvSp4GKhPUqdxbySzCeXLGgPttswZzG9j4/GFzSGuKhTI0Q== - dependencies: - htmlparser2 "^3.10.0" - -node-int64@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs= - node-notifier@^5.0.1: version "5.4.3" resolved "https://registry.yarnpkg.com/node-notifier/-/node-notifier-5.4.3.tgz#cb72daf94c93904098e28b9c590fd866e464bd50" @@ -4380,13 +2123,6 @@ normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package- semver "2 || 3 || 4 || 5" validate-npm-package-license "^3.0.1" -normalize-path@^2.0.0, normalize-path@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -4419,7 +2155,7 @@ oauth-sign@~0.9.0: resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== -object-assign@4.1.1, object-assign@^4.0.1, object-assign@^4.1.0: +object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= @@ -4429,37 +2165,6 @@ object-component@0.0.3: resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291" integrity sha1-8MaapQ78lbhmwYb0AKM3acsvEpE= -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - 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" - integrity sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo= - 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" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -4479,13 +2184,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ= - dependencies: - mimic-fn "^1.0.0" - opener@1: version "1.5.1" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" @@ -4499,54 +2197,11 @@ optimist@^0.6.1: minimist "~0.0.1" wordwrap "~0.0.2" -optionator@^0.8.2: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -options@>=0.0.5: - version "0.0.6" - resolved "https://registry.yarnpkg.com/options/-/options-0.0.6.tgz#ec22d312806bb53e731773e7cdaefcf1c643128f" - integrity sha1-7CLTEoBrtT5zF3Pnza788cZDEo8= - -"opts@>= 1.2.0": - version "1.2.7" - resolved "https://registry.yarnpkg.com/opts/-/opts-1.2.7.tgz#4de4721d592c96901dae623a438c988e9ea7779f" - integrity sha512-hwZhzGGG/GQ7igxAVFOEun2N4fWul31qE9nfBdCnZGQCB5+L7tN9xZ+94B4aUpLOJx/of3zZs5XsuubayQYQjA== - -os-homedir@^1.0.0, os-homedir@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= - dependencies: - lcid "^1.0.0" - -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: +os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -output-file-sync@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/output-file-sync/-/output-file-sync-1.1.2.tgz#d0a33eefe61a205facb90092e826598d5245ce76" - integrity sha1-0KM+7+YaIF+suQCS6CZZjVJFznY= - dependencies: - graceful-fs "^4.1.4" - mkdirp "^0.5.1" - object-assign "^4.1.0" - p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" @@ -4576,16 +2231,6 @@ parse-github-repo-url@^1.3.0: resolved "https://registry.yarnpkg.com/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50" integrity sha1-nn2LslKmy2ukJZUGC3v23z28H1A= -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - integrity sha1-ssN2z7EfNVE7rdFz7wu246OIORw= - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" @@ -4599,12 +2244,7 @@ parse-json@^4.0.0: integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= dependencies: error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= + json-parse-better-errors "^1.0.1" parseqs@0.0.5: version "0.0.5" @@ -4625,16 +2265,6 @@ parseurl@~1.3.3: resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -path-exists@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-1.0.0.tgz#d5a8998eb71ef37a74c34eb0d9eba6e878eea081" - integrity sha1-1aiZjrce83p0w06w2eum6HjuoIE= - path-exists@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" @@ -4647,43 +2277,21 @@ path-exists@^3.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= -path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: +path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= -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" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - 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" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-parse@^1.0.5, path-parse@^1.0.6: +path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== -path-posix@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/path-posix/-/path-posix-1.0.0.tgz#06b26113f56beab042545a23bfa88003ccac260f" - integrity sha1-BrJhE/Vr6rBCVFojv6iAA8ysJg8= - -path-root-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" - integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= - -path-root@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" - integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= - dependencies: - path-root-regex "^0.1.0" - path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -4705,11 +2313,21 @@ path-type@^3.0.0: dependencies: pify "^3.0.0" +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= +picomatch@^2.0.5, picomatch@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" + integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== + pify@^2.0.0, pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -4732,53 +2350,16 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -pluralize@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" - integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== - -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" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ= - -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - integrity sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks= - printf@^0.5.1: version "0.5.2" resolved "https://registry.yarnpkg.com/printf/-/printf-0.5.2.tgz#8546e01a1f647b1dff510ae92bdc92beb8c9b2f9" integrity sha512-Hn0UuWqTRd94HiCJoiCNGZTnSyXJdIF3t4/4I293hezIzyH4pQ3ai4TlH/SmRCiMvR5aNMxSYWshjQWWW6J8MQ== -private@^0.1.6, private@^0.1.8, private@~0.1.5: - version "0.1.8" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" - integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== - process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== -progress@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -promise-map-series@^0.2.1: - version "0.2.3" - resolved "https://registry.yarnpkg.com/promise-map-series/-/promise-map-series-0.2.3.tgz#c2d377afc93253f6bd03dbb77755eb88ab20a847" - integrity sha1-wtN3r8kyU/a9A9u3d1XriKsgqEc= - dependencies: - rsvp "^3.0.14" - proxy-addr@~2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" @@ -4787,11 +2368,6 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" ipaddr.js "1.9.0" -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= - psl@^1.1.24: version "1.7.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.7.0.tgz#f1c4c47a8ef97167dea5d6bbf4816d736e884a3c" @@ -4815,7 +2391,7 @@ punycode@^2.1.0: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -q@^1.1.2, q@^1.4.1, q@^1.5.1: +q@^1.4.1, q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= @@ -4835,15 +2411,6 @@ quick-lru@^1.0.0: resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-1.1.0.tgz#4360b17c61136ad38078397ff11416e186dcfbb8" integrity sha1-Q2CxfGETatOAeDl/8RQW4Ybc+7g= -quick-temp@^0.1.3, quick-temp@^0.1.5: - version "0.1.8" - resolved "https://registry.yarnpkg.com/quick-temp/-/quick-temp-0.1.8.tgz#bab02a242ab8fb0dd758a3c9776b32f9a5d94408" - integrity sha1-urAqJCq4+w3XWKPJd2sy+aXZRAg= - dependencies: - mktemp "~0.4.0" - rimraf "^2.5.4" - underscore.string "~3.3.4" - qunit@^2.9.3: version "2.9.3" resolved "https://registry.yarnpkg.com/qunit/-/qunit-2.9.3.tgz#9522a088e76f0782f70a45db92f2fd14db311bcc" @@ -4855,28 +2422,6 @@ qunit@^2.9.3: node-watch "0.6.1" resolve "1.9.0" -qunitjs@^2.1.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/qunitjs/-/qunitjs-2.4.1.tgz#88aba055a9e2ec3dbebfaad02471b2cb002c530b" - integrity sha512-by/2zYvsNdS6Q6Ev6UJ3qJK+OYVlTzWlQ4afaeYMhVh1dd2K3N1ZZKCrCm3WSWPnz5ELMT8WyJRcVy5PXT2y+Q== - dependencies: - chokidar "1.6.1" - commander "2.9.0" - exists-stat "1.0.0" - findup-sync "0.4.3" - js-reporters "1.2.0" - resolve "1.3.2" - walk-sync "0.3.1" - -randomatic@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-3.1.1.tgz#b776efc59375984e36c537b2f51a1f0aff0da1ed" - integrity sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw== - dependencies: - is-number "^4.0.0" - kind-of "^6.0.0" - math-random "^1.0.1" - range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" @@ -4926,7 +2471,7 @@ read-pkg@^3.0.0: normalize-package-data "^2.3.2" path-type "^3.0.0" -readable-stream@^2.0.0, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.2.2, readable-stream@^2.3.6, readable-stream@~2.3.6: +readable-stream@^2.0.0, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -4948,55 +2493,6 @@ readable-stream@^3.0.1, readable-stream@^3.1.1, readable-stream@^3.4.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@~1.0.2: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= - 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.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== - dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - -recast@0.10.33: - version "0.10.33" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.33.tgz#942808f7aa016f1fa7142c461d7e5704aaa8d697" - integrity sha1-lCgI96oBbx+nFCxGHX5XBKqo1pc= - dependencies: - ast-types "0.8.12" - esprima-fb "~15001.1001.0-dev-harmony-fb" - private "~0.1.5" - source-map "~0.5.0" - -recast@^0.10.10: - version "0.10.43" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.43.tgz#b95d50f6d60761a5f6252e15d80678168491ce7f" - integrity sha1-uV1Q9tYHYaX2JS4V2AZ4FoSRzn8= - dependencies: - ast-types "0.8.15" - esprima-fb "~15001.1001.0-dev-harmony-fb" - private "~0.1.5" - source-map "~0.5.0" - -recast@^0.11.17: - version "0.11.23" - resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3" - integrity sha1-RR/TAEqx5N+bTktmN2sqIZEkYtM= - dependencies: - ast-types "0.9.6" - esprima "~3.1.0" - private "~0.1.5" - source-map "~0.5.0" - redent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" @@ -5013,93 +2509,6 @@ redent@^2.0.0: indent-string "^3.0.0" strip-indent "^2.0.0" -regenerate@^1.2.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.0.tgz#4a856ec4b56e4077c557589cae85e7a4c8869a11" - integrity sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg== - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - -regenerator@0.8.40: - version "0.8.40" - resolved "https://registry.yarnpkg.com/regenerator/-/regenerator-0.8.40.tgz#a0e457c58ebdbae575c9f8cd75127e93756435d8" - integrity sha1-oORXxY69uuV1yfjNdRJ+k3VkNdg= - dependencies: - commoner "~0.10.3" - defs "~1.1.0" - esprima-fb "~15001.1001.0-dev-harmony-fb" - private "~0.1.5" - recast "0.10.33" - through "~2.3.8" - -regex-cache@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" - integrity sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ== - 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" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - 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" - integrity sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw== - -regexpu@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/regexpu/-/regexpu-1.3.0.tgz#e534dc991a9e5846050c98de6d7dd4a55c9ea16d" - integrity sha1-5TTcmRqeWEYFDJjebX3UpVyeoW0= - dependencies: - esprima "^2.6.0" - recast "^0.10.10" - 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" - integrity sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc= - -regjsparser@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" - integrity sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw= - dependencies: - jsesc "~0.5.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" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -repeat-element@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" - integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== - -repeat-string@^1.5.2, repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -repeating@^1.1.0, repeating@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-1.1.3.tgz#3d4114218877537494f97f77f9785fab810fa4ac" - integrity sha1-PUEUIYh3U3SU+X93+Xhfq4EPpKw= - dependencies: - is-finite "^1.0.0" - repeating@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" @@ -5133,14 +2542,6 @@ request@2.88.0, request@^2.81.0: tunnel-agent "^0.6.0" uuid "^3.3.2" -require-uncached@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - integrity sha1-Tg1W1slmL9MeQwEcS5WqSZVUIdM= - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -5153,39 +2554,6 @@ requizzle@^0.2.3: dependencies: lodash "^4.17.14" -resolve-dir@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-0.1.1.tgz#b219259a5602fac5c5c496ad894a6e8cc430261e" - integrity sha1-shklmlYC+sXFxJatiUpujMQwJh4= - dependencies: - expand-tilde "^1.2.2" - global-modules "^0.2.3" - -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" - integrity sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY= - -resolve-package-path@^1.0.11: - version "1.2.7" - resolved "https://registry.yarnpkg.com/resolve-package-path/-/resolve-package-path-1.2.7.tgz#2a7bc37ad96865e239330e3102c31322847e652e" - integrity sha512-fVEKHGeK85bGbVFuwO9o1aU0n3vqQGrezPc51JGu9UTXpFQfWq5qCeKxyaRUSvephs+06c5j5rPq/dzHGEo8+Q== - dependencies: - path-root "^0.1.1" - resolve "^1.10.0" - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -resolve@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.2.tgz#1f0442c9e0cbb8136e87b9305f932f46c7f28235" - integrity sha1-HwRCyeDLuBNuh7kwX5MvRsfygjU= - dependencies: - path-parse "^1.0.5" - resolve@1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.9.0.tgz#a14c6fdfa8f92a7df1d996cb7105fa744658ea06" @@ -5193,51 +2561,35 @@ resolve@1.9.0: dependencies: path-parse "^1.0.6" -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.11.1: +resolve@^1.10.0, resolve@^1.11.0, resolve@^1.11.1: version "1.15.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.0.tgz#1b7ca96073ebb52e741ffd799f6b39ea462c67f5" integrity sha512-+hTmAldEGE80U2wJJDC1lebb5jWqvTYAfm3YZ1ckk1gBr0MnCqUKlwK1e+anaFljIl+F5tR5IoZcm4ZDA1zMQw== dependencies: path-parse "^1.0.6" -resolve@~0.6.1: - version "0.6.3" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-0.6.3.tgz#dd957982e7e736debdf53b58a4dd91754575dd46" - integrity sha1-3ZV5gufnNt699TtYpN2RdUV13UY= - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368= - 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" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" - integrity sha1-YTObci/mo1FWiSENJOFMlhSGE+8= - dependencies: - align-text "^0.1.1" +reusify@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== -rimraf@^2.3.4, rimraf@^2.4.3, rimraf@^2.4.4, rimraf@^2.5.3, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2: +rimraf@^2.4.4, rimraf@^2.5.4: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" -rimraf@~2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== +rollup-plugin-copy@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-copy/-/rollup-plugin-copy-3.3.0.tgz#5ba230047f86b9f703a29288f242948a5580e7b9" + integrity sha512-euDjCUSBXZa06nqnwCNADbkAcYDfzwowfZQkto9K/TFhiH+QG7I4PUsEMwM9tDgomGWJc//z7KLW8t+tZwxADA== dependencies: - glob "^7.1.3" + "@types/fs-extra" "^8.0.1" + colorette "^1.1.0" + fs-extra "^8.1.0" + globby "10.0.1" + is-plain-object "^3.0.0" rollup-plugin-glob-import@^0.4.5: version "0.4.5" @@ -5271,39 +2623,10 @@ rollup@^1.31.0: "@types/node" "*" acorn "^7.1.0" -rsvp@^3.0.14, rsvp@^3.0.18, rsvp@^3.5.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.6.2.tgz#2e96491599a96cde1b515d5674a8f7a91452926a" - integrity sha512-OfWGQTb9vnwRjwtA2QwpG2ICclHC3pgXZO5xt8H2EfgDquO0qVdSb5T88L4qJVAEugbS56pAuV4XZM58UX8ulw== - -rsvp@^4.8.2: - version "4.8.5" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" - integrity sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA== - -rsvp@~3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-3.2.1.tgz#07cb4a5df25add9e826ebc67dcc9fd89db27d84a" - integrity sha1-B8tKXfJa3Z6Cbrxn3Mn9idsn2Eo= - -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= - dependencies: - is-promise "^2.1.0" - -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" - integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= - 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" - integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= +run-parallel@^1.1.9: + version "1.1.9" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" + integrity sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q== safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" @@ -5315,31 +2638,11 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.0.tgz#b74daec49b1148f88c64b68d49b1e815c1f2f519" integrity sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg== -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - "safer-buffer@>= 2.1.2 < 3", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== -sane@^1.1.1, sane@^1.4.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/sane/-/sane-1.7.0.tgz#b3579bccb45c94cf20355cc81124990dfd346e30" - integrity sha1-s1ebzLRclM8gNVzIESSZDf00bjA= - dependencies: - anymatch "^1.3.0" - exec-sh "^0.2.0" - fb-watchman "^2.0.0" - minimatch "^3.0.2" - minimist "^1.1.1" - walker "~1.0.5" - watch "~0.10.0" - sauce-connect-launcher@^1.2.2: version "1.3.1" resolved "https://registry.yarnpkg.com/sauce-connect-launcher/-/sauce-connect-launcher-1.3.1.tgz#31137f57b0f7176e1c0525b7fb09c6da746647cf" @@ -5362,7 +2665,7 @@ saucie@^3.3.2: sauce-connect-launcher "^1.2.2" wd "^1.2.0" -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0: +"semver@2 || 3 || 4 || 5", semver@^5.5.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -5401,16 +2704,6 @@ set-blocking@~2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - setprototypeof@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" @@ -5433,68 +2726,16 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= -simple-fmt@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/simple-fmt/-/simple-fmt-0.1.0.tgz#191bf566a59e6530482cb25ab53b4a8dc85c3a6b" - integrity sha1-GRv1ZqWeZTBILLJatTtKjchcOms= - -simple-is@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/simple-is/-/simple-is-0.2.0.tgz#2abb75aade39deb5cc815ce10e6191164850baf0" - integrity sha1-Krt1qt453rXMgVzhDmGRFkhQuvA= - -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - integrity sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU= - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== -slice-ansi@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" - integrity sha512-POqxBK6Lb3q6s047D/XsDVNPnF9Dl8JSaqe9h9lURl0OdNqy/ujDrOiIHtsqXMGbWWTIomRzAMaTyawAU//Reg== - 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" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - 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" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - 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" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - 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" - socket.io-adapter@~1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" @@ -5550,87 +2791,16 @@ socket.io@^2.1.0: socket.io-client "2.3.0" socket.io-parser "~3.4.0" -source-map-resolve@^0.5.0: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - 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.2.10: - version "0.2.10" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.2.10.tgz#ea5a3900a1c1cb25096a0ae8cc5c2b4b10ded3dc" - integrity sha1-6lo5AKHByyUJagrozFwrSxDe09w= - dependencies: - source-map "0.1.32" - -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" - integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== - dependencies: - source-map "^0.5.6" - -source-map-url@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.3.0.tgz#7ecaf13b57bcd09da8a40c5d269db33799d4aaf9" - integrity sha1-fsrxO1e80J2opAxdJp2zN5nUqvk= - -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" - integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= - -source-map@0.1.32: - version "0.1.32" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.32.tgz#c8b6c167797ba4740a8ea33252162ff08591b266" - integrity sha1-yLbBZ3l7pHQKjqMyUhYv8IWRsmY= - dependencies: - amdefine ">=0.0.4" - -source-map@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" - integrity sha1-66T12pwNyZneaAMti092FzZSA2s= - dependencies: - amdefine ">=0.0.4" - -source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.0: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - 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" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -source-map@~0.1.x: - version "0.1.43" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.1.43.tgz#c24bc146ca517c1471f5dacbe2571b2b7f9e3346" - integrity sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y= - dependencies: - amdefine ">=0.0.4" - sourcemap-codec@^1.4.4: version "1.4.8" resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== -sourcemap-validator@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/sourcemap-validator/-/sourcemap-validator-1.1.1.tgz#3d7d8a399ccab09c1fedc510d65436e25b1c386b" - integrity sha512-pq6y03Vs6HUaKo9bE0aLoksAcpeOo9HZd7I8pI6O480W/zxNZ9U32GfzgtPP0Pgc/K1JHna569nAbOk3X8/Qtw== - dependencies: - jsesc "~0.3.x" - lodash.foreach "^4.5.0" - lodash.template "^4.5.0" - source-map "~0.1.x" - spawn-args@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/spawn-args/-/spawn-args-0.2.0.tgz#fb7d0bd1d70fd4316bd9e3dec389e65f9d6361bb" @@ -5662,13 +2832,6 @@ spdx-license-ids@^3.0.0: resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654" integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q== -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" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - split2@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/split2/-/split2-2.2.0.tgz#186b2575bcf83e85b7d18465756238ee4ee42493" @@ -5683,11 +2846,6 @@ split@^1.0.0: dependencies: through "2" -sprintf-js@^1.0.3: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" - integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -5708,19 +2866,6 @@ sshpk@^1.7.0: safer-buffer "^2.0.2" tweetnacl "~0.14.0" -stable@~0.1.3: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" @@ -5735,7 +2880,7 @@ string-width@^1.0.1: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.1.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2": version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw== @@ -5750,11 +2895,6 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= - string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -5762,16 +2902,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringmap@~0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/stringmap/-/stringmap-0.2.2.tgz#556c137b258f942b8776f5b2ef582aa069d7d1b1" - integrity sha1-VWwTeyWPlCuHdvWy71gqoGnX0bE= - -stringset@~0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/stringset/-/stringset-0.2.1.tgz#ef259c4e349344377fcd1c913dd2e848c9c042b5" - integrity sha1-7yWcTjSTRDd/zRyRPdLoSMnAQrU= - 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" @@ -5820,45 +2950,11 @@ strip-json-comments@^3.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== -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" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - styled_string@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/styled_string/-/styled_string-0.0.1.tgz#d22782bd81295459bc4f1df18c4bad8e94dd124a" integrity sha1-0ieCvYEpVFm8Tx3xjEutjpTdEko= -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -symlink-or-copy@^1.0.0, symlink-or-copy@^1.0.1, symlink-or-copy@^1.1.8: - version "1.3.1" - resolved "https://registry.yarnpkg.com/symlink-or-copy/-/symlink-or-copy-1.3.1.tgz#9506dd64d8e98fa21dcbf4018d1eab23e77f71fe" - integrity sha512-0K91MEXFpBUaywiwSSkmKjnGcasG/rVBXFLJz5DrgGabpYD6N+3yZrfD6uUIfpuTu65DZLHi7N8CizHc07BPZA== - -table@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" - integrity sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA== - 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" - taffydb@2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268" @@ -5932,16 +3028,6 @@ text-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== -text-table@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -"textextensions@1 || 2": - version "2.6.0" - resolved "https://registry.yarnpkg.com/textextensions/-/textextensions-2.6.0.tgz#d7e4ab13fe54e32e08873be40d51b74229b00fc4" - integrity sha512-49WtAWS+tcsy93dRt6P0P3AMD2m5PvXRhuEA0kaXos5ZLlujtYmpmFsB+QvWUSxE1ZsstmYXfQ7L40+EcQgpAQ== - through2@^2.0.0, through2@^2.0.2: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -5950,71 +3036,29 @@ through2@^2.0.0, through2@^2.0.2: readable-stream "~2.3.6" xtend "~4.0.1" -through@2, "through@>=2.2.7 <3", through@^2.3.6, through@~2.3.8: +through@2, "through@>=2.2.7 <3": version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -tmp@0.0.28: - version "0.0.28" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.28.tgz#172735b7f614ea7af39664fa84cf0de4e515d120" - integrity sha1-Fyc1t/YU6nrzlmT6hM8N5OUV0SA= - dependencies: - os-tmpdir "~1.0.1" - -tmp@0.0.31: - version "0.0.31" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7" - integrity sha1-jzirlDjhcxXl29izZX6L+yd65Kc= - dependencies: - os-tmpdir "~1.0.1" - -tmp@0.0.33, tmp@^0.0.33: +tmp@0.0.33: version "0.0.33" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== dependencies: os-tmpdir "~1.0.2" -tmpl@1.0.x: - version "1.0.4" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" - integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= - to-array@0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= -to-fast-properties@^1.0.0, 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" - integrity sha1-uDVx+k2MJbguIxsG46MFXeTKGkc= - -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" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - 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" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - 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" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" + is-number "^7.0.0" toidentifier@1.0.0: version "1.0.0" @@ -6029,17 +3073,6 @@ tough-cookie@~2.4.3: psl "^1.1.24" punycode "^1.4.1" -tree-sync@^1.2.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/tree-sync/-/tree-sync-1.4.0.tgz#314598d13abaf752547d9335b8f95d9a137100d6" - integrity sha512-YvYllqh3qrR5TAYZZTXdspnIhlKAYezPYw11ntmweoceu4VK+keN356phHRIIo1d+RDmLpHZrUlmxga2gc9kSQ== - dependencies: - debug "^2.2.0" - fs-tree-diff "^0.5.6" - mkdirp "^0.5.1" - quick-temp "^0.1.5" - walk-sync "^0.3.3" - trim-newlines@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" @@ -6055,21 +3088,6 @@ trim-off-newlines@^1.0.0: resolved "https://registry.yarnpkg.com/trim-off-newlines/-/trim-off-newlines-1.0.1.tgz#9f9ba9d9efa8764c387698bcbfeb2c848f11adb3" integrity sha1-n5up2e+odkw4dpi8v+sshI8RrbM= -trim-right@^1.0.0, trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" - integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= - -try-resolve@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/try-resolve/-/try-resolve-1.0.1.tgz#cfde6fabd72d63e5797cfaab873abbe8e700e912" - integrity sha1-z95vq9ctY+V5fPqrhzq76OcA6RI= - -tryor@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/tryor/-/tryor-0.1.2.tgz#8145e4ca7caff40acde3ccf946e8b8bb75b4172b" - integrity sha1-gUXkynyv9ArN48z5Rui4u3W0Fys= - tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -6082,13 +3100,6 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0: resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I= - dependencies: - prelude-ls "~1.1.2" - type-is@~1.6.17, type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -6097,11 +3108,6 @@ type-is@~1.6.17, type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - uc.micro@^1.0.1, uc.micro@^1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" @@ -6115,34 +3121,11 @@ uglify-js@^3.1.4: commander "~2.20.3" source-map "~0.6.1" -ultron@1.0.x: - version "1.0.2" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.0.2.tgz#ace116ab557cd197386a4e88f4685378c8b2e4fa" - integrity sha1-rOEWq1V80Zc4ak6I9GhTeMiy5Po= - -underscore.string@^3.2.2, underscore.string@~3.3.4: - version "3.3.5" - resolved "https://registry.yarnpkg.com/underscore.string/-/underscore.string-3.3.5.tgz#fc2ad255b8bd309e239cbc5816fd23a9b7ea4023" - integrity sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg== - dependencies: - sprintf-js "^1.0.3" - util-deprecate "^1.0.2" - underscore@>=1.8.3, underscore@~1.9.1: version "1.9.2" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.2.tgz#0c8d6f536d6f378a5af264a72f7bec50feb7cf2f" integrity sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ== -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -6153,14 +3136,6 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -6168,27 +3143,7 @@ uri-js@^4.2.2: 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" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -user-home@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/user-home/-/user-home-1.1.1.tgz#2b5be23a32b63a7c9deb8d0f28d485724a3df190" - integrity sha1-K1viOjK2Onyd640PKNSFcko98ZA= - -username-sync@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/username-sync/-/username-sync-1.0.2.tgz#0a3697909fb7b5768d29e2921f573acfdd427592" - integrity sha512-ayNkOJdoNSGNDBE46Nkc+l6IXmeugbzahZLSMkwvgRWv5y5ZqNY2IrzcgmkR4z32sj1W3tM3TuTUMqkqBzO+RA== - -util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: +util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= @@ -6235,34 +3190,6 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -walk-sync@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-0.3.1.tgz#558a16aeac8c0db59c028b73c66f397684ece465" - integrity sha1-VYoWrqyMDbWcAotzxm85doTs5GU= - dependencies: - ensure-posix-path "^1.0.0" - matcher-collection "^1.0.0" - -walk-sync@^0.3.1, walk-sync@^0.3.2, walk-sync@^0.3.3: - version "0.3.4" - resolved "https://registry.yarnpkg.com/walk-sync/-/walk-sync-0.3.4.tgz#cf78486cc567d3a96b5b2237c6108017a5ffb9a4" - integrity sha512-ttGcuHA/OBnN2pcM6johpYlEms7XpO5/fyKIr48541xXedan4roO8cS1Q2S/zbbjGH/BarYDAMeS2Mi9HE5Tig== - dependencies: - ensure-posix-path "^1.0.0" - matcher-collection "^1.0.0" - -walker@~1.0.5: - version "1.0.7" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb" - integrity sha1-L3+bj9ENZ3JisYqITijRlhjgKPs= - dependencies: - makeerror "1.0.x" - -watch@~0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/watch/-/watch-0.10.0.tgz#77798b2da0f9910d595f1ace5b0c2258521f21dc" - integrity sha1-d3mLLaD5kQ1ZXxrOWwwiWFIfIdw= - wd@^1.2.0: version "1.12.1" resolved "https://registry.yarnpkg.com/wd/-/wd-1.12.1.tgz#067eb3674db00eeb9e506701f9314657c44d5a89" @@ -6276,7 +3203,7 @@ wd@^1.2.0: request "2.88.0" vargs "^0.1.0" -which@^1.2.12, which@^1.2.9, which@^1.3.0: +which@^1.2.9, which@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -6290,53 +3217,16 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" -window-size@^0.1.2: - version "0.1.4" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" - integrity sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY= - -word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - integrity sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8= - wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= -workerpool@^2.3.0: - version "2.3.3" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-2.3.3.tgz#49a70089bd55e890d68cc836a19419451d7c81d7" - integrity sha512-L1ovlYHp6UObYqElXXpbd214GgbEKDED0d3sj7pRdFXjNkb2+un/AUcCkceHizO0IVI6SOGGncrcjozruCkRgA== - dependencies: - object-assign "4.1.1" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - integrity sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c= - dependencies: - mkdirp "^0.5.1" - -ws@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-1.1.5.tgz#cbd9e6e75e09fc5d2c90015f21f0c40875e0dd51" - integrity sha512-o3KqipXNUdS7wpQzBHSe180lBGO60SoK0yVo3CYJgb2MkobuWuBX6dhkYP5ORCLd55y+SaflMOV5fqAB53ux4w== - dependencies: - options ">=0.0.5" - ultron "1.0.x" - ws@^7.1.2: version "7.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.1.tgz#03ed52423cd744084b2cf42ed197c8b65a936b8e" @@ -6369,33 +3259,11 @@ xtend@~4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -y18n@^3.2.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= - yallist@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== -yargs@~3.27.0: - version "3.27.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.27.0.tgz#21205469316e939131d59f2da0c6d7f98221ea40" - integrity sha1-ISBUaTFuk5Ex1Z8toMbX+YIh6kA= - dependencies: - camelcase "^1.2.1" - cliui "^2.1.0" - decamelize "^1.0.0" - os-locale "^1.4.0" - window-size "^0.1.2" - y18n "^3.2.0" - yeast@0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"