From 2a3472a0fa016d9d395195ab2fb9c021272745f3 Mon Sep 17 00:00:00 2001 From: Zahra Jabini Date: Wed, 27 May 2020 09:28:14 -0400 Subject: [PATCH 1/3] =?UTF-8?q?Adds=20prettier=20+=20applies=20formatting?= =?UTF-8?q?=20=F0=9F=A7=9F=E2=80=8D=E2=99=80=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 10 +- src/css/mobiledoc-kit.css | 28 +- src/js/cards/image.js | 14 +- src/js/editor/edit-history.js | 110 ++- src/js/editor/edit-state.js | 111 +-- src/js/editor/editor.js | 705 +++++++++--------- src/js/editor/event-manager.js | 273 +++---- src/js/editor/key-commands.js | 312 ++++---- src/js/editor/mutation-handler.js | 123 ++- src/js/editor/post.js | 828 ++++++++++----------- src/js/editor/post/post-inserter.js | 230 +++--- src/js/editor/selection-change-observer.js | 74 +- src/js/editor/selection-manager.js | 24 +- src/js/editor/text-input-handler.js | 77 +- src/js/editor/text-input-handlers.js | 62 +- src/js/editor/ui.js | 56 +- src/js/index.js | 18 +- src/js/models/_attributable.js | 28 +- src/js/models/_markerable.js | 251 +++---- src/js/models/_section.js | 88 ++- src/js/models/atom-node.js | 69 +- src/js/models/atom.js | 93 ++- src/js/models/card-node.js | 88 +-- src/js/models/card.js | 44 +- src/js/models/image.js | 14 +- src/js/models/lifecycle-callbacks.js | 46 +- src/js/models/list-item.js | 40 +- src/js/models/list-section.js | 73 +- src/js/models/marker.js | 108 ++- src/js/models/markup-section.js | 66 +- src/js/models/markup.js | 47 +- src/js/models/post-node-builder.js | 143 ++-- src/js/models/post.js | 184 +++-- src/js/models/render-node.js | 93 +-- src/js/models/render-tree.js | 40 +- src/js/models/types.js | 18 +- src/js/parsers/dom.js | 323 ++++---- src/js/parsers/html.js | 20 +- src/js/parsers/mobiledoc/0-2.js | 102 +-- src/js/parsers/mobiledoc/0-3-1.js | 130 ++-- src/js/parsers/mobiledoc/0-3-2.js | 140 ++-- src/js/parsers/mobiledoc/0-3.js | 130 ++-- src/js/parsers/mobiledoc/index.js | 37 +- src/js/parsers/section.js | 394 +++++----- src/js/parsers/text.js | 93 ++- src/js/renderers/editor-dom.js | 579 +++++++------- src/js/renderers/mobiledoc/0-2.js | 124 +-- src/js/renderers/mobiledoc/0-3-1.js | 162 ++-- src/js/renderers/mobiledoc/0-3-2.js | 166 ++--- src/js/renderers/mobiledoc/0-3.js | 162 ++-- src/js/renderers/mobiledoc/index.js | 26 +- src/js/utils/array-utils.js | 125 ++-- src/js/utils/assert.js | 6 +- src/js/utils/browser.js | 8 +- src/js/utils/characters.js | 6 +- src/js/utils/compiler.js | 30 +- src/js/utils/copy.js | 12 +- src/js/utils/cursor.js | 170 ++--- src/js/utils/cursor/position.js | 340 ++++----- src/js/utils/cursor/range.js | 126 ++-- src/js/utils/deprecate.js | 4 +- src/js/utils/dom-utils.js | 71 +- src/js/utils/element-map.js | 23 +- src/js/utils/element-utils.js | 86 +-- src/js/utils/environment.js | 6 +- src/js/utils/fixed-queue.js | 18 +- src/js/utils/key.js | 217 +++--- src/js/utils/keycodes.js | 72 +- src/js/utils/keys.js | 42 +- src/js/utils/linked-item.js | 4 +- src/js/utils/linked-list.js | 202 ++--- src/js/utils/log-manager.js | 28 +- src/js/utils/markuperable.js | 50 +- src/js/utils/merge.js | 14 +- src/js/utils/mixin.js | 14 +- src/js/utils/mobiledoc-error.js | 20 +- src/js/utils/object-utils.js | 10 +- src/js/utils/parse-utils.js | 95 +-- src/js/utils/placeholder-image-src.js | 5 +- src/js/utils/selection-utils.js | 186 ++--- src/js/utils/set.js | 14 +- src/js/utils/string-utils.js | 14 +- src/js/utils/to-range.js | 14 +- src/js/version.js | 2 +- src/js/views/tooltip.js | 102 ++- src/js/views/view.js | 50 +- yarn.lock | 5 + 87 files changed, 4664 insertions(+), 4703 deletions(-) diff --git a/package.json b/package.json index 171225041..ee7b40a37 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "scripts": { "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", + "test": "yarn format && yarn build && testem ci -f testem.js", + "format": "prettier src --write", "build": "rollup -c", "build:docs": "jsdoc -c ./.jsdoc", "build:website": "yarn build:docs && yarn build && ./bin/build-website.sh", @@ -52,6 +53,7 @@ "conventional-changelog-cli": "^2.0.34", "jquery": "^3.5.1", "jsdoc": "^3.6.4", + "prettier": "^2.0.5", "qunit": "^2.10.0", "rollup": "^2.10.3", "rollup-plugin-copy": "^3.3.0", @@ -60,6 +62,12 @@ "saucie": "^3.3.3", "testem": "^3.1.0" }, + "prettier": { + "arrowParens": "avoid", + "printWidth": 120, + "semi": false, + "singleQuote": true + }, "volta": { "node": "12.14.1", "yarn": "1.22.1" diff --git a/src/css/mobiledoc-kit.css b/src/css/mobiledoc-kit.css index ae0cb66d3..1fcdf89be 100644 --- a/src/css/mobiledoc-kit.css +++ b/src/css/mobiledoc-kit.css @@ -96,26 +96,34 @@ */ @-webkit-keyframes fade-in { - 0% { opacity: 0; } - 100% { opacity: 1; } + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } } @keyframes fade-in { - 0% { opacity: 0; } - 100% { opacity: 1; } + 0% { + opacity: 0; + } + 100% { + opacity: 1; + } } .__mobiledoc-tooltip { - font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + 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); + background-color: rgba(43, 43, 43, 0.9); border-radius: 3px; line-height: 1em; padding: 0.7em 0.9em; - color: #FFF; + color: #fff; -webkit-animation: fade-in 0.2s; - animation: fade-in 0.2s; + animation: fade-in 0.2s; } .__mobiledoc-tooltip:before { @@ -126,7 +134,7 @@ height: 0; border-left: 5px solid transparent; border-right: 5px solid transparent; - border-bottom: 5px solid rgba(43,43,43,0.9); + border-bottom: 5px solid rgba(43, 43, 43, 0.9); top: -5px; margin-left: -5px; } @@ -142,7 +150,7 @@ } .__mobiledoc-tooltip a { - color: #FFF; + color: #fff; text-decoration: none; } diff --git a/src/js/cards/image.js b/src/js/cards/image.js index 5b30ea2fc..0d10202db 100644 --- a/src/js/cards/image.js +++ b/src/js/cards/image.js @@ -1,12 +1,12 @@ -import placeholderImageSrc from 'mobiledoc-kit/utils/placeholder-image-src'; +import placeholderImageSrc from 'mobiledoc-kit/utils/placeholder-image-src' export default { name: 'image', type: 'dom', - render({payload}) { - let img = document.createElement('img'); - img.src = payload.src || placeholderImageSrc; - return img; - } -}; + render({ payload }) { + let img = document.createElement('img') + img.src = payload.src || placeholderImageSrc + return img + }, +} diff --git a/src/js/editor/edit-history.js b/src/js/editor/edit-history.js index 3d24cf9ec..07a076c5a 100644 --- a/src/js/editor/edit-history.js +++ b/src/js/editor/edit-history.js @@ -1,126 +1,122 @@ -import mobiledocParsers from 'mobiledoc-kit/parsers/mobiledoc'; -import FixedQueue from 'mobiledoc-kit/utils/fixed-queue'; +import mobiledocParsers from 'mobiledoc-kit/parsers/mobiledoc' +import FixedQueue from 'mobiledoc-kit/utils/fixed-queue' function findLeafSectionAtIndex(post, index) { - let section; + let section post.walkAllLeafSections((_section, _index) => { if (index === _index) { - section = _section; + section = _section } - }); - return section; + }) + return section } export class Snapshot { - constructor(takenAt, editor, editAction=null) { - this.mobiledoc = editor.serialize(); - this.editor = editor; - this.editAction = editAction; - this.takenAt = takenAt; + constructor(takenAt, editor, editAction = null) { + this.mobiledoc = editor.serialize() + this.editor = editor + this.editAction = editAction + this.takenAt = takenAt - this.snapshotRange(); + this.snapshotRange() } snapshotRange() { - let { range, cursor } = this.editor; + let { range, cursor } = this.editor if (cursor.hasCursor() && !range.isBlank) { - let { head, tail } = range; + let { head, tail } = range this.range = { head: [head.leafSectionIndex, head.offset], - tail: [tail.leafSectionIndex, tail.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); + 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); + head = headSection.toPosition(headOffset) + tail = tailSection.toPosition(tailOffset) - return head.toRange(tail); + return head.toRange(tail) } } groupsWith(groupingTimeout, editAction, takenAt) { - return ( - editAction !== null && - this.editAction === editAction && - this.takenAt + groupingTimeout > takenAt - ); + return editAction !== null && this.editAction === editAction && this.takenAt + groupingTimeout > takenAt } } export default class EditHistory { constructor(editor, queueLength, groupingTimeout) { - this.editor = editor; - this._undoStack = new FixedQueue(queueLength); - this._redoStack = new FixedQueue(queueLength); + this.editor = editor + this._undoStack = new FixedQueue(queueLength) + this._redoStack = new FixedQueue(queueLength) - this._pendingSnapshot = null; - this._groupingTimeout = groupingTimeout; + this._pendingSnapshot = null + this._groupingTimeout = groupingTimeout } snapshot() { // update the current snapshot with the range read from DOM if (this._pendingSnapshot) { - this._pendingSnapshot.snapshotRange(); + this._pendingSnapshot.snapshotRange() } } - storeSnapshot(editAction=null) { - let now = Date.now(); + storeSnapshot(editAction = null) { + let now = Date.now() // store pending snapshot - let pendingSnapshot = this._pendingSnapshot; + let pendingSnapshot = this._pendingSnapshot if (pendingSnapshot) { if (!pendingSnapshot.groupsWith(this._groupingTimeout, editAction, now)) { - this._undoStack.push(pendingSnapshot); + this._undoStack.push(pendingSnapshot) } - this._redoStack.clear(); + this._redoStack.clear() } // take new pending snapshot to store next time `storeSnapshot` is called - this._pendingSnapshot = new Snapshot(now, this.editor, editAction); + this._pendingSnapshot = new Snapshot(now, this.editor, editAction) } stepBackward(postEditor) { // Throw away the pending snapshot - this._pendingSnapshot = null; + this._pendingSnapshot = null - let snapshot = this._undoStack.pop(); + let snapshot = this._undoStack.pop() if (snapshot) { - this._redoStack.push(new Snapshot(Date.now(), this.editor)); - this._restoreFromSnapshot(snapshot, postEditor); + this._redoStack.push(new Snapshot(Date.now(), this.editor)) + this._restoreFromSnapshot(snapshot, postEditor) } } stepForward(postEditor) { - let snapshot = this._redoStack.pop(); + let snapshot = this._redoStack.pop() if (snapshot) { - this._undoStack.push(new Snapshot(Date.now(), this.editor)); - this._restoreFromSnapshot(snapshot, postEditor); + this._undoStack.push(new Snapshot(Date.now(), this.editor)) + this._restoreFromSnapshot(snapshot, postEditor) } - postEditor.cancelSnapshot(); + postEditor.cancelSnapshot() } _restoreFromSnapshot(snapshot, postEditor) { - let { mobiledoc } = snapshot; - let { editor } = this; - let { builder, post } = editor; - let restoredPost = mobiledocParsers.parse(builder, mobiledoc); + let { mobiledoc } = snapshot + let { editor } = this + let { builder, post } = editor + let restoredPost = mobiledocParsers.parse(builder, mobiledoc) - postEditor.removeAllSections(); - postEditor.migrateSectionsFromPost(restoredPost); + postEditor.removeAllSections() + postEditor.migrateSectionsFromPost(restoredPost) // resurrect snapshotted range if it exists - let newRange = snapshot.getRange(post); + let newRange = snapshot.getRange(post) if (newRange) { - postEditor.setRange(newRange); + postEditor.setRange(newRange) } } } diff --git a/src/js/editor/edit-state.js b/src/js/editor/edit-state.js index be4d0f9c6..0d68d0d5a 100644 --- a/src/js/editor/edit-state.js +++ b/src/js/editor/edit-state.js @@ -1,9 +1,5 @@ -import { - contains, - isArrayEqual, - objectToSortedKVArray -} from 'mobiledoc-kit/utils/array-utils'; -import Range from 'mobiledoc-kit/utils/cursor/range'; +import { contains, isArrayEqual, objectToSortedKVArray } from 'mobiledoc-kit/utils/array-utils' +import Range from 'mobiledoc-kit/utils/cursor/range' /** * Used by {@link Editor} to manage its current state (cursor, active markups @@ -12,36 +8,39 @@ import Range from 'mobiledoc-kit/utils/cursor/range'; */ class EditState { constructor(editor) { - this.editor = editor; + this.editor = editor let defaultState = { range: Range.blankRange(), activeMarkups: [], activeSections: [], activeSectionTagNames: [], - activeSectionAttributes: {} - }; + activeSectionAttributes: {}, + } - this.prevState = this.state = defaultState; + this.prevState = this.state = defaultState } updateRange(newRange) { - this.prevState = this.state; - this.state = this._readState(newRange); + this.prevState = this.state + this.state = this._readState(newRange) } destroy() { - this.editor = null; - this.prevState = this.state = null; + this.editor = null + this.prevState = this.state = null } /** * @return {Boolean} */ rangeDidChange() { - let { state: { range } , prevState: {range: prevRange} } = this; + let { + state: { range }, + prevState: { range: prevRange }, + } = this - return !prevRange.isEqual(range); + return !prevRange.isEqual(range) } /** @@ -49,39 +48,43 @@ class EditState { * 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))); + 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 this.state.range } /** * @return {Section[]} */ get activeSections() { - return this.state.activeSections; + return this.state.activeSections } - /** * @return {Object} */ get activeSectionAttributes() { - return this.state.activeSectionAttributes; + return this.state.activeSectionAttributes } /** * @return {Markup[]} */ get activeMarkups() { - return this.state.activeMarkups; + return this.state.activeMarkups } /** @@ -92,67 +95,71 @@ class EditState { */ toggleMarkupState(markup) { if (contains(this.activeMarkups, markup)) { - this._removeActiveMarkup(markup); + this._removeActiveMarkup(markup) } else { - this._addActiveMarkup(markup); + this._addActiveMarkup(markup) } } _readState(range) { let state = { range, - activeMarkups: this._readActiveMarkups(range), - activeSections: this._readActiveSections(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; + 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; + let { head, tail } = range + let { + editor: { post }, + } = this if (range.isBlank) { - return []; + return [] } else { - return post.sections.readRange(head.section, tail.section); + return post.sections.readRange(head.section, tail.section) } } _readActiveMarkups(range) { - let { editor: { post } } = this; - return post.markupsInRange(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; + 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] || []; + let camelizedAttrName = attrName.replace(/^data-md-/, '') + let attrValue = attributes[attrName] + sectionAttributes[camelizedAttrName] = sectionAttributes[camelizedAttrName] || [] if (!contains(sectionAttributes[camelizedAttrName], attrValue)) { - sectionAttributes[camelizedAttrName].push(attrValue); + sectionAttributes[camelizedAttrName].push(attrValue) } - }); - return sectionAttributes; - }, {}); + }) + return sectionAttributes + }, {}) } _removeActiveMarkup(markup) { - let index = this.state.activeMarkups.indexOf(markup); - this.state.activeMarkups.splice(index, 1); + let index = this.state.activeMarkups.indexOf(markup) + this.state.activeMarkups.splice(index, 1) } _addActiveMarkup(markup) { - this.state.activeMarkups.push(markup); + this.state.activeMarkups.push(markup) } } -export default EditState; +export default EditState diff --git a/src/js/editor/editor.js b/src/js/editor/editor.js index 54a699924..34150a20e 100644 --- a/src/js/editor/editor.js +++ b/src/js/editor/editor.js @@ -1,43 +1,41 @@ -import Tooltip, { DEFAULT_TOOLTIP_PLUGIN } from '../views/tooltip'; -import PostEditor from './post'; -import ImageCard from '../cards/image'; -import { DIRECTION } from '../utils/key'; -import mobiledocParsers from '../parsers/mobiledoc'; -import HTMLParser from '../parsers/html'; -import DOMParser from '../parsers/dom'; -import Renderer from 'mobiledoc-kit/renderers/editor-dom'; -import RenderTree from 'mobiledoc-kit/models/render-tree'; -import mobiledocRenderers from '../renderers/mobiledoc'; -import { MOBILEDOC_VERSION } from 'mobiledoc-kit/renderers/mobiledoc'; -import { mergeWithOptions } from '../utils/merge'; -import { normalizeTagName, clearChildNodes, serializeHTML } from '../utils/dom-utils'; -import { forEach, filter, contains, values, detect } from '../utils/array-utils'; -import { setData } from '../utils/element-utils'; -import Cursor from '../utils/cursor'; -import Range from '../utils/cursor/range'; -import Position from '../utils/cursor/position'; -import Environment from '../utils/environment'; -import PostNodeBuilder from '../models/post-node-builder'; -import { DEFAULT_TEXT_INPUT_HANDLERS } from './text-input-handlers'; -import { - DEFAULT_KEY_COMMANDS, buildKeyCommand, findKeyCommands, validateKeyCommand -} from './key-commands'; -import { CARD_MODES } from '../models/card'; -import assert from '../utils/assert'; -import MutationHandler from 'mobiledoc-kit/editor/mutation-handler'; -import EditHistory from 'mobiledoc-kit/editor/edit-history'; -import EventManager from 'mobiledoc-kit/editor/event-manager'; -import EditState from 'mobiledoc-kit/editor/edit-state'; -import DOMRenderer from 'mobiledoc-dom-renderer'; -import TextRenderer from 'mobiledoc-text-renderer'; -import LifecycleCallbacks from 'mobiledoc-kit/models/lifecycle-callbacks'; -import LogManager from 'mobiledoc-kit/utils/log-manager'; -import toRange from 'mobiledoc-kit/utils/to-range'; -import MobiledocError from 'mobiledoc-kit/utils/mobiledoc-error'; +import Tooltip, { DEFAULT_TOOLTIP_PLUGIN } from '../views/tooltip' +import PostEditor from './post' +import ImageCard from '../cards/image' +import { DIRECTION } from '../utils/key' +import mobiledocParsers from '../parsers/mobiledoc' +import HTMLParser from '../parsers/html' +import DOMParser from '../parsers/dom' +import Renderer from 'mobiledoc-kit/renderers/editor-dom' +import RenderTree from 'mobiledoc-kit/models/render-tree' +import mobiledocRenderers from '../renderers/mobiledoc' +import { MOBILEDOC_VERSION } from 'mobiledoc-kit/renderers/mobiledoc' +import { mergeWithOptions } from '../utils/merge' +import { normalizeTagName, clearChildNodes, serializeHTML } from '../utils/dom-utils' +import { forEach, filter, contains, values, detect } from '../utils/array-utils' +import { setData } from '../utils/element-utils' +import Cursor from '../utils/cursor' +import Range from '../utils/cursor/range' +import Position from '../utils/cursor/position' +import Environment from '../utils/environment' +import PostNodeBuilder from '../models/post-node-builder' +import { DEFAULT_TEXT_INPUT_HANDLERS } from './text-input-handlers' +import { DEFAULT_KEY_COMMANDS, buildKeyCommand, findKeyCommands, validateKeyCommand } from './key-commands' +import { CARD_MODES } from '../models/card' +import assert from '../utils/assert' +import MutationHandler from 'mobiledoc-kit/editor/mutation-handler' +import EditHistory from 'mobiledoc-kit/editor/edit-history' +import EventManager from 'mobiledoc-kit/editor/event-manager' +import EditState from 'mobiledoc-kit/editor/edit-state' +import DOMRenderer from 'mobiledoc-dom-renderer' +import TextRenderer from 'mobiledoc-text-renderer' +import LifecycleCallbacks from 'mobiledoc-kit/models/lifecycle-callbacks' +import LogManager from 'mobiledoc-kit/utils/log-manager' +import toRange from 'mobiledoc-kit/utils/to-range' +import MobiledocError from 'mobiledoc-kit/utils/mobiledoc-error' // This export may later be deprecated, but re-export it from the renderer here // for consumers that may depend on it. -export { EDITOR_ELEMENT_CLASS_NAME } from 'mobiledoc-kit/renderers/editor-dom'; +export { EDITOR_ELEMENT_CLASS_NAME } from 'mobiledoc-kit/renderers/editor-dom' const defaults = { placeholder: 'Write here...', @@ -49,16 +47,16 @@ const defaults = { cards: [], atoms: [], cardOptions: {}, - unknownCardHandler: ({env}) => { - throw new MobiledocError(`Unknown card encountered: ${env.name}`); + unknownCardHandler: ({ env }) => { + throw new MobiledocError(`Unknown card encountered: ${env.name}`) }, - unknownAtomHandler: ({env}) => { - throw new MobiledocError(`Unknown atom encountered: ${env.name}`); + unknownAtomHandler: ({ env }) => { + throw new MobiledocError(`Unknown atom encountered: ${env.name}`) }, mobiledoc: null, html: null, - tooltipPlugin: DEFAULT_TOOLTIP_PLUGIN -}; + tooltipPlugin: DEFAULT_TOOLTIP_PLUGIN, +} const CALLBACK_QUEUES = { DID_UPDATE: 'didUpdate', @@ -71,8 +69,8 @@ const CALLBACK_QUEUES = { DID_REPARSE: 'didReparse', POST_DID_CHANGE: 'postDidChange', INPUT_MODE_DID_CHANGE: 'inputModeDidChange', - WILL_COPY: 'willCopy' -}; + WILL_COPY: 'willCopy', +} /** * The Editor is a core component of mobiledoc-kit. After instantiating @@ -122,37 +120,39 @@ class Editor { * @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 || []; + 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); + mergeWithOptions(this, defaults, options) + this.cards.push(ImageCard) - DEFAULT_KEY_COMMANDS.forEach(kc => this.registerKeyCommand(kc)); + 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._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.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)); - this._beforeHooks = { toggleMarkup: [] }; + 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)) + this._beforeHooks = { toggleMarkup: [] } - DEFAULT_TEXT_INPUT_HANDLERS.forEach(handler => this.onTextInput(handler)); + DEFAULT_TEXT_INPUT_HANDLERS.forEach(handler => this.onTextInput(handler)) - this.hasRendered = false; + this.hasRendered = false } /** @@ -160,11 +160,11 @@ class Editor { * @param {Array} [logTypes=[]] If present, only the given log types will be logged. * @public */ - enableLogging(logTypes=[]) { + enableLogging(logTypes = []) { if (logTypes.length === 0) { - this._logManager.enableAll(); + this._logManager.enableAll() } else { - this._logManager.enableTypes(logTypes); + this._logManager.enableTypes(logTypes) } } @@ -173,14 +173,14 @@ class Editor { * @public */ disableLogging() { - this._logManager.disable(); + this._logManager.disable() } /** * @private */ loggerFor(type) { - return this._logManager.for(type); + return this._logManager.for(type) } /** @@ -188,43 +188,44 @@ class Editor { * @type {PostNodeBuilder} */ get builder() { - if (!this._builder) { this._builder = new PostNodeBuilder(); } - return this._builder; + if (!this._builder) { + this._builder = new PostNodeBuilder() + } + return this._builder } loadPost() { - let {mobiledoc, html} = this; + let { mobiledoc, html } = this if (mobiledoc) { - return mobiledocParsers.parse(this.builder, 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); + let options = { plugins: this._parserPlugins } + return new HTMLParser(this.builder, options).parse(this.html) } else { - let dom = html; - return this._parser.parse(dom); + let dom = html + return this._parser.parse(dom) } } else { - return this.builder.createPost([this.builder.createMarkupSection()]); + return this.builder.createPost([this.builder.createMarkupSection()]) } } rerender() { - let postRenderNode = this.post.renderNode; + 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(); + assert('Must call `render` before `rerender` can be called', this.hasRendered) + postRenderNode.element = this.element + postRenderNode.markDirty() } - this.runCallbacks(CALLBACK_QUEUES.WILL_RENDER); + this.runCallbacks(CALLBACK_QUEUES.WILL_RENDER) this._mutationHandler.suspendObservation(() => { - this._renderer.render(this._renderTree); - }); - this.runCallbacks(CALLBACK_QUEUES.DID_RENDER); + this._renderer.render(this._renderTree) + }) + this.runCallbacks(CALLBACK_QUEUES.DID_RENDER) } /** @@ -233,55 +234,60 @@ class Editor { * @public */ render(element) { - assert('Cannot render an editor twice. Use `rerender` to update the ' + - 'rendering of an existing editor instance.', - !this.hasRendered); + assert( + 'Cannot render an editor twice. Use `rerender` to update the ' + 'rendering of an existing editor instance.', + !this.hasRendered + ) - element.spellcheck = this.spellcheck; + element.spellcheck = this.spellcheck - clearChildNodes(element); + clearChildNodes(element) - this.element = element; + this.element = element if (this.showLinkTooltips) { - this._addTooltip(); + this._addTooltip() } // A call to `run` will trigger the didUpdatePostCallbacks hooks with a // postEditor. - this.run(() => {}); + 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.hasRendered = true + this.rerender() - this._mutationHandler.init(); - this._eventManager.init(); + this._mutationHandler.init() + this._eventManager.init() if (this.isEditable === false) { - this.disableEditing(); + this.disableEditing() } else { - this.enableEditing(); + this.enableEditing() } if (this.autofocus) { - this.selectRange(this.post.headPosition()); + this.selectRange(this.post.headPosition()) } } _addTooltip() { - this.addView(new Tooltip({ - rootElement: this.element, - showForTag: 'a', - editor: this - })); + this.addView( + new Tooltip({ + rootElement: this.element, + showForTag: 'a', + editor: this, + }) + ) } get keyCommands() { - if (!this._keyCommands) { this._keyCommands = []; } - return this._keyCommands; + if (!this._keyCommands) { + this._keyCommands = [] + } + return this._keyCommands } /** @@ -292,9 +298,9 @@ class Editor { * @public */ registerKeyCommand(rawKeyCommand) { - const keyCommand = buildKeyCommand(rawKeyCommand); - assert('Key Command is not valid', validateKeyCommand(keyCommand)); - this.keyCommands.unshift(keyCommand); + const keyCommand = buildKeyCommand(rawKeyCommand) + assert('Key Command is not valid', validateKeyCommand(keyCommand)) + this.keyCommands.unshift(keyCommand) } /** @@ -302,11 +308,11 @@ class Editor { * @public */ unregisterKeyCommands(name) { - for(let i = this.keyCommands.length-1; i > -1; i--) { - let keyCommand = this.keyCommands[i]; + for (let i = this.keyCommands.length - 1; i > -1; i--) { + let keyCommand = this.keyCommands[i] - if(keyCommand.name === name) { - this.keyCommands.splice(i,1); + if (keyCommand.name === name) { + this.keyCommands.splice(i, 1) } } } @@ -316,11 +322,11 @@ class Editor { * cursor in the new position. * @public */ - deleteAtPosition(position, direction, {unit}) { + deleteAtPosition(position, direction, { unit }) { this.run(postEditor => { - let nextPosition = postEditor.deleteAtPosition(position, direction, {unit}); - postEditor.setRange(nextPosition); - }); + let nextPosition = postEditor.deleteAtPosition(position, direction, { unit }) + postEditor.setRange(nextPosition) + }) } /** @@ -331,52 +337,60 @@ class Editor { */ deleteRange(range) { this.run(postEditor => { - let nextPosition = postEditor.deleteRange(range); - postEditor.setRange(nextPosition); - }); + let nextPosition = postEditor.deleteRange(range) + postEditor.setRange(nextPosition) + }) } /** * @private */ - performDelete({direction, unit}={direction: DIRECTION.BACKWARD, unit: 'char'}) { - let { range } = this; + performDelete({ direction, unit } = { direction: DIRECTION.BACKWARD, unit: 'char' }) { + let { range } = this - this.runCallbacks(CALLBACK_QUEUES.WILL_DELETE, [range, direction, unit]); + this.runCallbacks(CALLBACK_QUEUES.WILL_DELETE, [range, direction, unit]) if (range.isCollapsed) { - this.deleteAtPosition(range.head, direction, {unit}); + this.deleteAtPosition(range.head, direction, { unit }) } else { - this.deleteRange(range); + this.deleteRange(range) } - this.runCallbacks(CALLBACK_QUEUES.DID_DELETE, [range, direction, unit]); + this.runCallbacks(CALLBACK_QUEUES.DID_DELETE, [range, direction, unit]) } handleNewline(event) { - if (!this.hasCursor()) { return; } + if (!this.hasCursor()) { + return + } - event.preventDefault(); + event.preventDefault() - let { range } = this; + let { range } = this this.run(postEditor => { - let cursorSection; + let cursorSection if (!range.isCollapsed) { - let nextPosition = postEditor.deleteRange(range); - cursorSection = nextPosition.section; + let nextPosition = postEditor.deleteRange(range) + cursorSection = nextPosition.section if (cursorSection && cursorSection.isBlank) { - postEditor.setRange(cursorSection.headPosition()); - return; + 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.WILL_HANDLE_NEWLINE, [event]); - if (defaultPrevented) { return; } + let defaultPrevented = false + const event = { + preventDefault() { + defaultPrevented = true + }, + } + this.runCallbacks(CALLBACK_QUEUES.WILL_HANDLE_NEWLINE, [event]) + if (defaultPrevented) { + return + } - cursorSection = postEditor.splitSection(range.head)[1]; - postEditor.setRange(cursorSection.headPosition()); - }); + cursorSection = postEditor.splitSection(range.head)[1] + postEditor.setRange(cursorSection.headPosition()) + }) } /** @@ -385,7 +399,7 @@ class Editor { * @private */ _postDidChange() { - this.runCallbacks(CALLBACK_QUEUES.POST_DID_CHANGE); + this.runCallbacks(CALLBACK_QUEUES.POST_DID_CHANGE) } /** @@ -395,14 +409,14 @@ class Editor { * @param {Range|Position} range */ selectRange(range) { - range = toRange(range); + range = toRange(range) - this.cursor.selectRange(range); - this.range = range; + this.cursor.selectRange(range) + this.range = range } get cursor() { - return new Cursor(this); + return new Cursor(this) } /** @@ -410,66 +424,66 @@ class Editor { * @return {Range} */ get range() { - return this._editState.range; + return this._editState.range } set range(newRange) { - this._editState.updateRange(newRange); + this._editState.updateRange(newRange) if (this._editState.rangeDidChange()) { - this._rangeDidChange(); + this._rangeDidChange() } if (this._editState.inputModeDidChange()) { - this._inputModeDidChange(); + this._inputModeDidChange() } } _readRangeFromDOM() { - this.range = this.cursor.offsets; + this.range = this.cursor.offsets } setPlaceholder(placeholder) { - setData(this.element, 'placeholder', placeholder); + setData(this.element, 'placeholder', placeholder) } _reparsePost() { - let post = this._parser.parse(this.element); + let post = this._parser.parse(this.element) this.run(postEditor => { - postEditor.removeAllSections(); - postEditor.migrateSectionsFromPost(post); - postEditor.setRange(Range.blankRange()); - }); + postEditor.removeAllSections() + postEditor.migrateSectionsFromPost(post) + postEditor.setRange(Range.blankRange()) + }) - this.runCallbacks(CALLBACK_QUEUES.DID_REPARSE); - this._postDidChange(); + this.runCallbacks(CALLBACK_QUEUES.DID_REPARSE) + this._postDidChange() } - _reparseSections(sections=[]) { - let currentRange; + _reparseSections(sections = []) { + let currentRange sections.forEach(section => { - this._parser.reparseSection(section, this._renderTree); - }); - this._removeDetachedSections(); + this._parser.reparseSection(section, this._renderTree) + }) + this._removeDetachedSections() if (this._renderTree.isDirty) { - currentRange = this.range; + 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; + let range = this._editHistory._pendingSnapshot.range this.run(() => { - this._editHistory._pendingSnapshot.range = range; - }); - this.rerender(); + this._editHistory._pendingSnapshot.range = range + }) + this.rerender() if (currentRange) { - this.selectRange(currentRange); + this.selectRange(currentRange) } - this.runCallbacks(CALLBACK_QUEUES.DID_REPARSE); - this._postDidChange(); + this.runCallbacks(CALLBACK_QUEUES.DID_REPARSE) + this._postDidChange() } // FIXME this should be able to be removed now -- if any sections are detached, @@ -478,7 +492,7 @@ class Editor { forEach( filter(this.post.sections, s => !s.renderNode.isAttached()), s => s.renderNode.scheduleForRemoval() - ); + ) } /** @@ -486,23 +500,23 @@ class Editor { * @type {Section[]} */ get activeSections() { - return this._editState.activeSections; + return this._editState.activeSections } get activeSection() { - const { activeSections } = this; - return activeSections[activeSections.length - 1]; + const { activeSections } = this + return activeSections[activeSections.length - 1] } get activeSectionAttributes() { - return this._editState.activeSectionAttributes; + return this._editState.activeSectionAttributes } detectMarkupInRange(range, markupTagName) { - let markups = this.post.markupsInRange(range); + let markups = this.post.markupsInRange(range) return detect(markups, markup => { - return markup.hasTag(markupTagName); - }); + return markup.hasTag(markupTagName) + }) } /** @@ -510,7 +524,7 @@ class Editor { * @public */ get activeMarkups() { - return this._editState.activeMarkups; + return this._editState.activeMarkups } /** @@ -518,15 +532,15 @@ class Editor { * @return {boolean} */ hasActiveMarkup(markup) { - let matchesFn; + let matchesFn if (typeof markup === 'string') { - let tagName = normalizeTagName(markup); - matchesFn = (m) => m.tagName === tagName; + let tagName = normalizeTagName(markup) + matchesFn = m => m.tagName === tagName } else { - matchesFn = (m) => m === markup; + matchesFn = m => m === markup } - return !!detect(this.activeMarkups, matchesFn); + return !!detect(this.activeMarkups, matchesFn) } /** @@ -534,8 +548,8 @@ class Editor { * @return {Mobiledoc} Serialized mobiledoc * @public */ - serialize(version=MOBILEDOC_VERSION) { - return this.serializePost(this.post, 'mobiledoc', {version}); + serialize(version = MOBILEDOC_VERSION) { + return this.serializePost(this.post, 'mobiledoc', { version }) } /** @@ -549,8 +563,8 @@ class Editor { * @public */ serializeTo(format) { - let post = this.post; - return this.serializePost(post, format); + let post = this.post + return this.serializePost(post, format) } /** @@ -561,47 +575,46 @@ class Editor { * @return {Object|String} * @private */ - serializePost(post, format, options={}) { - const validFormats = ['mobiledoc', 'html', 'text']; - assert(`Unrecognized serialization format ${format}`, - contains(validFormats, format)); + 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; - return mobiledocRenderers.render(post, version); + let version = options.version || MOBILEDOC_VERSION + return mobiledocRenderers.render(post, version) } else { - let rendered; - let mobiledoc = this.serializePost(post, 'mobiledoc'); - let unknownCardHandler = () => {}; - let unknownAtomHandler = () => {}; - let rendererOptions = { unknownCardHandler, unknownAtomHandler }; + let rendered + let mobiledoc = this.serializePost(post, 'mobiledoc') + let unknownCardHandler = () => {} + let unknownAtomHandler = () => {} + let rendererOptions = { unknownCardHandler, unknownAtomHandler } switch (format) { case 'html': { - let result; + let result if (Environment.hasDOM()) { - rendered = new DOMRenderer(rendererOptions).render(mobiledoc); - result = `
${serializeHTML(rendered.result)}
`; + rendered = new DOMRenderer(rendererOptions).render(mobiledoc) + result = `
${serializeHTML(rendered.result)}
` } else { // Fallback to text serialization - result = this.serializePost(post, 'text', options); + result = this.serializePost(post, 'text', options) } - return result; + return result } case 'text': - rendered = new TextRenderer(rendererOptions).render(mobiledoc); - return rendered.result; + rendered = new TextRenderer(rendererOptions).render(mobiledoc) + return rendered.result } } } addView(view) { - this._views.push(view); + this._views.push(view) } removeAllViews() { - this._views.forEach((v) => v.destroy()); - this._views = []; + this._views.forEach(v => v.destroy()) + this._views = [] } /** @@ -613,7 +626,7 @@ class Editor { * @public */ hasCursor() { - return this.cursor.hasCursor(); + return this.cursor.hasCursor() } /** @@ -621,18 +634,18 @@ class Editor { * @public */ destroy() { - this.isDestroyed = true; + this.isDestroyed = true if (this._hasSelection()) { - this.cursor.clearSelection(); + this.cursor.clearSelection() } if (this._hasFocus()) { - this.element.blur(); // FIXME This doesn't blur the element on IE11 + 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(); + this._mutationHandler.destroy() + this._eventManager.destroy() + this.removeAllViews() + this._renderer.destroy() + this._editState.destroy() } /** @@ -642,12 +655,12 @@ class Editor { * @public */ disableEditing() { - this.isEditable = false; + this.isEditable = false if (this.hasRendered) { - this._eventManager.stop(); - this.element.setAttribute('contentEditable', false); - this.setPlaceholder(''); - this.selectRange(Range.blankRange()); + this._eventManager.stop() + this.element.setAttribute('contentEditable', false) + this.setPlaceholder('') + this.selectRange(Range.blankRange()) } } @@ -659,11 +672,11 @@ class Editor { * @public */ enableEditing() { - this.isEditable = true; + this.isEditable = true if (this.hasRendered) { - this._eventManager.start(); - this.element.setAttribute('contentEditable', true); - this.setPlaceholder(this.placeholder); + this._eventManager.start() + this.element.setAttribute('contentEditable', true) + this.setPlaceholder(this.placeholder) } } @@ -675,7 +688,7 @@ class Editor { * @public */ editCard(cardSection) { - this._setCardMode(cardSection, CARD_MODES.EDIT); + this._setCardMode(cardSection, CARD_MODES.EDIT) } /** @@ -687,7 +700,7 @@ class Editor { * @public */ displayCard(cardSection) { - this._setCardMode(cardSection, CARD_MODES.DISPLAY); + this._setCardMode(cardSection, CARD_MODES.DISPLAY) } /** @@ -716,20 +729,20 @@ class Editor { * @public */ run(callback) { - const postEditor = new PostEditor(this); - postEditor.begin(); - this._editHistory.snapshot(); - const result = callback(postEditor); - this.runCallbacks(CALLBACK_QUEUES.DID_UPDATE, [postEditor]); - postEditor.complete(); - this._readRangeFromDOM(); + const postEditor = new PostEditor(this) + postEditor.begin() + this._editHistory.snapshot() + const result = callback(postEditor) + this.runCallbacks(CALLBACK_QUEUES.DID_UPDATE, [postEditor]) + postEditor.complete() + this._readRangeFromDOM() if (postEditor._shouldCancelSnapshot) { - this._editHistory._pendingSnapshot = null; + this._editHistory._pendingSnapshot = null } - this._editHistory.storeSnapshot(postEditor.editActionTaken); + this._editHistory.storeSnapshot(postEditor.editActionTaken) - return result; + return result } /** @@ -737,7 +750,7 @@ class Editor { * @public */ didUpdatePost(callback) { - this.addCallback(CALLBACK_QUEUES.DID_UPDATE, callback); + this.addCallback(CALLBACK_QUEUES.DID_UPDATE, callback) } /** @@ -746,7 +759,7 @@ class Editor { * retrieve the post in portable mobiledoc format. */ postDidChange(callback) { - this.addCallback(CALLBACK_QUEUES.POST_DID_CHANGE, callback); + this.addCallback(CALLBACK_QUEUES.POST_DID_CHANGE, callback) } /** @@ -765,7 +778,7 @@ class Editor { * @public */ onTextInput(inputHandler) { - this._eventManager.registerInputHandler(inputHandler); + this._eventManager.registerInputHandler(inputHandler) } /** @@ -774,7 +787,7 @@ class Editor { * @public */ unregisterAllTextInputHandlers() { - this._eventManager.unregisterAllTextInputHandlers(); + this._eventManager.unregisterAllTextInputHandlers() } /** @@ -784,7 +797,7 @@ class Editor { * @public */ unregisterTextInputHandler(name) { - this._eventManager.unregisterInputHandler(name); + this._eventManager.unregisterInputHandler(name) } /** @@ -792,7 +805,7 @@ class Editor { * active sections) has changed, either via user input or programmatically */ inputModeDidChange(callback) { - this.addCallback(CALLBACK_QUEUES.INPUT_MODE_DID_CHANGE, callback); + this.addCallback(CALLBACK_QUEUES.INPUT_MODE_DID_CHANGE, callback) } /** @@ -801,7 +814,7 @@ class Editor { * @public */ willRender(callback) { - this.addCallback(CALLBACK_QUEUES.WILL_RENDER, callback); + this.addCallback(CALLBACK_QUEUES.WILL_RENDER, callback) } /** @@ -810,11 +823,11 @@ class Editor { * @public */ didRender(callback) { - this.addCallback(CALLBACK_QUEUES.DID_RENDER, callback); + this.addCallback(CALLBACK_QUEUES.DID_RENDER, callback) } willCopy(callback) { - this.addCallback(CALLBACK_QUEUES.WILL_COPY, callback); + this.addCallback(CALLBACK_QUEUES.WILL_COPY, callback) } /** @@ -822,7 +835,7 @@ class Editor { * @public */ willDelete(callback) { - this.addCallback(CALLBACK_QUEUES.WILL_DELETE, callback); + this.addCallback(CALLBACK_QUEUES.WILL_DELETE, callback) } /** @@ -830,7 +843,7 @@ class Editor { * @public */ didDelete(callback) { - this.addCallback(CALLBACK_QUEUES.DID_DELETE, callback); + this.addCallback(CALLBACK_QUEUES.DID_DELETE, callback) } /** @@ -838,7 +851,7 @@ class Editor { * @public */ willHandleNewline(callback) { - this.addCallback(CALLBACK_QUEUES.WILL_HANDLE_NEWLINE, callback); + this.addCallback(CALLBACK_QUEUES.WILL_HANDLE_NEWLINE, callback) } /** @@ -847,25 +860,25 @@ class Editor { * @public */ cursorDidChange(callback) { - this.addCallback(CALLBACK_QUEUES.CURSOR_DID_CHANGE, callback); + this.addCallback(CALLBACK_QUEUES.CURSOR_DID_CHANGE, callback) } _rangeDidChange() { if (this.hasRendered) { - this.runCallbacks(CALLBACK_QUEUES.CURSOR_DID_CHANGE); + this.runCallbacks(CALLBACK_QUEUES.CURSOR_DID_CHANGE) } } _inputModeDidChange() { - this.runCallbacks(CALLBACK_QUEUES.INPUT_MODE_DID_CHANGE); + this.runCallbacks(CALLBACK_QUEUES.INPUT_MODE_DID_CHANGE) } _insertEmptyMarkupSectionAtCursor() { this.run(postEditor => { - const section = postEditor.builder.createMarkupSection('p'); - postEditor.insertSectionBefore(this.post.sections, section); - postEditor.setRange(section.toRange()); - }); + const section = postEditor.builder.createMarkupSection('p') + postEditor.insertSectionBefore(this.post.sections, section) + postEditor.setRange(section.toRange()) + }) } /** @@ -884,7 +897,7 @@ class Editor { * @param {editorBeforeCallback} */ beforeToggleMarkup(callback) { - this._beforeHooks.toggleMarkup.push(callback); + this._beforeHooks.toggleMarkup.push(callback) } /** @@ -901,34 +914,36 @@ class Editor { * @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; } + 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(); + 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(); + this._ensureFocus() } else { - this.run(postEditor => postEditor.toggleMarkup(markup, range)); + 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(); + this.focus() } } focus() { - this.element.focus(); + this.element.focus() } /** @@ -938,8 +953,8 @@ class Editor { * @return {Boolean} */ _hasSelection() { - let { cursor } = this; - return this.hasRendered && (cursor._hasCollapsedSelection() || cursor._hasSelection()); + let { cursor } = this + return this.hasRendered && (cursor._hasCollapsedSelection() || cursor._hasSelection()) } /** @@ -949,7 +964,7 @@ class Editor { * @return {Boolean} */ _hasFocus() { - return document.activeElement === this.element; + return document.activeElement === this.element } /** @@ -961,7 +976,7 @@ class Editor { * @see PostEditor#toggleSection */ toggleSection(tagName) { - this.run(postEditor => postEditor.toggleSection(tagName, this.range)); + this.run(postEditor => postEditor.toggleSection(tagName, this.range)) } /** @@ -973,7 +988,7 @@ class Editor { * @see PostEditor#setAttribute */ setAttribute(key, value) { - this.run(postEditor => postEditor.setAttribute(key, value, this.range)); + this.run(postEditor => postEditor.setAttribute(key, value, this.range)) } /** @@ -984,7 +999,7 @@ class Editor { * @see PostEditor#removeAttribute */ removeAttribute(key) { - this.run(postEditor => postEditor.removeAttribute(key, this.range)); + this.run(postEditor => postEditor.removeAttribute(key, this.range)) } /** @@ -1001,15 +1016,15 @@ class Editor { * @private */ handleKeyCommand(event) { - const keyCommands = findKeyCommands(this.keyCommands, event); - for (let i=0; i { if (!range.isCollapsed) { - position = postEditor.deleteRange(range); + position = postEditor.deleteRange(range) } - postEditor.insertTextWithMarkup(position, text, activeMarkups); - }); + postEditor.insertTextWithMarkup(position, text, activeMarkups) + }) } /** @@ -1046,25 +1067,27 @@ class Editor { * @return {Atom} The inserted atom. * @public */ - insertAtom(atomName, atomText='', atomPayload={}) { - if (!this.hasCursor()) { return; } + insertAtom(atomName, atomText = '', atomPayload = {}) { + if (!this.hasCursor()) { + return + } if (this.post.isBlank) { - this._insertEmptyMarkupSectionAtCursor(); + this._insertEmptyMarkupSectionAtCursor() } - let atom; - let { range } = this; + let atom + let { range } = this this.run(postEditor => { - let position = range.head; + let position = range.head - atom = postEditor.builder.createAtom(atomName, atomText, atomPayload); + atom = postEditor.builder.createAtom(atomName, atomText, atomPayload) if (!range.isCollapsed) { - position = postEditor.deleteRange(range); + position = postEditor.deleteRange(range) } - postEditor.insertMarkers(position, [atom]); - }); - return atom; + postEditor.insertMarkers(position, [atom]) + }) + return atom } /** @@ -1079,33 +1102,37 @@ class Editor { * @return {Card} The inserted Card section. * @public */ - insertCard(cardName, cardPayload={}, inEditMode=false) { - if (!this.hasCursor()) { return; } + insertCard(cardName, cardPayload = {}, inEditMode = false) { + if (!this.hasCursor()) { + return + } if (this.post.isBlank) { - this._insertEmptyMarkupSectionAtCursor(); + this._insertEmptyMarkupSectionAtCursor() } - let card; - let { range } = this; + let card + let { range } = this this.run(postEditor => { - let position = range.tail; - card = postEditor.builder.createCardSection(cardName, cardPayload); + let position = range.tail + card = postEditor.builder.createCardSection(cardName, cardPayload) if (inEditMode) { - this.editCard(card); + this.editCard(card) } if (!range.isCollapsed) { - position = postEditor.deleteRange(range); + position = postEditor.deleteRange(range) } - let section = position.section; - if (section.isNested) { section = section.parent; } + let section = position.section + if (section.isNested) { + section = section.parent + } if (section.isBlank) { - postEditor.replaceSection(section, card); + postEditor.replaceSection(section, card) } else { - let collection = this.post.sections; - postEditor.insertSectionBefore(collection, card, section.next); + 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. @@ -1116,9 +1143,9 @@ class Editor { // 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; + postEditor.setRange(card.tailPosition()) + }) + return card } /** @@ -1127,40 +1154,40 @@ class Editor { * @return {Position|null} */ positionAtPoint(x, y) { - return Position.atPoint(x, y, this); + return Position.atPoint(x, y, this) } /** * @private */ _setCardMode(cardSection, mode) { - const renderNode = cardSection.renderNode; + const renderNode = cardSection.renderNode if (renderNode && renderNode.isRendered) { - const cardNode = renderNode.cardNode; - cardNode[mode](); + const cardNode = renderNode.cardNode + cardNode[mode]() } else { - cardSection.setInitialMode(mode); + cardSection.setInitialMode(mode) } } triggerEvent(context, eventName, event) { - this._eventManager._trigger(context, eventName, event); + this._eventManager._trigger(context, eventName, event) } addCallback(...args) { - this._callbacks.addCallback(...args); + this._callbacks.addCallback(...args) } addCallbackOnce(...args) { - this._callbacks.addCallbackOnce(...args); + this._callbacks.addCallbackOnce(...args) } runCallbacks(...args) { if (this.isDestroyed) { // TODO warn that callback attempted after editor was destroyed - return; + return } - this._callbacks.runCallbacks(...args); + this._callbacks.runCallbacks(...args) } /** @@ -1170,13 +1197,13 @@ class Editor { * @private */ _runBeforeHooks(hookName, ...args) { - let hooks = this._beforeHooks[hookName] || []; + let hooks = this._beforeHooks[hookName] || [] for (let i = 0; i < hooks.length; i++) { if (hooks[i](...args) === false) { - return true; + return true } } } } -export default Editor; +export default Editor diff --git a/src/js/editor/event-manager.js b/src/js/editor/event-manager.js index 6671f7805..ea6aa7408 100644 --- a/src/js/editor/event-manager.js +++ b/src/js/editor/event-manager.js @@ -1,79 +1,74 @@ -import assert from 'mobiledoc-kit/utils/assert'; -import { - parsePostFromPaste, - setClipboardData, - parsePostFromDrop -} from 'mobiledoc-kit/utils/parse-utils'; -import { filter, forEach } from 'mobiledoc-kit/utils/array-utils'; -import Key from 'mobiledoc-kit/utils/key'; -import TextInputHandler from 'mobiledoc-kit/editor/text-input-handler'; -import SelectionManager from 'mobiledoc-kit/editor/selection-manager'; -import Browser from 'mobiledoc-kit/utils/browser'; - -const ELEMENT_EVENT_TYPES = [ - 'keydown', 'keyup', 'cut', 'copy', 'paste', 'keypress', 'drop' -]; +import assert from 'mobiledoc-kit/utils/assert' +import { parsePostFromPaste, setClipboardData, parsePostFromDrop } from 'mobiledoc-kit/utils/parse-utils' +import { filter, forEach } from 'mobiledoc-kit/utils/array-utils' +import Key from 'mobiledoc-kit/utils/key' +import TextInputHandler from 'mobiledoc-kit/editor/text-input-handler' +import SelectionManager from 'mobiledoc-kit/editor/selection-manager' +import Browser from 'mobiledoc-kit/utils/browser' + +const ELEMENT_EVENT_TYPES = ['keydown', 'keyup', 'cut', 'copy', 'paste', 'keypress', 'drop'] export default class EventManager { constructor(editor) { - this.editor = editor; - this.logger = editor.loggerFor('event-manager'); - this._textInputHandler = new TextInputHandler(editor); - this._listeners = []; + this.editor = editor + this.logger = editor.loggerFor('event-manager') + this._textInputHandler = new TextInputHandler(editor) + this._listeners = [] this.modifierKeys = { - shift: false - }; + shift: false, + } - this._selectionManager = new SelectionManager( - this.editor, this.selectionDidChange.bind(this)); - this.started = true; + 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); + let { + editor: { element }, + } = this + assert(`Cannot init EventManager without element`, !!element) ELEMENT_EVENT_TYPES.forEach(type => { - this._addListener(element, type); - }); + this._addListener(element, type) + }) - this._selectionManager.start(); + this._selectionManager.start() } start() { - this.started = true; + this.started = true } stop() { - this.started = false; + this.started = false } registerInputHandler(inputHandler) { - this._textInputHandler.register(inputHandler); + this._textInputHandler.register(inputHandler) } unregisterInputHandler(name) { - this._textInputHandler.unregister(name); + this._textInputHandler.unregister(name) } unregisterAllTextInputHandlers() { - this._textInputHandler.destroy(); - this._textInputHandler = new TextInputHandler(this.editor); + this._textInputHandler.destroy() + this._textInputHandler = new TextInputHandler(this.editor) } _addListener(context, type) { - assert(`Missing listener for ${type}`, !!this[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]); + 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 = []; + context.removeEventListener(type, listener) + }) + this._listeners = [] } // This is primarily useful for programmatically simulating events on the @@ -81,211 +76,223 @@ export default class EventManager { _trigger(context, type, event) { forEach( filter(this._listeners, ([_context, _type]) => { - return _context === context && _type === type; + return _context === context && _type === type }), - ([context,, listener]) => { - listener.call(context, event); + ([context, , listener]) => { + listener.call(context, event) } - ); + ) } destroy() { - this._textInputHandler.destroy(); - this._selectionManager.destroy(); - this._removeListeners(); + this._textInputHandler.destroy() + this._selectionManager.destroy() + this._removeListeners() } _handleEvent(type, event) { - let {target: element} = event; + let { target: element } = event if (!this.started) { // abort handling this event - return true; + return true } if (!this.isElementAddressable(element)) { // abort handling this event - return true; + return true } - this[type](event); + this[type](event) } isElementAddressable(element) { - return this.editor.cursor.isAddressable(element); + return this.editor.cursor.isAddressable(element) } selectionDidChange(selection /*, prevSelection */) { - let shouldNotify = true; - let { anchorNode } = selection; + 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; + shouldNotify = true } else { // selection changes wholly outside the editor should not trigger // change notifications - shouldNotify = false; + shouldNotify = false } } if (shouldNotify) { - this.editor._readRangeFromDOM(); + this.editor._readRangeFromDOM() } } keypress(event) { - let { editor, _textInputHandler } = this; - if (!editor.hasCursor()) { return; } + let { editor, _textInputHandler } = this + if (!editor.hasCursor()) { + return + } - let key = Key.fromEvent(event); + let key = Key.fromEvent(event) if (!key.isPrintable()) { - return; + return } else { - event.preventDefault(); + event.preventDefault() } - _textInputHandler.handle(key.toString()); + _textInputHandler.handle(key.toString()) } keydown(event) { - let { editor } = this; - if (!editor.hasCursor()) { return; } - if (!editor.isEditable) { return; } + let { editor } = this + if (!editor.hasCursor()) { + return + } + if (!editor.isEditable) { + return + } - let key = Key.fromEvent(event); - this._updateModifiersFromKey(key, {isDown:true}); + let key = Key.fromEvent(event) + this._updateModifiersFromKey(key, { isDown: true }) - if (editor.handleKeyCommand(event)) { return; } + if (editor.handleKeyCommand(event)) { + return + } if (editor.post.isBlank) { - editor._insertEmptyMarkupSectionAtCursor(); + editor._insertEmptyMarkupSectionAtCursor() } - let range = editor.range; + let range = editor.range - switch(true) { + switch (true) { // FIXME This should be restricted to only card/atom boundaries case key.isHorizontalArrowWithoutModifiersOtherThanShift(): { - let newRange; + let newRange if (key.isShift()) { - newRange = range.extend(key.direction * 1); + newRange = range.extend(key.direction * 1) } else { - newRange = range.move(key.direction); + newRange = range.move(key.direction) } - editor.selectRange(newRange); - event.preventDefault(); - break; + editor.selectRange(newRange) + event.preventDefault() + break } case key.isDelete(): { - let { direction } = key; - let unit = 'char'; + let { direction } = key + let unit = 'char' if (key.altKey && Browser.isMac()) { - unit = 'word'; + unit = 'word' } else if (key.ctrlKey && !Browser.isMac()) { - unit = 'word'; + unit = 'word' } - editor.performDelete({direction, unit}); - event.preventDefault(); - break; + editor.performDelete({ direction, unit }) + event.preventDefault() + break } case key.isEnter(): - this._textInputHandler.handleNewLine(); - editor.handleNewline(event); - break; + 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; + 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}); + let { editor } = this + if (!editor.hasCursor()) { + return + } + let key = Key.fromEvent(event) + this._updateModifiersFromKey(key, { isDown: false }) } cut(event) { - event.preventDefault(); + event.preventDefault() - this.copy(event); - this.editor.performDelete(); + this.copy(event) + this.editor.performDelete() } copy(event) { - event.preventDefault(); + event.preventDefault() - let { editor, editor: { range, post } } = this; - post = post.trimTo(range); + 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') - }; + mobiledoc: editor.serializePost(post, 'mobiledoc'), + } - editor.runCallbacks('willCopy', [data]); + editor.runCallbacks('willCopy', [data]) - setClipboardData(event, data, window); + setClipboardData(event, data, window) } paste(event) { - event.preventDefault(); + event.preventDefault() - let { editor } = this; - let range = editor.range; + let { editor } = this + let range = editor.range if (!range.isCollapsed) { - editor.performDelete(); + editor.performDelete() } if (editor.post.isBlank) { - editor._insertEmptyMarkupSectionAtCursor(); + editor._insertEmptyMarkupSectionAtCursor() } - let position = editor.range.head; - let targetFormat = this.modifierKeys.shift ? 'text' : 'html'; - let pastedPost = parsePostFromPaste(event, editor, {targetFormat}); + 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); - }); + let nextPosition = postEditor.insertPost(position, pastedPost) + postEditor.setRange(nextPosition) + }) } drop(event) { - event.preventDefault(); + event.preventDefault() - let { clientX: x, clientY: y } = event; - let { editor } = this; + let { clientX: x, clientY: y } = event + let { editor } = this - let position = editor.positionAtPoint(x, y); + let position = editor.positionAtPoint(x, y) if (!position) { - this.logger.log('Could not find drop position'); - return; + this.logger.log('Could not find drop position') + return } - let post = parsePostFromDrop(event, editor, {logger: this.logger}); + let post = parsePostFromDrop(event, editor, { logger: this.logger }) if (!post) { - this.logger.log('Could not determine post from drop event'); - return; + this.logger.log('Could not determine post from drop event') + return } editor.run(postEditor => { - let nextPosition = postEditor.insertPost(position, post); - postEditor.setRange(nextPosition); - }); + let nextPosition = postEditor.insertPost(position, post) + postEditor.setRange(nextPosition) + }) } - _updateModifiersFromKey(key, {isDown}) { + _updateModifiersFromKey(key, { isDown }) { if (key.isShiftKey()) { - this.modifierKeys.shift = isDown; + this.modifierKeys.shift = isDown } } - } diff --git a/src/js/editor/key-commands.js b/src/js/editor/key-commands.js index f8bf610ef..8ea7c3c49 100644 --- a/src/js/editor/key-commands.js +++ b/src/js/editor/key-commands.js @@ -1,187 +1,213 @@ -import Key from '../utils/key'; -import { MODIFIERS, specialCharacterToCode } from '../utils/key'; -import { filter, reduce } from '../utils/array-utils'; -import assert from '../utils/assert'; -import Browser from '../utils/browser'; -import { toggleLink } from './ui'; +import Key from '../utils/key' +import { MODIFIERS, specialCharacterToCode } from '../utils/key' +import { filter, reduce } from '../utils/array-utils' +import assert from '../utils/assert' +import Browser from '../utils/browser' +import { toggleLink } from './ui' function selectAll(editor) { - let { post } = editor; - editor.selectRange(post.toRange()); + let { post } = editor + editor.selectRange(post.toRange()) } function gotoStartOfLine(editor) { - let {range} = editor; - let {tail: {section}} = range; + let { range } = editor + let { + tail: { section }, + } = range editor.run(postEditor => { - postEditor.setRange(section.headPosition()); - }); + postEditor.setRange(section.headPosition()) + }) } function gotoEndOfLine(editor) { - let {range} = editor; - let {tail: {section}} = range; + let { range } = editor + let { + tail: { section }, + } = range editor.run(postEditor => { - postEditor.setRange(section.tailPosition()); - }); + postEditor.setRange(section.tailPosition()) + }) } function deleteToEndOfSection(editor) { - let { range } = editor; + let { range } = editor if (range.isCollapsed) { - let { head, head: { section } } = range; - range = head.toRange(section.tailPosition()); + let { + head, + head: { section }, + } = range + range = head.toRange(section.tailPosition()) } editor.run(postEditor => { - let nextPosition = postEditor.deleteRange(range); - postEditor.setRange(nextPosition); - }); + let nextPosition = postEditor.deleteRange(range) + postEditor.setRange(nextPosition) + }) } -export 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); +export const DEFAULT_KEY_COMMANDS = [ + { + str: 'META+B', + run(editor) { + editor.toggleMarkup('strong') + }, }, - -}, { - 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()); - } -}]; + { + 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); + 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); + const upperCharacter = character.toUpperCase() + const special = specialCharacterToCode(upperCharacter) if (special) { - return 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); + assert(`Only 1 character can be used in a key command str (got "${character}")`, character.length === 1) + return upperCharacter.charCodeAt(0) } } export function buildKeyCommand(keyCommand) { - let { str } = keyCommand; + let { str } = keyCommand if (!str) { - return keyCommand; + return keyCommand } - assert('[deprecation] Key commands no longer use the `modifier` property', - !keyCommand.modifier); + assert('[deprecation] Key commands no longer use the `modifier` property', !keyCommand.modifier) - let [character, ...modifierNames] = str.split('+').reverse(); + let [character, ...modifierNames] = str.split('+').reverse() - keyCommand.modifierMask = modifierNamesToMask(modifierNames); - keyCommand.code = characterToCode(character); + keyCommand.modifierMask = modifierNamesToMask(modifierNames) + keyCommand.code = characterToCode(character) - return keyCommand; + return keyCommand } export function validateKeyCommand(keyCommand) { - return !!keyCommand.code && !!keyCommand.run; + return !!keyCommand.code && !!keyCommand.run } export function findKeyCommands(keyCommands, keyEvent) { - const key = Key.fromEvent(keyEvent); + const key = Key.fromEvent(keyEvent) - return filter(keyCommands, ({modifierMask, code}) => { - return key.keyCode === code && key.modifierMask === modifierMask; - }); + return filter(keyCommands, ({ modifierMask, code }) => { + return key.keyCode === code && key.modifierMask === modifierMask + }) } diff --git a/src/js/editor/mutation-handler.js b/src/js/editor/mutation-handler.js index d93476a66..a72dc5fb1 100644 --- a/src/js/editor/mutation-handler.js +++ b/src/js/editor/mutation-handler.js @@ -1,69 +1,69 @@ -import Set from 'mobiledoc-kit/utils/set'; -import { forEach, filter } from 'mobiledoc-kit/utils/array-utils'; -import assert from 'mobiledoc-kit/utils/assert'; -import { containsNode } from 'mobiledoc-kit/utils/dom-utils'; +import Set from 'mobiledoc-kit/utils/set' +import { forEach, filter } from 'mobiledoc-kit/utils/array-utils' +import assert from 'mobiledoc-kit/utils/assert' +import { containsNode } from 'mobiledoc-kit/utils/dom-utils' const MUTATION = { NODES_CHANGED: 'childList', - CHARACTER_DATA: 'characterData' -}; + CHARACTER_DATA: 'characterData', +} export default 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); - }); + 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(); + this.startObserving() } destroy() { - this.stopObserving(); - this._observer = null; + this.stopObserving() + this._observer = null } suspendObservation(callback) { - this.stopObserving(); - callback(); - this.startObserving(); + this.stopObserving() + callback() + this.startObserving() } stopObserving() { if (this._isObserving) { - this._isObserving = false; - this._observer.disconnect(); + this._isObserving = false + this._observer.disconnect() } } startObserving() { if (!this._isObserving) { - let { editor } = this; - assert('Cannot observe un-rendered editor', editor.hasRendered); + let { editor } = this + assert('Cannot observe un-rendered editor', editor.hasRendered) - this._isObserving = true; - this.renderTree = editor._renderTree; + this._isObserving = true + this.renderTree = editor._renderTree this._observer.observe(editor.element, { characterData: true, childList: true, - subtree: true - }); + subtree: true, + }) } } reparsePost() { - this.editor._reparsePost(); + this.editor._reparsePost() } reparseSections(sections) { - this.editor._reparseSections(sections); + this.editor._reparseSections(sections) } /** @@ -80,77 +80,76 @@ export default class MutationHandler { * * if no section, reparse all (and break) */ _handleMutations(mutations) { - let reparsePost = false; - let sections = new Set(); + let reparsePost = false + let sections = new Set() for (let i = 0; i < mutations.length; i++) { if (reparsePost) { - break; + break } - let nodes = this._findTargetNodes(mutations[i]); + let nodes = this._findTargetNodes(mutations[i]) - for (let j=0; j < nodes.length; j++) { - let node = nodes[j]; - let renderNode = this._findRenderNodeFromNode(node); + 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); + let section = this._findSectionFromRenderNode(renderNode) if (section) { - sections.add(section); + sections.add(section) } else { - reparsePost = true; + reparsePost = true } } } else { - reparsePost = true; - break; + reparsePost = true + break } } } if (reparsePost) { - this.logger.log(`reparsePost (${mutations.length} mutations)`); - this.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()); + this.logger.log(`reparse ${sections.length} sections (${mutations.length} mutations)`) + this.reparseSections(sections.toArray()) } } _findTargetNodes(mutation) { - let nodes = []; + let nodes = [] switch (mutation.type) { case MUTATION.CHARACTER_DATA: - nodes.push(mutation.target); - break; + nodes.push(mutation.target) + break case MUTATION.NODES_CHANGED: - forEach(mutation.addedNodes, n => nodes.push(n)); + forEach(mutation.addedNodes, n => nodes.push(n)) if (mutation.removedNodes.length) { - nodes.push(mutation.target); + nodes.push(mutation.target) } - break; + break } - let element = this.editor.element; - let attachedNodes = filter(nodes, node => containsNode(element, node)); - return attachedNodes; + 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; - }); + return this.renderTree.findRenderNodeFromElement(node, rn => { + return rn.postNode.isSection + }) } _findRenderNodeFromNode(node) { - return this.renderTree.findRenderNodeFromElement(node); + return this.renderTree.findRenderNodeFromElement(node) } _findSectionFromRenderNode(renderNode) { - let sectionRenderNode = this._findSectionRenderNodeFromNode(renderNode.element); - return sectionRenderNode && sectionRenderNode.postNode; + let sectionRenderNode = this._findSectionRenderNodeFromNode(renderNode.element) + return sectionRenderNode && sectionRenderNode.postNode } - } diff --git a/src/js/editor/post.js b/src/js/editor/post.js index 4245afbe6..181e5a1e0 100644 --- a/src/js/editor/post.js +++ b/src/js/editor/post.js @@ -1,43 +1,43 @@ -import Position from '../utils/cursor/position'; -import Range from 'mobiledoc-kit/utils/cursor/range'; -import { detect, forEach, reduce, filter, values, commonItems } from '../utils/array-utils'; -import { DIRECTION } from '../utils/key'; -import LifecycleCallbacks from '../models/lifecycle-callbacks'; -import assert from '../utils/assert'; -import { normalizeTagName } from '../utils/dom-utils'; -import PostInserter from './post/post-inserter'; -import deprecate from 'mobiledoc-kit/utils/deprecate'; -import toRange from 'mobiledoc-kit/utils/to-range'; - -const { FORWARD, BACKWARD } = DIRECTION; +import Position from '../utils/cursor/position' +import Range from 'mobiledoc-kit/utils/cursor/range' +import { detect, forEach, reduce, filter, values, commonItems } from '../utils/array-utils' +import { DIRECTION } from '../utils/key' +import LifecycleCallbacks from '../models/lifecycle-callbacks' +import assert from '../utils/assert' +import { normalizeTagName } from '../utils/dom-utils' +import PostInserter from './post/post-inserter' +import deprecate from 'mobiledoc-kit/utils/deprecate' +import toRange from 'mobiledoc-kit/utils/to-range' + +const { FORWARD, BACKWARD } = DIRECTION function isListSectionTagName(tagName) { - return tagName === 'ul' || tagName === 'ol'; + return tagName === 'ul' || tagName === 'ol' } function shrinkRange(range) { - const { head, tail } = range; + const { head, tail } = range if (tail.offset === 0 && head.section !== tail.section) { - range.tail = new Position(tail.section.prev, tail.section.prev.length); + range.tail = new Position(tail.section.prev, tail.section.prev.length) } - return range; + return range } const CALLBACK_QUEUES = { BEFORE_COMPLETE: 'beforeComplete', COMPLETE: 'complete', - AFTER_COMPLETE: 'afterComplete' -}; + 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 -}; + DELETE: 2, +} /** * The PostEditor is used to modify a post. It should not be instantiated directly. @@ -57,33 +57,33 @@ class PostEditor { * @private */ constructor(editor) { - this.editor = editor; - this.builder = this.editor.builder; - this._callbacks = new LifecycleCallbacks(values(CALLBACK_QUEUES)); + this.editor = editor + this.builder = this.editor.builder + this._callbacks = new LifecycleCallbacks(values(CALLBACK_QUEUES)) - this._didComplete = false; - this.editActionTaken = null; + this._didComplete = false + this.editActionTaken = null - this._renderRange = () => this.editor.selectRange(this._range); - this._postDidChange = () => this.editor._postDidChange(); - this._rerender = () => this.editor.rerender(); + this._renderRange = () => this.editor.selectRange(this._range) + this._postDidChange = () => this.editor._postDidChange() + this._rerender = () => this.editor.rerender() } addCallback(...args) { - this._callbacks.addCallback(...args); + this._callbacks.addCallback(...args) } addCallbackOnce(...args) { - this._callbacks.addCallbackOnce(...args); + this._callbacks.addCallbackOnce(...args) } runCallbacks(...args) { - this._callbacks.runCallbacks(...args); + this._callbacks.runCallbacks(...args) } begin() { // cache the editor's range - this._range = this.editor.range; + this._range = this.editor.range } /** @@ -105,12 +105,12 @@ class PostEditor { * @public */ setRange(range) { - range = toRange(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); + this._range = range + this.scheduleAfterRender(this._renderRange, true) } /** @@ -129,61 +129,65 @@ class PostEditor { * @public */ deleteRange(range) { - assert("Must pass MobiledocKit Range to `deleteRange`", range instanceof Range); + assert('Must pass MobiledocKit Range to `deleteRange`', range instanceof Range) - this.editActionTaken = EDIT_ACTIONS.DELETE; + this.editActionTaken = EDIT_ACTIONS.DELETE let { - head, head: {section: headSection}, - tail, tail: {section: tailSection} - } = range; + head, + head: { section: headSection }, + tail, + tail: { section: tailSection }, + } = range - let { editor: { post } } = this; + let { + editor: { post }, + } = this if (headSection === tailSection) { - return this.cutSection(headSection, head, tail); + return this.cutSection(headSection, head, tail) } - let nextSection = headSection.nextLeafSection(); + let nextSection = headSection.nextLeafSection() - let nextPos = this.cutSection(headSection, head, headSection.tailPosition()); + let nextPos = this.cutSection(headSection, head, headSection.tailPosition()) // cutSection can replace the section, so re-read headSection here - headSection = nextPos.section; + headSection = nextPos.section // Remove sections in the middle of the range while (nextSection !== tailSection) { - let tmp = nextSection; - nextSection = nextSection.nextLeafSection(); - this.removeSection(tmp); + let tmp = nextSection + nextSection = nextSection.nextLeafSection() + this.removeSection(tmp) } - let tailPos = this.cutSection(tailSection, tailSection.headPosition(), tail); + let tailPos = this.cutSection(tailSection, tailSection.headPosition(), tail) // cutSection can replace the section, so re-read tailSection here - tailSection = tailPos.section; + tailSection = tailPos.section if (tailSection.isBlank) { - this.removeSection(tailSection); + 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); + headSection.join(tailSection) + this._markDirty(headSection) + this.removeSection(tailSection) } else if (headSection.isBlank) { - this.removeSection(headSection); - nextPos = tailPos; + this.removeSection(headSection) + nextPos = tailPos } } if (post.isBlank) { - post.sections.append(this.builder.createMarkupSection('p')); - nextPos = post.headPosition(); + post.sections.append(this.builder.createMarkupSection('p')) + nextPos = post.headPosition() } - return nextPos; + return nextPos } /** @@ -202,34 +206,35 @@ class PostEditor { * @private */ cutSection(section, head, tail) { - assert('Must pass head position and tail position to `cutSection`', - head instanceof Position && tail instanceof Position); - assert('Must pass positions within same section to `cutSection`', - head.section === tail.section); + assert( + 'Must pass head position and tail position to `cutSection`', + head instanceof Position && tail instanceof Position + ) + assert('Must pass positions within same section to `cutSection`', head.section === tail.section) if (section.isBlank || head.isEqual(tail)) { - return head; + return head } if (section.isCardSection) { if (head.isHead() && tail.isTail()) { - let newSection = this.builder.createMarkupSection(); - this.replaceSection(section, newSection); - return newSection.headPosition(); + let newSection = this.builder.createMarkupSection() + this.replaceSection(section, newSection) + return newSection.headPosition() } else { - return tail; + return tail } } - let range = head.toRange(tail); - this.splitMarkers(range).forEach(m => this.removeMarker(m)); + let range = head.toRange(tail) + this.splitMarkers(range).forEach(m => this.removeMarker(m)) - return head; + return head } _coalesceMarkers(section) { if (section.isMarkerable) { - this._removeBlankMarkers(section); - this._joinSimilarMarkers(section); + this._removeBlankMarkers(section) + this._joinSimilarMarkers(section) } } @@ -237,129 +242,120 @@ class PostEditor { 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; + let marker = section.markers.head + let nextMarker while (marker && marker.next) { - nextMarker = marker.next; + nextMarker = marker.next if (marker.canJoin(nextMarker)) { - nextMarker.value = marker.value + nextMarker.value; - this._markDirty(nextMarker); - this.removeMarker(marker); + nextMarker.value = marker.value + nextMarker.value + this._markDirty(nextMarker) + this.removeMarker(marker) } - marker = nextMarker; + marker = nextMarker } } removeMarker(marker) { - this._scheduleForRemoval(marker); + this._scheduleForRemoval(marker) if (marker.section) { - this._markDirty(marker.section); - marker.section.markers.remove(marker); + this._markDirty(marker.section) + marker.section.markers.remove(marker) } } _scheduleForRemoval(postNode) { if (postNode.renderNode) { - postNode.renderNode.scheduleForRemoval(); + postNode.renderNode.scheduleForRemoval() - this.scheduleRerender(); - this.scheduleDidUpdate(); + this.scheduleRerender() + this.scheduleDidUpdate() } - let removedAdjacentToList = (postNode.prev && postNode.prev.isListSection) || - (postNode.next && postNode.next.isListSection); + let removedAdjacentToList = + (postNode.prev && postNode.prev.isListSection) || (postNode.next && postNode.next.isListSection) if (removedAdjacentToList) { - this.addCallback( - CALLBACK_QUEUES.BEFORE_COMPLETE, - () => this._joinContiguousListSections() - ); + this.addCallback(CALLBACK_QUEUES.BEFORE_COMPLETE, () => this._joinContiguousListSections()) } } _joinContiguousListSections() { - let { post } = this.editor; - let range = this._range; - let prev; - let groups = []; - let currentGroup; + 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; + let updatedHead = null forEach(post.sections, section => { - if (prev && - prev.isListSection && - section.isListSection && - prev.tagName === section.tagName) { - - currentGroup = currentGroup || [prev]; - currentGroup.push(section); + if (prev && prev.isListSection && section.isListSection && prev.tagName === section.tagName) { + currentGroup = currentGroup || [prev] + currentGroup.push(section) } else { if (currentGroup) { - groups.push(currentGroup); + groups.push(currentGroup) } - currentGroup = null; + currentGroup = null } - prev = section; - }); + prev = section + }) if (currentGroup) { - groups.push(currentGroup); + groups.push(currentGroup) } forEach(groups, group => { - let list = group[0]; + let list = group[0] forEach(group, listSection => { if (listSection === list) { - return; + return } - let currentHead = range.head; - let prevPosition; + 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(); + if (!range.isBlank && currentHead.section.isListItem && currentHead.section.parent === listSection) { + prevPosition = list.tailPosition() } - this._joinListSections(list, listSection); + this._joinListSections(list, listSection) if (prevPosition) { - updatedHead = prevPosition.move(FORWARD); + updatedHead = prevPosition.move(FORWARD) } - }); - }); + }) + }) if (updatedHead) { - this.setRange(updatedHead); + this.setRange(updatedHead) } } _joinListSections(baseList, nextList) { - baseList.join(nextList); - this._markDirty(baseList); - this.removeSection(nextList); + baseList.join(nextList) + this._markDirty(baseList) + this.removeSection(nextList) } _markDirty(postNode) { if (postNode.renderNode) { - postNode.renderNode.markDirty(); + postNode.renderNode.markDirty() - this.scheduleRerender(); - this.scheduleDidUpdate(); + this.scheduleRerender() + this.scheduleDidUpdate() } if (postNode.section) { - this._markDirty(postNode.section); + this._markDirty(postNode.section) } if (postNode.isMarkerable) { - this.addCallback( - CALLBACK_QUEUES.BEFORE_COMPLETE, () => this._coalesceMarkers(postNode)); + this.addCallback(CALLBACK_QUEUES.BEFORE_COMPLETE, () => this._coalesceMarkers(postNode)) } } @@ -370,9 +366,11 @@ class PostEditor { * @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'}); + 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' }) } /** @@ -397,29 +395,29 @@ class PostEditor { * @param {String} [options.unit="char"] The unit of deletion ("word" or "char") * @return {Position} */ - deleteAtPosition(position, direction=DIRECTION.BACKWARD, {unit}={unit: 'char'}) { + deleteAtPosition(position, direction = DIRECTION.BACKWARD, { unit } = { unit: 'char' }) { if (direction === DIRECTION.BACKWARD) { - return this._deleteAtPositionBackward(position, unit); + return this._deleteAtPositionBackward(position, unit) } else { - return this._deleteAtPositionForward(position, unit); + return this._deleteAtPositionForward(position, unit) } } _deleteAtPositionBackward(position, unit) { if (position.isHead() && position.section.isListItem) { - this.toggleSection('p', position); - return this._range.head; + this.toggleSection('p', position) + return this._range.head } else { - let prevPosition = unit === 'word' ? position.moveWord(BACKWARD) : position.move(BACKWARD); - let range = prevPosition.toRange(position); - return this.deleteRange(range); + let prevPosition = unit === 'word' ? position.moveWord(BACKWARD) : position.move(BACKWARD) + let range = prevPosition.toRange(position) + return this.deleteRange(range) } } _deleteAtPositionForward(position, unit) { - let nextPosition = unit === 'word' ? position.moveWord(FORWARD) : position.move(FORWARD); - let range = position.toRange(nextPosition); - return this.deleteRange(range); + let nextPosition = unit === 'word' ? position.moveWord(FORWARD) : position.move(FORWARD) + let range = position.toRange(nextPosition) + return this.deleteRange(range) } /** @@ -441,18 +439,18 @@ class PostEditor { * @private */ splitMarkers(range) { - const { post } = this.editor; - const { head, tail } = range; + const { post } = this.editor + const { head, tail } = range - this.splitSectionMarkerAtOffset(head.section, head.offset); - this.splitSectionMarkerAtOffset(tail.section, tail.offset); + this.splitSectionMarkerAtOffset(head.section, head.offset) + this.splitSectionMarkerAtOffset(tail.section, tail.offset) - return post.markersContainedByRange(range); + return post.markersContainedByRange(range) } splitSectionMarkerAtOffset(section, offset) { - const edit = section.splitMarkerAtOffset(offset); - edit.removed.forEach(m => this.removeMarker(m)); + const edit = section.splitMarkerAtOffset(offset) + edit.removed.forEach(m => this.removeMarker(m)) } /** @@ -476,31 +474,31 @@ class PostEditor { * @public */ splitSection(position) { - const { section } = position; + const { section } = position if (section.isCardSection) { - return this._splitCardSection(section, position); + return this._splitCardSection(section, position) } else if (section.isListItem) { - let isLastAndBlank = section.isBlank && !section.next; + 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); + 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]; + return [null, blank] } else { - let [pre, post] = this._splitListItem(section, position); - return [pre, post]; + 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); + let splitSections = section.splitAtPosition(position) + splitSections.forEach(s => this._coalesceMarkers(s)) + this._replaceSection(section, splitSections) - return splitSections; + return splitSections } } @@ -511,26 +509,25 @@ class PostEditor { * @private */ _splitCardSection(cardSection, position) { - let { offset } = position; - assert('Cards section must be split at offset 0 or 1', - offset === 0 || offset === 1); + 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; + let newSection = this.builder.createMarkupSection() + let nextSection + let surroundingSections if (offset === 0) { - nextSection = cardSection; - surroundingSections = [newSection, cardSection]; + nextSection = cardSection + surroundingSections = [newSection, cardSection] } else { - nextSection = cardSection.next; - surroundingSections = [cardSection, newSection]; + nextSection = cardSection.next + surroundingSections = [cardSection, newSection] } - let collection = this.editor.post.sections; - this.insertSectionBefore(collection, newSection, nextSection); + let collection = this.editor.post.sections + this.insertSectionBefore(collection, newSection, nextSection) - return surroundingSections; + return surroundingSections } /** @@ -542,17 +539,17 @@ class PostEditor { replaceSection(section, newSection) { if (!section) { // FIXME should a falsy section be a valid argument? - this.insertSectionBefore(this.editor.post.sections, newSection, null); + this.insertSectionBefore(this.editor.post.sections, newSection, null) } else { - this._replaceSection(section, [newSection]); + this._replaceSection(section, [newSection]) } } moveSectionBefore(collection, renderedSection, beforeSection) { - const newSection = renderedSection.clone(); - this.removeSection(renderedSection); - this.insertSectionBefore(collection, newSection, beforeSection); - return newSection; + const newSection = renderedSection.clone() + this.removeSection(renderedSection) + this.insertSectionBefore(collection, newSection, beforeSection) + return newSection } /** @@ -560,14 +557,14 @@ class PostEditor { * @public */ moveSectionUp(renderedSection) { - const isFirst = !renderedSection.prev; + const isFirst = !renderedSection.prev if (isFirst) { - return renderedSection; + return renderedSection } - const collection = renderedSection.parent.sections; - const beforeSection = renderedSection.prev; - return this.moveSectionBefore(collection, renderedSection, beforeSection); + const collection = renderedSection.parent.sections + const beforeSection = renderedSection.prev + return this.moveSectionBefore(collection, renderedSection, beforeSection) } /** @@ -575,14 +572,14 @@ class PostEditor { * @public */ moveSectionDown(renderedSection) { - const isLast = !renderedSection.next; + const isLast = !renderedSection.next if (isLast) { - return renderedSection; + return renderedSection } - const beforeSection = renderedSection.next.next; - const collection = renderedSection.parent.sections; - return this.moveSectionBefore(collection, renderedSection, beforeSection); + const beforeSection = renderedSection.next.next + const collection = renderedSection.parent.sections + return this.moveSectionBefore(collection, renderedSection, beforeSection) } /** @@ -595,28 +592,27 @@ class PostEditor { * @public */ insertMarkers(position, markers) { - let { section, offset } = position; - assert('Cannot insert markers at non-markerable position', - section.isMarkerable); + let { section, offset } = position + assert('Cannot insert markers at non-markerable position', section.isMarkerable) - this.editActionTaken = EDIT_ACTIONS.INSERT_TEXT; + this.editActionTaken = EDIT_ACTIONS.INSERT_TEXT - let edit = section.splitMarkerAtOffset(offset); - edit.removed.forEach(marker => this._scheduleForRemoval(marker)); + let edit = section.splitMarkerAtOffset(offset) + edit.removed.forEach(marker => this._scheduleForRemoval(marker)) - let prevMarker = section.markerBeforeOffset(offset); + let prevMarker = section.markerBeforeOffset(offset) markers.forEach(marker => { - section.markers.insertAfter(marker, prevMarker); - offset += marker.length; - prevMarker = marker; - }); + section.markers.insertAfter(marker, prevMarker) + offset += marker.length + prevMarker = marker + }) - this._coalesceMarkers(section); - this._markDirty(section); + this._coalesceMarkers(section) + this._markDirty(section) - let nextPosition = section.toPosition(offset); - this.setRange(nextPosition); - return nextPosition; + let nextPosition = section.toPosition(offset) + this.setRange(nextPosition) + return nextPosition } /** @@ -628,11 +624,13 @@ class PostEditor { * @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]); + insertTextWithMarkup(position, text, markups = []) { + let { section } = position + if (!section.isMarkerable) { + return + } + let marker = this.builder.createMarker(text, markups) + return this.insertMarkers(position, [marker]) } /** @@ -644,27 +642,29 @@ class PostEditor { * @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); + 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 nextSection = section.next + let collection = section.parent.sections - let nextNewSection = newSections[0]; + 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; + collection = section.parent.parent.sections + nextSection = section.parent.next } - newSections.forEach(s => this.insertSectionBefore(collection, s, nextSection)); - this.removeSection(section); + newSections.forEach(s => this.insertSectionBefore(collection, s, nextSection)) + this.removeSection(section) } /** @@ -687,9 +687,11 @@ class PostEditor { * @public */ addMarkupToRange(range, markup) { - if (range.isCollapsed) { return; } + if (range.isCollapsed) { + return + } - let markers = this.splitMarkers(range); + 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 @@ -701,15 +703,19 @@ class PostEditor { // 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; + 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); - }); + marker.addMarkupAtIndex(markup, indexToInsert) + this._markDirty(marker) + }) } } @@ -733,12 +739,14 @@ class PostEditor { * @private */ removeMarkupFromRange(range, markupOrMarkupCallback) { - if (range.isCollapsed) { return; } + if (range.isCollapsed) { + return + } this.splitMarkers(range).forEach(marker => { - marker.removeMarkup(markupOrMarkupCallback); - this._markDirty(marker); - }); + marker.removeMarkup(markupOrMarkupCallback) + this._markDirty(marker) + }) } /** @@ -764,23 +772,22 @@ class PostEditor { * @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; + 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); + 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); + this.removeMarkupFromRange(range, hasMarkup) } else { - this.addMarkupToRange(range, markup); + this.addMarkupToRange(range, markup) } - this.setRange(range); + this.setRange(range) } /** @@ -794,48 +801,48 @@ class PostEditor { * Defaults to the current editor range. * @public */ - toggleSection(sectionTagName, range=this._range) { - range = shrinkRange(toRange(range)); + toggleSection(sectionTagName, range = this._range) { + range = shrinkRange(toRange(range)) - sectionTagName = normalizeTagName(sectionTagName); - let { post } = this.editor; + sectionTagName = normalizeTagName(sectionTagName) + let { post } = this.editor - let everySectionHasTagName = true; + let everySectionHasTagName = true post.walkMarkerableSections(range, section => { if (!this._isSameSectionType(section, sectionTagName)) { - everySectionHasTagName = false; + everySectionHasTagName = false } - }); + }) - let tagName = everySectionHasTagName ? 'p' : sectionTagName; - let sectionTransformations = []; + let tagName = everySectionHasTagName ? 'p' : sectionTagName + let sectionTransformations = [] post.walkMarkerableSections(range, section => { - let changedSection = this.changeSectionTagName(section, tagName); + let changedSection = this.changeSectionTagName(section, tagName) sectionTransformations.push({ from: section, - to: changedSection - }); - }); + to: changedSection, + }) + }) - let nextRange = this._determineNextRangeAfterToggleSection(range, sectionTransformations); - this.setRange(nextRange); + 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; + return from === range.headSection + }).to let changedTailSection = detect(sectionTransformations, ({ from }) => { - return from === range.tailSection; - }).to; + 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(); + return sectionTransformations[0].to.headPosition().toRange() } else { return Range.create( changedHeadSection, @@ -843,53 +850,51 @@ class PostEditor { changedTailSection, range.tailSectionOffset, range.direction - ); + ) } } else { - return range; + return range } } - setAttribute(key, value, range=this._range) { + setAttribute(key, value, range = this._range) { this._mutateAttribute(key, range, (section, attribute) => { if (section.getAttribute(attribute) !== value) { - section.setAttribute(attribute, value); - return true; + section.setAttribute(attribute, value) + return true } - }); + }) } - removeAttribute(key, range=this._range) { + removeAttribute(key, range = this._range) { this._mutateAttribute(key, range, (section, attribute) => { if (section.hasAttribute(attribute)) { - section.removeAttribute(attribute); - return true; + section.removeAttribute(attribute) + return true } - }); + }) } _mutateAttribute(key, range, cb) { - range = toRange(range); - let { post } = this.editor; - let attribute = `data-md-${key}`; + range = toRange(range) + let { post } = this.editor + let attribute = `data-md-${key}` post.walkMarkerableSections(range, section => { if (section.isListItem) { - section = section.parent; + section = section.parent } if (cb(section, attribute) === true) { - this._markDirty(section); + this._markDirty(section) } - }); + }) - this.setRange(range); + this.setRange(range) } _isSameSectionType(section, sectionTagName) { - return section.isListItem ? - section.parent.tagName === sectionTagName : - section.tagName === sectionTagName; + return section.isListItem ? section.parent.tagName === sectionTagName : section.tagName === sectionTagName } /** @@ -897,17 +902,16 @@ class PostEditor { * @private */ changeSectionTagName(section, newTagName) { - assert('Cannot pass non-markerable section to `changeSectionTagName`', - section.isMarkerable); + assert('Cannot pass non-markerable section to `changeSectionTagName`', section.isMarkerable) if (isListSectionTagName(newTagName)) { - return this._changeSectionToListItem(section, newTagName); + return this._changeSectionToListItem(section, newTagName) } else if (section.isListItem) { - return this._changeSectionFromListItem(section, newTagName); + return this._changeSectionFromListItem(section, newTagName) } else { - section.tagName = newTagName; - this._markDirty(section); - return section; + section.tagName = newTagName + this._markDirty(section) + return section } } @@ -921,24 +925,23 @@ class PostEditor { * @private */ _splitListItem(item, position) { - let { section, offset } = position; - assert('Cannot split list item at position that does not include item', - item === section); + 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(); + item.splitMarkerAtOffset(offset) + let prevMarker = item.markerBeforeOffset(offset) + let preItem = this.builder.createListItem(), + postItem = this.builder.createListItem() - let currentItem = preItem; + let currentItem = preItem item.markers.forEach(marker => { - currentItem.markers.append(marker.clone()); + currentItem.markers.append(marker.clone()) if (marker === prevMarker) { - currentItem = postItem; + currentItem = postItem } - }); - this._replaceSection(item, [preItem, postItem]); - return [preItem, postItem]; + }) + this._replaceSection(item, [preItem, postItem]) + return [preItem, postItem] } /** @@ -952,37 +955,35 @@ class PostEditor { * @private */ _splitListAtPosition(list, position) { - assert('Cannot split list at position not in list', - position.section.parent === list); + assert('Cannot split list at position not in list', position.section.parent === list) - let positionIsMiddle = !position.isHead() && !position.isTail(); + let positionIsMiddle = !position.isHead() && !position.isTail() if (positionIsMiddle) { - let item = position.section; - let [pre,] = - this._splitListItem(item, position); - position = pre.tailPosition(); + 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 preList = this.builder.createListSection(list.tagName) + let postList = this.builder.createListSection(list.tagName) - let preItem = position.section; - let currentList = preList; + 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 = postList } - currentList.items.append(item.clone()); + currentList.items.append(item.clone()) // If we just appended the preItem, append the remaining items to the postList if (item === preItem) { - currentList = postList; + currentList = postList } - }); + }) - this._replaceSection(list, [preList, postList]); - return [preList, postList]; + this._replaceSection(list, [preList, postList]) + return [preList, postList] } /** @@ -995,80 +996,78 @@ class PostEditor { * @private */ _splitListAtItem(list, item) { - let next = list; - let prev = this.builder.createListSection(next.tagName, [], next.attributes); - let mid = this.builder.createListSection(next.tagName); + let next = list + let prev = this.builder.createListSection(next.tagName, [], next.attributes) + let mid = this.builder.createListSection(next.tagName) - let addToPrev = true; + 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(); + let items = next.items.toArray() items.forEach(i => { - let listToAppend; + let listToAppend if (i === item) { - addToPrev = false; - listToAppend = mid; + addToPrev = false + listToAppend = mid } else if (addToPrev) { - listToAppend = prev; + listToAppend = prev } else { - return; // break after iterating prev and mid parts of the list + 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); + 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); + 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; + ;[prev, next].forEach(_list => { + let isAttached = !!_list.parent if (_list.isBlank && isAttached) { - this.removeSection(_list); + this.removeSection(_list) } - }); - }); + }) + }) - return [prev, mid, next]; + return [prev, mid, next] } _changeSectionFromListItem(section, newTagName) { - assert('Must pass list item to `_changeSectionFromListItem`', - section.isListItem); + assert('Must pass list item to `_changeSectionFromListItem`', section.isListItem) - let listSection = section.parent; - let markupSection = this.builder.createMarkupSection(newTagName); - markupSection.join(section); + 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; + let [, mid] = this._splitListAtItem(listSection, section) + this.replaceSection(mid, markupSection) + return markupSection } _changeSectionToListItem(section, newTagName) { - let isAlreadyCorrectListItem = section.isListItem && - section.parent.tagName === newTagName; + let isAlreadyCorrectListItem = section.isListItem && section.parent.tagName === newTagName if (isAlreadyCorrectListItem) { - return section; + return section } - let listSection = this.builder.createListSection(newTagName); - listSection.join(section); + let listSection = this.builder.createListSection(newTagName) + listSection.join(section) - let sectionToReplace; + let sectionToReplace if (section.isListItem) { - let [, mid,] = this._splitListAtItem(section.parent, section); - sectionToReplace = mid; + let [, mid] = this._splitListAtItem(section.parent, section) + sectionToReplace = mid } else { - sectionToReplace = section; + sectionToReplace = section } - this.replaceSection(sectionToReplace, listSection); - return listSection; + this.replaceSection(sectionToReplace, listSection) + return listSection } /** @@ -1092,8 +1091,8 @@ class PostEditor { * @public */ insertSectionBefore(collection, section, beforeSection) { - collection.insertBefore(section, beforeSection); - this._markDirty(section.parent); + collection.insertBefore(section, beforeSection) + this._markDirty(section.parent) } /** @@ -1103,11 +1102,11 @@ class PostEditor { * @public */ insertSection(section) { - const activeSection = this.editor.activeSection; - const nextSection = activeSection && activeSection.next; + const activeSection = this.editor.activeSection + const nextSection = activeSection && activeSection.next - const collection = this.editor.post.sections; - this.insertSectionBefore(collection, section, nextSection); + const collection = this.editor.post.sections + this.insertSectionBefore(collection, section, nextSection) } /** @@ -1116,7 +1115,7 @@ class PostEditor { * @public */ insertSectionAtEnd(section) { - this.insertSectionBefore(this.editor.post.sections, section, null); + this.insertSectionBefore(this.editor.post.sections, section, null) } /** @@ -1126,10 +1125,10 @@ class PostEditor { * @private */ insertPost(position, newPost) { - let post = this.editor.post; - let inserter = new PostInserter(this, post); - let nextPosition = inserter.insert(position, newPost); - return nextPosition; + let post = this.editor.post + let inserter = new PostInserter(this, post) + let nextPosition = inserter.insert(position, newPost) + return nextPosition } /** @@ -1147,37 +1146,37 @@ class PostEditor { * @public */ removeSection(section) { - let parent = section.parent; - this._scheduleForRemoval(section); - parent.sections.remove(section); + let parent = section.parent + this._scheduleForRemoval(section) + parent.sections.remove(section) if (parent.isListSection) { - this._scheduleListRemovalIfEmpty(parent); + this._scheduleListRemovalIfEmpty(parent) } } removeAllSections() { this.editor.post.sections.toArray().forEach(section => { - this.removeSection(section); - }); + this.removeSection(section) + }) } migrateSectionsFromPost(post) { post.sections.toArray().forEach(section => { - post.sections.remove(section); - this.insertSectionBefore(this.editor.post.sections, section, null); - }); + 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; + let isAttached = !!listSection.parent if (isAttached && listSection.isBlank) { - this.removeSection(listSection); + this.removeSection(listSection) } - }); + }) } /** @@ -1187,13 +1186,12 @@ class PostEditor { * @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); + 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); + this.addCallbackOnce(CALLBACK_QUEUES.COMPLETE, callback) } else { - this.addCallback(CALLBACK_QUEUES.COMPLETE, callback); + this.addCallback(CALLBACK_QUEUES.COMPLETE, callback) } } @@ -1206,7 +1204,7 @@ class PostEditor { * @public */ scheduleOnce(callback) { - this.schedule(callback, true); + this.schedule(callback, true) } /** @@ -1215,7 +1213,7 @@ class PostEditor { * @public */ scheduleRerender() { - this.scheduleOnce(this._rerender); + this.scheduleOnce(this._rerender) } /** @@ -1226,14 +1224,14 @@ class PostEditor { * @public */ scheduleDidUpdate() { - this.scheduleOnce(this._postDidChange); + this.scheduleOnce(this._postDidChange) } - scheduleAfterRender(callback, once=false) { + scheduleAfterRender(callback, once = false) { if (once) { - this.addCallbackOnce(CALLBACK_QUEUES.AFTER_COMPLETE, callback); + this.addCallbackOnce(CALLBACK_QUEUES.AFTER_COMPLETE, callback) } else { - this.addCallback(CALLBACK_QUEUES.AFTER_COMPLETE, callback); + this.addCallback(CALLBACK_QUEUES.AFTER_COMPLETE, callback) } } @@ -1244,25 +1242,25 @@ class PostEditor { * @private */ complete() { - assert('Post editing can only be completed once', !this._didComplete); + 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); + 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); + this.editor._editHistory.stepBackward(this) } redoLastChange() { - this.editor._editHistory.stepForward(this); + this.editor._editHistory.stepForward(this) } cancelSnapshot() { - this._shouldCancelSnapshot = true; + this._shouldCancelSnapshot = true } } -export default PostEditor; +export default PostEditor diff --git a/src/js/editor/post/post-inserter.js b/src/js/editor/post/post-inserter.js index c3b55583b..46bce5489 100644 --- a/src/js/editor/post/post-inserter.js +++ b/src/js/editor/post/post-inserter.js @@ -1,4 +1,4 @@ -import assert from 'mobiledoc-kit/utils/assert'; +import assert from 'mobiledoc-kit/utils/assert' import { MARKUP_SECTION_TYPE, LIST_SECTION_TYPE, @@ -6,196 +6,194 @@ import { CARD_TYPE, IMAGE_SECTION_TYPE, LIST_ITEM_TYPE, -} from 'mobiledoc-kit/models/types'; +} from 'mobiledoc-kit/models/types' const MARKERABLE = 'markerable', - NESTED_MARKERABLE = 'nested_markerable', - NON_MARKERABLE = 'non_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; + let { postEditor, post } = inserter + this.postEditor = postEditor + this._post = post + this.cursorPosition = cursorPosition + this.builder = this.postEditor.builder - this._hasInsertedFirstLeafSection = false; + this._hasInsertedFirstLeafSection = false } get cursorPosition() { - return this._cursorPosition; + return this._cursorPosition } set cursorPosition(position) { - this._cursorPosition = position; - this.postEditor.setRange(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); + let method = node.type + assert(`Cannot visit node of type ${node.type}`, !!this[method]) + this[method](node) } _canMergeSection(section) { if (this._hasInsertedFirstLeafSection) { - return false; + return false } else { - return this._isMarkerable && section.isMarkerable; + return this._isMarkerable && section.isMarkerable } } get _isMarkerable() { - return this.cursorSection.isMarkerable; + return this.cursorSection.isMarkerable } get cursorSection() { - return this.cursorPosition.section; + return this.cursorPosition.section } get cursorOffset() { - return this.cursorPosition.offset; + return this.cursorPosition.offset } get _isNested() { - return this.cursorSection.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); + let newSections = node.sections.map(s => s.clone()) + this._replaceSection(this.cursorSection, newSections) } else { - node.sections.forEach(section => this.visit(section)); + node.sections.forEach(section => this.visit(section)) } } [MARKUP_SECTION_TYPE](node) { - this[MARKERABLE](node); + this[MARKERABLE](node) } [LIST_SECTION_TYPE](node) { - let hasNext = !!node.next; - node.items.forEach(item => this.visit(item)); + let hasNext = !!node.next + node.items.forEach(item => this.visit(item)) if (this._isNested && hasNext) { - this._breakNestedAtCursor(); + this._breakNestedAtCursor() } } [LIST_ITEM_TYPE](node) { - this[NESTED_MARKERABLE](node); + this[NESTED_MARKERABLE](node) } [CARD_TYPE](node) { - this[NON_MARKERABLE](node); + this[NON_MARKERABLE](node) } [IMAGE_SECTION_TYPE](node) { - this[NON_MARKERABLE](node); + this[NON_MARKERABLE](node) } [NON_MARKERABLE](section) { if (this._isNested) { - this._breakNestedAtCursor(); + this._breakNestedAtCursor() } else if (!this.cursorSection.isBlank) { - this._breakAtCursor(); + this._breakAtCursor() } - this._insertLeafSection(section); + this._insertLeafSection(section) } [MARKERABLE](section) { if (this._canMergeSection(section)) { - this._mergeSection(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(); + this._breakAtCursor() // Advance the cursor to the head of the blank list item - let nextPosition = this.cursorSection.next.headPosition(); - this.cursorPosition = nextPosition; + let nextPosition = this.cursorSection.next.headPosition() + this.cursorPosition = nextPosition // Merge this section onto the list item - this._mergeSection(section); + this._mergeSection(section) } else { - this._breakAtCursor(); - this._insertLeafSection(section); + this._breakAtCursor() + this._insertLeafSection(section) } } [NESTED_MARKERABLE](section) { if (this._canMergeSection(section)) { - this._mergeSection(section); - return; + this._mergeSection(section) + return } - section = this._isNested ? section : this._wrapNestedSection(section); - this._breakAtCursor(); - this._insertLeafSection(section); + 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); + assert('Cannot call _breakNestedAtCursor if not nested', this._isNested) - let parent = this.cursorSection.parent; - let cursorAtEndOfList = this.cursorPosition.isEqual(parent.tailPosition()); + let parent = this.cursorSection.parent + let cursorAtEndOfList = this.cursorPosition.isEqual(parent.tailPosition()) if (cursorAtEndOfList) { - let blank = this.builder.createMarkupSection(); - this._insertSectionAfter(blank, parent); + let blank = this.builder.createMarkupSection() + this._insertSectionAfter(blank, parent) } else { - let [, blank,] = this._breakListAtCursor(); - this.cursorPosition = blank.tailPosition(); + let [, blank] = this._breakListAtCursor() + this.cursorPosition = blank.tailPosition() } } _breakListAtCursor() { - assert('Cannot _splitParentSection if cursor position is not nested', - this._isNested); + 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 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]; + 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; + 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; + 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); + let markers = section.markers.map(m => m.clone()) + let position = this.postEditor.insertMarkers(this.cursorPosition, markers) - this.cursorPosition = position; + 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; + return } else if (this._isMarkerable) { - this._breakMarkerableAtCursor(); + this._breakMarkerableAtCursor() } else { - this._breakNonMarkerableAtCursor(); + this._breakNonMarkerableAtCursor() } } @@ -203,84 +201,82 @@ class Visitor { // 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(); + 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); + let [pre] = this.postEditor.splitSection(this.cursorPosition) - this.cursorPosition = pre.tailPosition(); + 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); + 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); + 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.postEditor.insertSectionBefore(collection, section, reference) + }) + let lastSection = newSections[newSections.length - 1] - this.cursorPosition = lastSection.tailPosition(); + this.cursorPosition = lastSection.tailPosition() } _insertSectionBefore(section, reference) { - let collection = this.cursorSection.parent.sections; - this.postEditor.insertSectionBefore(collection, section, reference); + let collection = this.cursorSection.parent.sections + this.postEditor.insertSectionBefore(collection, section, reference) - this.cursorPosition = section.tailPosition(); + 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(); + 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()); + assert('Can only _insertLeafSection when cursor is at end of section', this.cursorPosition.isTail()) - this._hasInsertedFirstLeafSection = true; - section = section.clone(); + 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]); + 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]); + this._replaceSection(this.cursorSection.next, [section]) } else { - let reference = this.cursorSection.next; - this._insertSectionBefore(section, reference); + let reference = this.cursorSection.next + this._insertSectionBefore(section, reference) } } } export default class Inserter { constructor(postEditor, post) { - this.postEditor = postEditor; - this.post = post; + this.postEditor = postEditor + this.post = post } insert(cursorPosition, newPost) { - let visitor = new Visitor(this, cursorPosition); + let visitor = new Visitor(this, cursorPosition) if (!newPost.isBlank) { - visitor.visit(newPost); + visitor.visit(newPost) } - return visitor.cursorPosition; + return visitor.cursorPosition } } diff --git a/src/js/editor/selection-change-observer.js b/src/js/editor/selection-change-observer.js index 8c37411f4..c48a74538 100644 --- a/src/js/editor/selection-change-observer.js +++ b/src/js/editor/selection-change-observer.js @@ -1,99 +1,103 @@ -let instance; +let instance class SelectionChangeObserver { constructor() { - this.started = false; - this.listeners = []; - this.selection = {}; + this.started = false + this.listeners = [] + this.selection = {} } static getInstance() { if (!instance) { - instance = new SelectionChangeObserver(); + instance = new SelectionChangeObserver() } - return instance; + return instance } static addListener(listener) { - SelectionChangeObserver.getInstance().addListener(listener); + SelectionChangeObserver.getInstance().addListener(listener) } addListener(listener) { if (this.listeners.indexOf(listener) === -1) { - this.listeners.push(listener); - this.start(); + this.listeners.push(listener) + this.start() } } static removeListener(listener) { - SelectionChangeObserver.getInstance().removeListener(listener); + SelectionChangeObserver.getInstance().removeListener(listener) } removeListener(listener) { - let index = this.listeners.indexOf(listener); + let index = this.listeners.indexOf(listener) if (index !== -1) { - this.listeners.splice(index, 1); + this.listeners.splice(index, 1) if (this.listeners.length === 0) { - this.stop(); + this.stop() } } } start() { - if (this.started) { return; } - this.started = true; + if (this.started) { + return + } + this.started = true - this.poll(); + this.poll() } stop() { - this.started = false; - this.selection = {}; + this.started = false + this.selection = {} } notifyListeners(/* newSelection, prevSelection */) { this.listeners.forEach(listener => { - listener.selectionDidChange(...arguments); - }); + listener.selectionDidChange(...arguments) + }) } destroy() { - this.stop(); - this.listeners = []; + this.stop() + this.listeners = [] } getSelection() { - let selection = window.getSelection(); - let { anchorNode, focusNode, anchorOffset, focusOffset } = selection; - return { anchorNode, focusNode, anchorOffset, focusOffset }; + 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()); + this.update() + this.runNext(() => this.poll()) } } runNext(fn) { - window.requestAnimationFrame(fn); + window.requestAnimationFrame(fn) } update() { - let prevSelection = this.selection; - let curSelection = this.getSelection(); + let prevSelection = this.selection + let curSelection = this.getSelection() if (!this.selectionIsEqual(prevSelection, curSelection)) { - this.selection = curSelection; - this.notifyListeners(curSelection, prevSelection); + this.selection = curSelection + this.notifyListeners(curSelection, prevSelection) } } selectionIsEqual(s1, s2) { - return s1.anchorNode === s2.anchorNode && + return ( + s1.anchorNode === s2.anchorNode && s1.anchorOffset === s2.anchorOffset && s1.focusNode === s2.focusNode && - s1.focusOffset === s2.focusOffset; + s1.focusOffset === s2.focusOffset + ) } } -export default SelectionChangeObserver; +export default SelectionChangeObserver diff --git a/src/js/editor/selection-manager.js b/src/js/editor/selection-manager.js index 72a8beb2d..6bd80febe 100644 --- a/src/js/editor/selection-manager.js +++ b/src/js/editor/selection-manager.js @@ -1,31 +1,33 @@ -import SelectionChangeObserver from 'mobiledoc-kit/editor/selection-change-observer'; +import SelectionChangeObserver from 'mobiledoc-kit/editor/selection-change-observer' export default class SelectionManager { constructor(editor, callback) { - this.editor = editor; - this.callback = callback; - this.started = false; + this.editor = editor + this.callback = callback + this.started = false } start() { - if (this.started) { return; } + if (this.started) { + return + } - SelectionChangeObserver.addListener(this); - this.started = true; + SelectionChangeObserver.addListener(this) + this.started = true } stop() { - this.started = false; - SelectionChangeObserver.removeListener(this); + this.started = false + SelectionChangeObserver.removeListener(this) } destroy() { - this.stop(); + this.stop() } selectionDidChange() { if (this.started) { - this.callback(...arguments); + this.callback(...arguments) } } } diff --git a/src/js/editor/text-input-handler.js b/src/js/editor/text-input-handler.js index 535155157..ed2888e9b 100644 --- a/src/js/editor/text-input-handler.js +++ b/src/js/editor/text-input-handler.js @@ -1,76 +1,85 @@ -import { endsWith } from 'mobiledoc-kit/utils/string-utils'; -import assert from 'mobiledoc-kit/utils/assert'; -import deprecate from 'mobiledoc-kit/utils/deprecate'; -import { ENTER } from 'mobiledoc-kit/utils/characters'; +import { endsWith } from 'mobiledoc-kit/utils/string-utils' +import assert from 'mobiledoc-kit/utils/assert' +import deprecate from 'mobiledoc-kit/utils/deprecate' +import { ENTER } from 'mobiledoc-kit/utils/characters' class TextInputHandler { constructor(editor) { - this.editor = editor; - this._handlers = []; + this.editor = editor + this._handlers = [] } register(handler) { - assert(`Input Handler is not valid`, this._validateHandler(handler)); - this._handlers.push(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 { - let { builder } = postEditor; - let item = builder.createListItem(); - let listSection = builder.createListSection(listTagName, [item]); + let { builder } = postEditor + let item = builder.createListItem() + let listSection = builder.createListSection(listTagName, [item]) - postEditor.replaceSection(section, listSection); - postEditor.setRange(listSection.headPosition()); - }); + postEditor.replaceSection(section, listSection) + postEditor.setRange(listSection.headPosition()) + }) } /** @@ -37,18 +42,23 @@ export function replaceWithListSection(editor, listTagName) { * @public */ export function replaceWithHeaderSection(editor, headingTagName) { - let { range: { head, head: { section } } } = editor; + let { + range: { + head, + head: { section }, + }, + } = editor // Skip if cursor is not at end of section if (!head.isTail()) { - return; + return } editor.run(postEditor => { - let { builder } = postEditor; - let newSection = builder.createMarkupSection(headingTagName); - postEditor.replaceSection(section, newSection); - postEditor.setRange(newSection.headPosition()); - }); + let { builder } = postEditor + let newSection = builder.createMarkupSection(headingTagName) + postEditor.replaceSection(section, newSection) + postEditor.setRange(newSection.headPosition()) + }) } export const DEFAULT_TEXT_INPUT_HANDLERS = [ @@ -57,16 +67,16 @@ export const DEFAULT_TEXT_INPUT_HANDLERS = [ // "* " -> ul match: /^\* $/, run(editor) { - replaceWithListSection(editor, 'ul'); - } + replaceWithListSection(editor, 'ul') + }, }, { name: 'ol', // "1" -> ol, "1." -> ol match: /^1\.? $/, run(editor) { - replaceWithListSection(editor, 'ol'); - } + replaceWithListSection(editor, 'ol') + }, }, { name: 'heading', @@ -80,9 +90,9 @@ export const DEFAULT_TEXT_INPUT_HANDLERS = [ */ match: /^(#{1,6}) $/, run(editor, matches) { - let capture = matches[1]; - let headingTag = 'h' + capture.length; - replaceWithHeaderSection(editor, headingTag); - } - } -]; + let capture = matches[1] + let headingTag = 'h' + capture.length + replaceWithHeaderSection(editor, headingTag) + }, + }, +] diff --git a/src/js/editor/ui.js b/src/js/editor/ui.js index 06aa69d88..97099031b 100644 --- a/src/js/editor/ui.js +++ b/src/js/editor/ui.js @@ -2,10 +2,10 @@ * @module UI */ -import Position from '../utils/cursor/position'; -import Range from '../utils/cursor/range'; +import Position from '../utils/cursor/position' +import Range from '../utils/cursor/range' -let defaultShowPrompt = (message, defaultValue, callback) => callback(window.prompt(message, defaultValue)); +let defaultShowPrompt = (message, defaultValue, callback) => callback(window.prompt(message, defaultValue)) /** * @callback promptCallback @@ -44,26 +44,30 @@ let defaultShowPrompt = (message, defaultValue, callback) => callback(window.pro * }); * @public */ -export function toggleLink(editor, showPrompt=defaultShowPrompt) { +export function toggleLink(editor, showPrompt = defaultShowPrompt) { if (editor.range.isCollapsed) { - return; + return } - let selectedText = editor.cursor.selectedText(); - let defaultUrl = ''; - if (selectedText.indexOf('http') !== -1) { defaultUrl = selectedText; } + let selectedText = editor.cursor.selectedText() + let defaultUrl = '' + if (selectedText.indexOf('http') !== -1) { + defaultUrl = selectedText + } - let {range} = editor; - let hasLink = editor.detectMarkupInRange(range, 'a'); + let { range } = editor + let hasLink = editor.detectMarkupInRange(range, 'a') if (hasLink) { - editor.toggleMarkup('a'); + editor.toggleMarkup('a') } else { showPrompt('Enter a URL', defaultUrl, url => { - if (!url) { return; } + if (!url) { + return + } - editor.toggleMarkup('a', {href: url}); - }); + editor.toggleMarkup('a', { href: url }) + }) } } @@ -79,25 +83,27 @@ export function toggleLink(editor, showPrompt=defaultShowPrompt) { * * @public */ -export function editLink(target, editor, showPrompt=defaultShowPrompt) { +export function editLink(target, editor, showPrompt = defaultShowPrompt) { showPrompt('Enter a URL', target.href, url => { - if (!url) { return; } + if (!url) { + return + } - const position = Position.fromNode(editor._renderTree, target.firstChild); - const range = new Range(position, new Position(position.section, position.offset + target.textContent.length)); + const position = Position.fromNode(editor._renderTree, target.firstChild) + const range = new Range(position, new Position(position.section, position.offset + target.textContent.length)) editor.run(post => { - let markup = editor.builder.createMarkup('a', {href: url}); + let markup = editor.builder.createMarkup('a', { href: url }) // This is the only way to "update" a markup with new attributes in the // current API. - post.toggleMarkup(markup, range); - post.toggleMarkup(markup, range); - }); - }); + post.toggleMarkup(markup, range) + post.toggleMarkup(markup, range) + }) + }) } export default { toggleLink, - editLink -}; + editLink, +} diff --git a/src/js/index.js b/src/js/index.js index 404201f10..05f4d0d60 100644 --- a/src/js/index.js +++ b/src/js/index.js @@ -1,10 +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'; +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, UI, ImageCard, Range, Position, Error, VERSION, MOBILEDOC_VERSION }; +export { Editor, UI, ImageCard, Range, Position, Error, VERSION, MOBILEDOC_VERSION } diff --git a/src/js/models/_attributable.js b/src/js/models/_attributable.js index 459fd18ca..b1adc8c13 100644 --- a/src/js/models/_attributable.js +++ b/src/js/models/_attributable.js @@ -1,30 +1,28 @@ -import { entries } from '../utils/object-utils'; -import { contains } from '../utils/array-utils'; +import { entries } from '../utils/object-utils' +import { contains } from '../utils/array-utils' -export const VALID_ATTRIBUTES = [ - 'data-md-text-align' -]; +export const VALID_ATTRIBUTES = ['data-md-text-align'] /* * A "mixin" to add section attribute support * to markup and list sections. */ export function attributable(ctx) { - ctx.attributes = {}; + ctx.attributes = {} - ctx.hasAttribute = key => key in 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.`); + throw new Error(`Invalid attribute "${key}" was passed. Constrain attributes to the spec-compliant whitelist.`) } - ctx.attributes[key] = value; - }; + ctx.attributes[key] = value + } ctx.removeAttribute = key => { - delete ctx.attributes[key]; - }; - ctx.getAttribute = key => ctx.attributes[key]; + delete ctx.attributes[key] + } + ctx.getAttribute = key => ctx.attributes[key] ctx.eachAttribute = cb => { - entries(ctx.attributes).forEach(([k,v]) => cb(k,v)); - }; + entries(ctx.attributes).forEach(([k, v]) => cb(k, v)) + } } diff --git a/src/js/models/_markerable.js b/src/js/models/_markerable.js index 49104a774..c89cab809 100644 --- a/src/js/models/_markerable.js +++ b/src/js/models/_markerable.js @@ -1,61 +1,57 @@ -import { forEach, reduce } from '../utils/array-utils'; -import Set from '../utils/set'; +import { forEach, reduce } from '../utils/array-utils' +import Set from '../utils/set' -import LinkedList from '../utils/linked-list'; -import Section from './_section'; -import assert from '../utils/assert'; +import LinkedList from '../utils/linked-list' +import Section from './_section' +import assert from '../utils/assert' export default class Markerable extends Section { - constructor(type, tagName, markers=[]) { - super(type); - this.isMarkerable = true; - this.tagName = tagName; + 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; + 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 - }); + freeItem: m => (m.section = m.parent = null), + }) - markers.forEach(m => this.markers.append(m)); + markers.forEach(m => this.markers.append(m)) } canJoin(other) { - return other.isMarkerable && - other.type === this.type && - other.tagName === this.tagName; + 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); + 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 true } - return this.markers.every(m => m.isBlank); + 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; + 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; + text += currentMarker.textUntil(offsetInMarker) + break } else { - text += currentMarker.text; - currentMarker = currentMarker.next; + text += currentMarker.text + currentMarker = currentMarker.next } } - return text; + return text } /** @@ -64,47 +60,45 @@ export default class Markerable extends Section { * * @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); + 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; + 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; + let length = currentMarker === marker ? markerOffset : currentMarker.length + offset += length + currentMarker = currentMarker.next } - return offset; + 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; + _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; + 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()); + currentSection.markers.append(m.clone()) } - }); + }) - return [beforeSection, afterSection]; + return [beforeSection, afterSection] } splitAtMarker(/*marker, offset=0*/) { - assert('splitAtMarker must be implemented by sub-class', false); + assert('splitAtMarker must be implemented by sub-class', false) } /** @@ -117,85 +111,83 @@ export default class Markerable extends Section { * 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: []}; + 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); + let blankMarker = this.builder.createMarker() + this.markers.prepend(blankMarker) + edit.added.push(blankMarker) } else { while (currentMarker) { - len += currentMarker.length; + len += currentMarker.length if (len === sectionOffset) { // nothing to do, there is a gap at the requested offset - break; + 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; + 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; + currentMarker = currentMarker.next } } } - return edit; + return edit } splitAtPosition(position) { - const {marker, offsetInMarker} = position; - return this.splitAtMarker(marker, offsetInMarker); + 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; + let len = 0 + let currentMarker = this.markers.head while (currentMarker) { - len += currentMarker.length; + len += currentMarker.length if (len === sectionOffset) { - return currentMarker; + return currentMarker } else { - assert('markerBeforeOffset called with sectionOffset not between markers', - len < sectionOffset); - currentMarker = currentMarker.next; + 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; + 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 + currentMarker = marker + return true // break out of detect } - }); + }) - return {marker:currentMarker, offset:currentOffset}; + return { marker: currentMarker, offset: currentOffset } } get text() { - return reduce(this.markers, (prev, m) => prev + m.value, ''); + return reduce(this.markers, (prev, m) => prev + m.value, '') } get length() { - return reduce(this.markers, (prev, m) => prev + m.length, 0); + return reduce(this.markers, (prev, m) => prev + m.length, 0) } /** @@ -203,75 +195,80 @@ export default class Markerable extends Section { * 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}}; + 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(); + 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); + cloned.value = marker.value.slice(markerHead, markerTail) } - markers.push(cloned); - }); - return markers; + markers.push(cloned) + }) + return markers } markupsInRange(range) { - const markups = new Set(); + const markups = new Set() this._markersInRange(range, marker => { - marker.markups.forEach(m => markups.add(m)); - }); - return markups.toArray(); + 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; + 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; + 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; + 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}); + callback(currentMarker, { markerHead, markerTail, isContained }) } - currentHead += currentMarker.length; - currentMarker = currentMarker.next; + currentHead += currentMarker.length + currentMarker = currentMarker.next - if (currentHead > tailOffset) { break; } + 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; + let beforeMarker = this.markers.tail + let afterMarker = null otherSection.markers.forEach(m => { if (!m.isBlank) { - m = m.clone(); - this.markers.append(m); + m = m.clone() + this.markers.append(m) if (!afterMarker) { - afterMarker = m; + afterMarker = m } } - }); + }) - return { beforeMarker, afterMarker }; + return { beforeMarker, afterMarker } } } diff --git a/src/js/models/_section.js b/src/js/models/_section.js index 90e3a551c..439be88f8 100644 --- a/src/js/models/_section.js +++ b/src/js/models/_section.js @@ -1,54 +1,52 @@ -import { normalizeTagName } from '../utils/dom-utils'; -import LinkedItem from '../utils/linked-item'; -import assert from '../utils/assert'; -import Position from '../utils/cursor/position'; +import { normalizeTagName } from '../utils/dom-utils' +import LinkedItem from '../utils/linked-item' +import assert from '../utils/assert' +import Position from '../utils/cursor/position' function unimplementedMethod(methodName, me) { - assert(`\`${methodName}()\` must be implemented by ${me.constructor.name}`, - false); + assert(`\`${methodName}()\` must be implemented by ${me.constructor.name}`, false) } export default 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; + 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; + let normalizedTagName = normalizeTagName(val) + assert(`Cannot set section tagName to ${val}`, this.isValidTagName(normalizedTagName)) + this._tagName = normalizedTagName } get tagName() { - return this._tagName; + return this._tagName } isValidTagName(/* normalizedTagName */) { - unimplementedMethod('isValidTagName', this); + unimplementedMethod('isValidTagName', this) } get length() { - return 0; + return 0 } get isBlank() { - unimplementedMethod('isBlank', this); + unimplementedMethod('isBlank', this) } clone() { - unimplementedMethod('clone', this); + unimplementedMethod('clone', this) } canJoin(/* otherSection */) { - unimplementedMethod('canJoin', this); + unimplementedMethod('canJoin', this) } /** @@ -56,7 +54,7 @@ export default class Section extends LinkedItem { * @public */ headPosition() { - return this.toPosition(0); + return this.toPosition(0) } /** @@ -64,7 +62,7 @@ export default class Section extends LinkedItem { * @public */ tailPosition() { - return this.toPosition(this.length); + return this.toPosition(this.length) } /** @@ -73,10 +71,10 @@ export default class Section extends LinkedItem { * @public */ toPosition(offset) { - assert("Must pass number to `toPosition`", typeof offset === 'number'); - assert("Cannot call `toPosition` with offset > length", offset <= this.length); + assert('Must pass number to `toPosition`', typeof offset === 'number') + assert('Cannot call `toPosition` with offset > length', offset <= this.length) - return new Position(this, offset); + return new Position(this, offset) } /** @@ -84,60 +82,60 @@ export default class Section extends LinkedItem { * @public */ toRange() { - return this.headPosition().toRange(this.tailPosition()); + return this.headPosition().toRange(this.tailPosition()) } join() { - unimplementedMethod('join', this); + unimplementedMethod('join', this) } textUntil(/* position */) { - return ''; + return '' } /** * Markerable sections should override this method */ splitMarkerAtOffset() { - let blankEdit = { added: [], removed: [] }; - return blankEdit; + let blankEdit = { added: [], removed: [] } + return blankEdit } nextLeafSection() { - const next = this.next; + const next = this.next if (next) { if (next.items) { - return next.items.head; + return next.items.head } else { - return next; + return next } } else { if (this.isNested) { - return this.parent.nextLeafSection(); + return this.parent.nextLeafSection() } } } immediatelyNextMarkerableSection() { - let next = this.nextLeafSection(); + let next = this.nextLeafSection() while (next && !next.isMarkerable) { - next = next.nextLeafSection(); + next = next.nextLeafSection() } - return next; + return next } previousLeafSection() { - const prev = this.prev; + const prev = this.prev if (prev) { if (prev.items) { - return prev.items.tail; + return prev.items.tail } else { - return prev; + return prev } } else { if (this.isNested) { - return this.parent.previousLeafSection(); + return this.parent.previousLeafSection() } } } diff --git a/src/js/models/atom-node.js b/src/js/models/atom-node.js index 58e7f409c..ff6a8ce3d 100644 --- a/src/js/models/atom-node.js +++ b/src/js/models/atom-node.js @@ -1,63 +1,66 @@ -import assert from '../utils/assert'; +import assert from '../utils/assert' export default 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; + 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; + let { + atomOptions: options, + env, + model: { value, payload }, + } = this // cache initial render - this._rendered = this.atom.render({options, env, value, payload}); + this._rendered = this.atom.render({ options, env, value, payload }) } - this._validateAndAppendRenderResult(this._rendered); + 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(); - } - }; + 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; + this._teardownCallback() + this._teardownCallback = null } if (this._rendered) { - this.element.removeChild(this._rendered); - this._rendered = null; + this.element.removeChild(this._rendered) + this._rendered = null } } _validateAndAppendRenderResult(rendered) { if (!rendered) { - return; + return } - let { atom: { name } } = this; - assert( - `Atom "${name}" must return a DOM node (returned value was: "${rendered}")`, - !!rendered.nodeType - ); - this.element.appendChild(rendered); + let { + atom: { name }, + } = this + assert(`Atom "${name}" must return a DOM node (returned value was: "${rendered}")`, !!rendered.nodeType) + this.element.appendChild(rendered) } } diff --git a/src/js/models/atom.js b/src/js/models/atom.js index 3d94f4137..972012012 100644 --- a/src/js/models/atom.js +++ b/src/js/models/atom.js @@ -1,91 +1,88 @@ -import { ATOM_TYPE } from './types'; -import mixin from '../utils/mixin'; -import MarkuperableMixin from '../utils/markuperable'; -import LinkedItem from '../utils/linked-item'; -import assert from '../utils/assert'; +import { ATOM_TYPE } from './types' +import mixin from '../utils/mixin' +import MarkuperableMixin from '../utils/markuperable' +import LinkedItem from '../utils/linked-item' +import assert from '../utils/assert' -const ATOM_LENGTH = 1; +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)); + 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 - ); + let clonedMarkups = this.markups.slice() + return this.builder.createAtom(this.name, this.value, this.payload, clonedMarkups) } get isBlank() { - return false; + return false } get length() { - return ATOM_LENGTH; + return ATOM_LENGTH } canJoin(/* other */) { - return false; + return false } textUntil(/* offset */) { - return ''; + return '' } - split(offset=0, endOffset=offset) { - let markers = []; + split(offset = 0, endOffset = offset) { + let markers = [] if (endOffset === 0) { - markers.push(this.builder.createMarker('', this.markups.slice())); + markers.push(this.builder.createMarker('', this.markups.slice())) } - markers.push(this.clone()); + markers.push(this.clone()) if (offset === ATOM_LENGTH) { - markers.push(this.builder.createMarker('', this.markups.slice())); + markers.push(this.builder.createMarker('', this.markups.slice())) } - return markers; + return markers } splitAtOffset(offset) { - assert('Cannot split a marker at an offset > its length', - offset <= this.length); + 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; + let { builder } = this + let clone = this.clone() + let blankMarker = builder.createMarker('') + let pre, post if (offset === 0) { - ([pre, post] = [blankMarker, clone]); + ;[pre, post] = [blankMarker, clone] } else if (offset === ATOM_LENGTH) { - ([pre, post] = [clone, blankMarker]); + ;[pre, post] = [clone, blankMarker] } else { - assert(`Invalid offset given to Atom#splitAtOffset: "${offset}"`, false); + assert(`Invalid offset given to Atom#splitAtOffset: "${offset}"`, false) } this.markups.forEach(markup => { - pre.addMarkup(markup); - post.addMarkup(markup); - }); - return [pre, post]; + pre.addMarkup(markup) + post.addMarkup(markup) + }) + return [pre, post] } } -mixin(Atom, MarkuperableMixin); +mixin(Atom, MarkuperableMixin) -export default Atom; +export default Atom diff --git a/src/js/models/card-node.js b/src/js/models/card-node.js index abfd04b72..d3470c112 100644 --- a/src/js/models/card-node.js +++ b/src/js/models/card-node.js @@ -1,54 +1,55 @@ -import assert from '../utils/assert'; +import assert from '../utils/assert' export default class CardNode { constructor(editor, card, section, element, options) { - this.editor = editor; - this.card = card; - this.section = section; - this.element = element; - this.options = options; + this.editor = editor + this.card = card + this.section = section + this.element = element + this.options = options - this.mode = null; + this.mode = null - this._teardownCallback = null; - this._rendered = null; + this._teardownCallback = null + this._rendered = null } render(mode) { - if (this.mode === mode) { return; } + if (this.mode === mode) { + return + } - this.teardown(); + this.teardown() - this.mode = mode; + this.mode = mode - let method = mode === 'display' ? 'render' : 'edit'; - method = this.card[method]; + let method = mode === 'display' ? 'render' : 'edit' + method = this.card[method] - assert(`Card is missing "${method}" (tried to render mode: "${mode}")`, - !!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 - }); + payload: this.section.payload, + }) - this._validateAndAppendRenderResult(rendered); + this._validateAndAppendRenderResult(rendered) } teardown() { if (this._teardownCallback) { - this._teardownCallback(); - this._teardownCallback = null; + this._teardownCallback() + this._teardownCallback = null } if (this._rendered) { - this.element.removeChild(this._rendered); - this._rendered = null; + this.element.removeChild(this._rendered) + this._rendered = null } } didRender() { if (this._didRenderCallback) { - this._didRenderCallback(); + this._didRenderCallback() } } @@ -56,47 +57,46 @@ export default class CardNode { return { name: this.card.name, isInEditor: true, - onTeardown: (callback) => this._teardownCallback = callback, - didRender: (callback) => this._didRenderCallback = callback, + onTeardown: callback => (this._teardownCallback = callback), + didRender: callback => (this._didRenderCallback = callback), edit: () => this.edit(), - save: (payload, transition=true) => { - this.section.payload = payload; + save: (payload, transition = true) => { + this.section.payload = payload - this.editor._postDidChange(); + this.editor._postDidChange() if (transition) { - this.display(); + this.display() } }, cancel: () => this.display(), remove: () => this.remove(), - postModel: this.section - }; + postModel: this.section, + } } display() { - this.render('display'); + this.render('display') } edit() { - this.render('edit'); + this.render('edit') } remove() { - this.editor.run(postEditor => postEditor.removeSection(this.section)); + this.editor.run(postEditor => postEditor.removeSection(this.section)) } _validateAndAppendRenderResult(rendered) { if (!rendered) { - return; + 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(); + 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() } } diff --git a/src/js/models/card.js b/src/js/models/card.js index 2bcb758fb..127230cb6 100644 --- a/src/js/models/card.js +++ b/src/js/models/card.js @@ -1,48 +1,48 @@ -import Section from './_section'; -import { CARD_TYPE } from './types'; -import { shallowCopyObject } from '../utils/copy'; +import Section from './_section' +import { CARD_TYPE } from './types' +import { shallowCopyObject } from '../utils/copy' export const CARD_MODES = { DISPLAY: 'display', - EDIT: 'edit' -}; + EDIT: 'edit', +} -const CARD_LENGTH = 1; +const CARD_LENGTH = 1 -const DEFAULT_INITIAL_MODE = CARD_MODES.DISPLAY; +const DEFAULT_INITIAL_MODE = CARD_MODES.DISPLAY export default class Card extends Section { constructor(name, payload) { - super(CARD_TYPE); - this.name = name; - this.payload = payload; - this.setInitialMode(DEFAULT_INITIAL_MODE); - this.isCardSection = true; + super(CARD_TYPE) + this.name = name + this.payload = payload + this.setInitialMode(DEFAULT_INITIAL_MODE) + this.isCardSection = true } get isBlank() { - return false; + return false } canJoin() { - return false; + return false } get length() { - return CARD_LENGTH; + return CARD_LENGTH } clone() { - let payload = shallowCopyObject(this.payload); - let card = this.builder.createCardSection(this.name, payload); + 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; + let mode = this._initialMode if (this.renderNode && this.renderNode.cardNode) { - mode = this.renderNode.cardNode.mode; + mode = this.renderNode.cardNode.mode } - card.setInitialMode(mode); - return card; + card.setInitialMode(mode) + return card } /** @@ -51,6 +51,6 @@ export default class Card extends Section { */ setInitialMode(initialMode) { // TODO validate initialMode - this._initialMode = initialMode; + this._initialMode = initialMode } } diff --git a/src/js/models/image.js b/src/js/models/image.js index a29dcdffb..a18fdfd7d 100644 --- a/src/js/models/image.js +++ b/src/js/models/image.js @@ -1,21 +1,21 @@ -import { IMAGE_SECTION_TYPE } from './types'; -import Section from './_section'; +import { IMAGE_SECTION_TYPE } from './types' +import Section from './_section' export default class Image extends Section { constructor() { - super(IMAGE_SECTION_TYPE); - this.src = null; + super(IMAGE_SECTION_TYPE) + this.src = null } canJoin() { - return false; + return false } get isBlank() { - return false; + return false } get length() { - return 1; + return 1 } } diff --git a/src/js/models/lifecycle-callbacks.js b/src/js/models/lifecycle-callbacks.js index 11c80bcbc..4cd972349 100644 --- a/src/js/models/lifecycle-callbacks.js +++ b/src/js/models/lifecycle-callbacks.js @@ -1,50 +1,50 @@ -import assert from 'mobiledoc-kit/utils/assert'; +import assert from 'mobiledoc-kit/utils/assert' export default class LifecycleCallbacks { - constructor(queueNames=[]) { - this.callbackQueues = {}; - this.removalQueues = {}; + constructor(queueNames = []) { + this.callbackQueues = {} + this.removalQueues = {} queueNames.forEach(name => { - this.callbackQueues[name] = []; - this.removalQueues[name] = []; - }); + this.callbackQueues[name] = [] + this.removalQueues[name] = [] + }) } - runCallbacks(queueName, args=[]) { - let queue = this._getQueue(queueName); - queue.forEach(cb => cb(...args)); + runCallbacks(queueName, args = []) { + let queue = this._getQueue(queueName) + queue.forEach(cb => cb(...args)) - let toRemove = this.removalQueues[queueName]; + let toRemove = this.removalQueues[queueName] toRemove.forEach(cb => { - let index = queue.indexOf(cb); + let index = queue.indexOf(cb) if (index !== -1) { - queue.splice(index, 1); + queue.splice(index, 1) } - }); + }) - this.removalQueues[queueName] = []; + this.removalQueues[queueName] = [] } addCallback(queueName, callback) { - this._getQueue(queueName).push(callback); + this._getQueue(queueName).push(callback) } _scheduleCallbackForRemoval(queueName, callback) { - this.removalQueues[queueName].push(callback); + this.removalQueues[queueName].push(callback) } addCallbackOnce(queueName, callback) { - let queue = this._getQueue(queueName); + let queue = this._getQueue(queueName) if (queue.indexOf(callback) === -1) { - queue.push(callback); - this._scheduleCallbackForRemoval(queueName, callback); + queue.push(callback) + this._scheduleCallbackForRemoval(queueName, callback) } } _getQueue(queueName) { - let queue = this.callbackQueues[queueName]; - assert(`No queue found for "${queueName}"`, !!queue); - return queue; + let queue = this.callbackQueues[queueName] + assert(`No queue found for "${queueName}"`, !!queue) + return queue } } diff --git a/src/js/models/list-item.js b/src/js/models/list-item.js index 80173c1bc..98369e59c 100644 --- a/src/js/models/list-item.js +++ b/src/js/models/list-item.js @@ -1,42 +1,36 @@ -import Markerable from './_markerable'; -import { LIST_ITEM_TYPE } from './types'; -import { - normalizeTagName -} from 'mobiledoc-kit/utils/dom-utils'; -import { contains } from 'mobiledoc-kit/utils/array-utils'; +import Markerable from './_markerable' +import { LIST_ITEM_TYPE } from './types' +import { normalizeTagName } from 'mobiledoc-kit/utils/dom-utils' +import { contains } from 'mobiledoc-kit/utils/array-utils' -export const VALID_LIST_ITEM_TAGNAMES = [ - 'li' -].map(normalizeTagName); +export const VALID_LIST_ITEM_TAGNAMES = ['li'].map(normalizeTagName) export default class ListItem extends Markerable { - constructor(tagName, markers=[]) { - super(LIST_ITEM_TYPE, tagName, markers); - this.isListItem = true; - this.isNested = true; + constructor(tagName, markers = []) { + super(LIST_ITEM_TYPE, tagName, markers) + this.isListItem = true + this.isNested = true } isValidTagName(normalizedTagName) { - return contains(VALID_LIST_ITEM_TAGNAMES, normalizedTagName); + return contains(VALID_LIST_ITEM_TAGNAMES, normalizedTagName) } - splitAtMarker(marker, offset=0) { + 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); + const isLastItem = !this.next + const createNewSection = !marker && offset === 0 && isLastItem let [beforeSection, afterSection] = [ this.builder.createListItem(), - createNewSection ? this.builder.createMarkupSection() : - this.builder.createListItem() - ]; + createNewSection ? this.builder.createMarkupSection() : this.builder.createListItem(), + ] - return this._redistributeMarkers( - beforeSection, afterSection, marker, offset); + return this._redistributeMarkers(beforeSection, afterSection, marker, offset) } get post() { - return this.section.post; + return this.section.post } } diff --git a/src/js/models/list-section.js b/src/js/models/list-section.js index 33d1f4313..2e160e33f 100644 --- a/src/js/models/list-section.js +++ b/src/js/models/list-section.js @@ -1,66 +1,63 @@ -import { LIST_SECTION_TYPE } from './types'; -import Section from './_section'; -import { attributable } from './_attributable'; +import { LIST_SECTION_TYPE } from './types' +import Section from './_section' +import { attributable } from './_attributable' -import LinkedList from '../utils/linked-list'; -import { forEach, contains } from '../utils/array-utils'; -import { normalizeTagName } from '../utils/dom-utils'; -import assert from '../utils/assert'; -import { entries } from '../utils/object-utils'; +import LinkedList from '../utils/linked-list' +import { forEach, contains } from '../utils/array-utils' +import { normalizeTagName } from '../utils/dom-utils' +import assert from '../utils/assert' +import { entries } from '../utils/object-utils' -export const VALID_LIST_SECTION_TAGNAMES = [ - 'ul', 'ol' -].map(normalizeTagName); +export const VALID_LIST_SECTION_TAGNAMES = ['ul', 'ol'].map(normalizeTagName) -export const DEFAULT_TAG_NAME = VALID_LIST_SECTION_TAGNAMES[0]; +export const DEFAULT_TAG_NAME = VALID_LIST_SECTION_TAGNAMES[0] export default class ListSection extends Section { - constructor(tagName=DEFAULT_TAG_NAME, items=[], attributes={}) { - super(LIST_SECTION_TYPE); - this.tagName = tagName; - this.isListSection = true; - this.isLeafSection = false; + constructor(tagName = DEFAULT_TAG_NAME, 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)); + 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; + 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; + freeItem: i => (i.section = i.parent = null), + }) + this.sections = this.items - items.forEach(i => this.items.append(i)); + items.forEach(i => this.items.append(i)) } canJoin() { - return false; + return false } isValidTagName(normalizedTagName) { - return contains(VALID_LIST_SECTION_TAGNAMES, normalizedTagName); + return contains(VALID_LIST_SECTION_TAGNAMES, normalizedTagName) } headPosition() { - return this.items.head.headPosition(); + return this.items.head.headPosition() } tailPosition() { - return this.items.tail.tailPosition(); + return this.items.tail.tailPosition() } get isBlank() { - return this.items.isEmpty; + return this.items.isEmpty } clone() { - let newSection = this.builder.createListSection(this.tagName); - forEach(this.items, i => newSection.items.append(i.clone())); - return newSection; + let newSection = this.builder.createListSection(this.tagName) + forEach(this.items, i => newSection.items.append(i.clone())) + return newSection } /** @@ -70,11 +67,11 @@ export default class ListSection extends Section { */ join(other) { if (other.isListSection) { - other.items.forEach(i => this.join(i)); + other.items.forEach(i => this.join(i)) } else if (other.isMarkerable) { - let item = this.builder.createListItem(); - item.join(other); - this.items.append(item); + let item = this.builder.createListItem() + item.join(other) + this.items.append(item) } } } diff --git a/src/js/models/marker.js b/src/js/models/marker.js index f0389cab2..b7ad72ff2 100644 --- a/src/js/models/marker.js +++ b/src/js/models/marker.js @@ -1,9 +1,9 @@ -import { MARKER_TYPE } from './types'; -import mixin from '../utils/mixin'; -import MarkuperableMixin from '../utils/markuperable'; -import LinkedItem from '../utils/linked-item'; -import assert from '../utils/assert'; -import { isArrayEqual } from '../utils/array-utils'; +import { MARKER_TYPE } from './types' +import mixin from '../utils/mixin' +import MarkuperableMixin from '../utils/markuperable' +import LinkedItem from '../utils/linked-item' +import assert from '../utils/assert' +import { isArrayEqual } from '../utils/array-utils' // Unicode uses a pair of "surrogate" characters" (a high- and low-surrogate) // to encode characters outside the basic multilingual plane (like emoji and @@ -12,36 +12,36 @@ import { isArrayEqual } from '../utils/array-utils'; // high- and low-surrogate characters. // See "high surrogate" and "low surrogate" on // https://en.wikipedia.org/wiki/Unicode_block -export const HIGH_SURROGATE_RANGE = [0xD800, 0xDBFF]; -export const LOW_SURROGATE_RANGE = [0xDC00, 0xDFFF]; +export const HIGH_SURROGATE_RANGE = [0xd800, 0xdbff] +export 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)); + 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); + const clonedMarkups = this.markups.slice() + return this.builder.createMarker(this.value, clonedMarkups) } get isEmpty() { - return this.isBlank; + return this.isBlank } get isBlank() { - return this.length === 0; + return this.length === 0 } charAt(offset) { - return this.value.slice(offset, offset+1); + return this.value.slice(offset, offset + 1) } /** @@ -49,77 +49,71 @@ const Marker = class Marker extends LinkedItem { * Compare with an Atom which distinguishes between text and value */ get text() { - return this.value; + return this.value } get length() { - return this.value.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); + assert('Cannot delete value at offset outside bounds', offset >= 0 && offset <= this.length) - let width = 1; - let code = this.value.charCodeAt(offset); + let width = 1 + let code = this.value.charCodeAt(offset) if (code >= HIGH_SURROGATE_RANGE[0] && code <= HIGH_SURROGATE_RANGE[1]) { - width = 2; + width = 2 } else if (code >= LOW_SURROGATE_RANGE[0] && code <= LOW_SURROGATE_RANGE[1]) { - width = 2; - offset = offset - 1; + width = 2 + offset = offset - 1 } - const [ left, right ] = [ - this.value.slice(0, offset), - this.value.slice(offset+width) - ]; - this.value = left + right; + const [left, right] = [this.value.slice(0, offset), this.value.slice(offset + width)] + this.value = left + right - return width; + return width } canJoin(other) { - return other && other.isMarker && isArrayEqual(this.markups, other.markups); + return other && other.isMarker && isArrayEqual(this.markups, other.markups) } textUntil(offset) { - return this.value.slice(0, offset); + return this.value.slice(0, offset) } - split(offset=0, endOffset=this.length) { + 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.builder.createMarker(this.value.substring(endOffset)), + ] - this.markups.forEach(mu => markers.forEach(m => m.addMarkup(mu))); - return markers; + 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; + 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)); + 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); - }); + pre.addMarkup(markup) + post.addMarkup(markup) + }) - return [pre, post]; + return [pre, post] } +} -}; +mixin(Marker, MarkuperableMixin) -mixin(Marker, MarkuperableMixin); - -export default Marker; +export default Marker diff --git a/src/js/models/markup-section.js b/src/js/models/markup-section.js index d9edd844b..700815d3b 100644 --- a/src/js/models/markup-section.js +++ b/src/js/models/markup-section.js @@ -1,62 +1,46 @@ -import Markerable from './_markerable'; -import { attributable } from './_attributable'; -import { MARKUP_SECTION_TYPE } from './types'; +import Markerable from './_markerable' +import { attributable } from './_attributable' +import { MARKUP_SECTION_TYPE } from './types' -import { normalizeTagName } from '../utils/dom-utils'; -import { contains } from '../utils/array-utils'; -import { entries } from '../utils/object-utils'; +import { normalizeTagName } from '../utils/dom-utils' +import { contains } from '../utils/array-utils' +import { entries } from '../utils/object-utils' // valid values of `tagName` for a MarkupSection -export const VALID_MARKUP_SECTION_TAGNAMES = [ - 'aside', - 'blockquote', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'p' -].map(normalizeTagName); +export 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 -export const MARKUP_SECTION_ELEMENT_NAMES = [ - 'aside', - 'blockquote', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'p' -].map(normalizeTagName); -export const DEFAULT_TAG_NAME = VALID_MARKUP_SECTION_TAGNAMES[8]; +export const MARKUP_SECTION_ELEMENT_NAMES = ['aside', 'blockquote', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p'].map( + normalizeTagName +) +export 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); + constructor(tagName = DEFAULT_TAG_NAME, markers = [], attributes = {}) { + super(MARKUP_SECTION_TYPE, tagName, markers) - attributable(this); - entries(attributes).forEach(([k,v]) => this.setAttribute(k, v)); + attributable(this) + entries(attributes).forEach(([k, v]) => this.setAttribute(k, v)) - this.isMarkupSection = true; + this.isMarkupSection = true } isValidTagName(normalizedTagName) { - return contains(VALID_MARKUP_SECTION_TAGNAMES, normalizedTagName); + return contains(VALID_MARKUP_SECTION_TAGNAMES, normalizedTagName) } - splitAtMarker(marker, offset=0) { + splitAtMarker(marker, offset = 0) { let [beforeSection, afterSection] = [ this.builder.createMarkupSection(this.tagName, [], false, this.attributes), - this.builder.createMarkupSection() - ]; + this.builder.createMarkupSection(), + ] - return this._redistributeMarkers(beforeSection, afterSection, marker, offset); + return this._redistributeMarkers(beforeSection, afterSection, marker, offset) } -}; +} -export default MarkupSection; +export default MarkupSection diff --git a/src/js/models/markup.js b/src/js/models/markup.js index 1389473b2..ecece259e 100644 --- a/src/js/models/markup.js +++ b/src/js/models/markup.js @@ -1,7 +1,7 @@ -import { normalizeTagName } from '../utils/dom-utils'; -import { filterObject } from '../utils/array-utils'; -import { MARKUP_TYPE } from './types'; -import assert from '../utils/assert'; +import { normalizeTagName } from '../utils/dom-utils' +import { filterObject } from '../utils/array-utils' +import { MARKUP_TYPE } from './types' +import assert from '../utils/assert' export const VALID_MARKUP_TAGNAMES = [ 'a', @@ -9,18 +9,15 @@ export const VALID_MARKUP_TAGNAMES = [ 'code', 'em', 'i', - 's', // strikethrough + 's', // strikethrough 'del', // deleted text (also strikethrough) 'strong', 'sub', // subscript 'sup', // superscript - 'u' -].map(normalizeTagName); + 'u', +].map(normalizeTagName) -export const VALID_ATTRIBUTES = [ - 'href', - 'rel' -]; +export const VALID_ATTRIBUTES = ['href', 'rel'] /** * A Markup is similar with an inline HTML tag that might be added to @@ -32,17 +29,15 @@ class Markup { /* * @param {Object} attributes key-values */ - constructor(tagName, attributes={}) { - this.tagName = normalizeTagName(tagName); + constructor(tagName, attributes = {}) { + this.tagName = normalizeTagName(tagName) - assert('Must use attributes object param (not array) for Markup', - !Array.isArray(attributes)); + assert('Must use attributes object param (not array) for Markup', !Array.isArray(attributes)) - this.attributes = filterObject(attributes, VALID_ATTRIBUTES); - this.type = MARKUP_TYPE; + this.attributes = filterObject(attributes, VALID_ATTRIBUTES) + this.type = MARKUP_TYPE - assert(`Cannot create markup of tagName ${tagName}`, - VALID_MARKUP_TAGNAMES.indexOf(this.tagName) !== -1); + assert(`Cannot create markup of tagName ${tagName}`, VALID_MARKUP_TAGNAMES.indexOf(this.tagName) !== -1) } /** @@ -51,15 +46,15 @@ class Markup { * @private */ isForwardInclusive() { - return this.tagName === normalizeTagName("a") ? false : true; + return this.tagName === normalizeTagName('a') ? false : true } isBackwardInclusive() { - return false; + return false } hasTag(tagName) { - return this.tagName === normalizeTagName(tagName); + return this.tagName === normalizeTagName(tagName) } /** @@ -67,13 +62,13 @@ class Markup { * @param {String} name, e.g. "href" */ getAttribute(name) { - return this.attributes[name]; + return this.attributes[name] } static isValidElement(element) { - const tagName = normalizeTagName(element.tagName); - return VALID_MARKUP_TAGNAMES.indexOf(tagName) !== -1; + const tagName = normalizeTagName(element.tagName) + return VALID_MARKUP_TAGNAMES.indexOf(tagName) !== -1 } } -export default Markup; +export default Markup diff --git a/src/js/models/post-node-builder.js b/src/js/models/post-node-builder.js index 16755d6a8..0690ecc0c 100644 --- a/src/js/models/post-node-builder.js +++ b/src/js/models/post-node-builder.js @@ -1,37 +1,30 @@ -import Atom from '../models/atom'; -import Post from '../models/post'; -import MarkupSection from '../models/markup-section'; -import ListSection from '../models/list-section'; -import ListItem from '../models/list-item'; -import ImageSection from '../models/image'; -import Marker from '../models/marker'; -import Markup from '../models/markup'; -import Card from '../models/card'; -import { normalizeTagName } from '../utils/dom-utils'; -import { objectToSortedKVArray } from '../utils/array-utils'; -import { - LIST_ITEM_TYPE, - MARKUP_SECTION_TYPE -} from '../models/types'; -import { - DEFAULT_TAG_NAME as DEFAULT_MARKUP_SECTION_TAG_NAME -} from '../models/markup-section'; -import { - DEFAULT_TAG_NAME as DEFAULT_LIST_SECTION_TAG_NAME -} from '../models/list-section'; -import assert from '../utils/assert'; +import Atom from '../models/atom' +import Post from '../models/post' +import MarkupSection from '../models/markup-section' +import ListSection from '../models/list-section' +import ListItem from '../models/list-item' +import ImageSection from '../models/image' +import Marker from '../models/marker' +import Markup from '../models/markup' +import Card from '../models/card' +import { normalizeTagName } from '../utils/dom-utils' +import { objectToSortedKVArray } from '../utils/array-utils' +import { LIST_ITEM_TYPE, MARKUP_SECTION_TYPE } from '../models/types' +import { DEFAULT_TAG_NAME as DEFAULT_MARKUP_SECTION_TAG_NAME } from '../models/markup-section' +import { DEFAULT_TAG_NAME as DEFAULT_LIST_SECTION_TAG_NAME } from '../models/list-section' +import assert from '../utils/assert' function cacheKey(tagName, attributes) { - return `${normalizeTagName(tagName)}-${objectToSortedKVArray(attributes).join('-')}`; + return `${normalizeTagName(tagName)}-${objectToSortedKVArray(attributes).join('-')}` } function addMarkupToCache(cache, markup) { - cache[cacheKey(markup.tagName, markup.attributes)] = markup; + cache[cacheKey(markup.tagName, markup.attributes)] = markup } function findMarkupInCache(cache, tagName, attributes) { - const key = cacheKey(tagName, attributes); - return cache[key]; + const key = cacheKey(tagName, attributes) + return cache[key] } /** @@ -47,29 +40,29 @@ class PostNodeBuilder { * @private */ constructor() { - this.markupCache = {}; + this.markupCache = {} } /** * @return {Post} A new, blank post */ - createPost(sections=[]) { - const post = new Post(); - post.builder = this; + createPost(sections = []) { + const post = new Post() + post.builder = this - sections.forEach(s => post.sections.append(s)); + sections.forEach(s => post.sections.append(s)) - return post; + return post } - createMarkerableSection(type, tagName, markers=[]) { + createMarkerableSection(type, tagName, markers = []) { switch (type) { case LIST_ITEM_TYPE: - return this.createListItem(markers); + return this.createListItem(markers) case MARKUP_SECTION_TYPE: - return this.createMarkupSection(tagName, markers); + return this.createMarkupSection(tagName, markers) default: - assert(`Cannot create markerable section of type ${type}`, false); + assert(`Cannot create markerable section of type ${type}`, false) } } @@ -78,36 +71,36 @@ class PostNodeBuilder { * @param {Marker[]} [markers=[]] * @return {MarkupSection} */ - createMarkupSection(tagName=DEFAULT_MARKUP_SECTION_TAG_NAME, markers=[], isGenerated=false, attributes={}) { - tagName = normalizeTagName(tagName); - const section = new MarkupSection(tagName, markers, attributes); + createMarkupSection(tagName = DEFAULT_MARKUP_SECTION_TAG_NAME, markers = [], isGenerated = false, attributes = {}) { + tagName = normalizeTagName(tagName) + const section = new MarkupSection(tagName, markers, attributes) if (isGenerated) { - section.isGenerated = true; + section.isGenerated = true } - section.builder = this; - return section; + section.builder = this + return section } - createListSection(tagName=DEFAULT_LIST_SECTION_TAG_NAME, items=[], attributes={}) { - tagName = normalizeTagName(tagName); - const section = new ListSection(tagName, items, attributes); - section.builder = this; - return section; + createListSection(tagName = DEFAULT_LIST_SECTION_TAG_NAME, 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; + createListItem(markers = []) { + const tagName = normalizeTagName('li') + const item = new ListItem(tagName, markers) + item.builder = this + return item } createImageSection(url) { - let section = new ImageSection(); + let section = new ImageSection() if (url) { - section.src = url; + section.src = url } - return section; + return section } /** @@ -115,10 +108,10 @@ class PostNodeBuilder { * @param {Object} [payload={}] * @return {CardSection} */ - createCardSection(name, payload={}) { - const card = new Card(name, payload); - card.builder = this; - return card; + createCardSection(name, payload = {}) { + const card = new Card(name, payload) + card.builder = this + return card } /** @@ -126,10 +119,10 @@ class PostNodeBuilder { * @param {Markup[]} [markups=[]] * @return {Marker} */ - createMarker(value, markups=[]) { - const marker = new Marker(value, markups); - marker.builder = this; - return marker; + createMarker(value, markups = []) { + const marker = new Marker(value, markups) + marker.builder = this + return marker } /** @@ -139,10 +132,10 @@ class PostNodeBuilder { * @param {Markup[]} [markups=[]] * @return {Atom} */ - createAtom(name, value='', payload={}, markups=[]) { - const atom = new Atom(name, value, payload, markups); - atom.builder = this; - return atom; + createAtom(name, value = '', payload = {}, markups = []) { + const atom = new Atom(name, value, payload, markups) + atom.builder = this + return atom } /** @@ -150,18 +143,18 @@ class PostNodeBuilder { * @param {Object} attributes Key-value pairs of attributes for the markup * @return {Markup} */ - createMarkup(tagName, attributes={}) { - tagName = normalizeTagName(tagName); + createMarkup(tagName, attributes = {}) { + tagName = normalizeTagName(tagName) - let markup = findMarkupInCache(this.markupCache, tagName, attributes); + let markup = findMarkupInCache(this.markupCache, tagName, attributes) if (!markup) { - markup = new Markup(tagName, attributes); - markup.builder = this; - addMarkupToCache(this.markupCache, markup); + markup = new Markup(tagName, attributes) + markup.builder = this + addMarkupToCache(this.markupCache, markup) } - return markup; + return markup } } -export default PostNodeBuilder; +export default PostNodeBuilder diff --git a/src/js/models/post.js b/src/js/models/post.js index 18772dd29..4117cfc48 100644 --- a/src/js/models/post.js +++ b/src/js/models/post.js @@ -1,9 +1,9 @@ -import { POST_TYPE } from './types'; -import LinkedList from 'mobiledoc-kit/utils/linked-list'; -import { forEach } from 'mobiledoc-kit/utils/array-utils'; -import Set from 'mobiledoc-kit/utils/set'; -import Position from 'mobiledoc-kit/utils/cursor/position'; -import assert from 'mobiledoc-kit/utils/assert'; +import { POST_TYPE } from './types' +import LinkedList from 'mobiledoc-kit/utils/linked-list' +import { forEach } from 'mobiledoc-kit/utils/array-utils' +import Set from 'mobiledoc-kit/utils/set' +import Position from 'mobiledoc-kit/utils/cursor/position' +import assert from 'mobiledoc-kit/utils/assert' /** * The Post is an in-memory representation of an editor's document. @@ -18,11 +18,11 @@ class Post { * @private */ constructor() { - this.type = POST_TYPE; + this.type = POST_TYPE this.sections = new LinkedList({ - adoptItem: s => s.post = s.parent = this, - freeItem: s => s.post = s.parent = null - }); + adoptItem: s => (s.post = s.parent = this), + freeItem: s => (s.post = s.parent = null), + }) } /** @@ -32,9 +32,9 @@ class Post { */ headPosition() { if (this.isBlank) { - return Position.blankPosition(); + return Position.blankPosition() } else { - return this.sections.head.headPosition(); + return this.sections.head.headPosition() } } @@ -45,9 +45,9 @@ class Post { */ tailPosition() { if (this.isBlank) { - return Position.blankPosition(); + return Position.blankPosition() } else { - return this.sections.tail.tailPosition(); + return this.sections.tail.tailPosition() } } @@ -56,11 +56,11 @@ class Post { * @public */ toRange() { - return this.headPosition().toRange(this.tailPosition()); + return this.headPosition().toRange(this.tailPosition()) } get isBlank() { - return this.sections.isEmpty; + return this.sections.isEmpty } /** @@ -70,11 +70,10 @@ class Post { * @public */ get hasContent() { - if ((this.sections.length > 1) || - (this.sections.length === 1 && !this.sections.head.isBlank)) { - return true; + if (this.sections.length > 1 || (this.sections.length === 1 && !this.sections.head.isBlank)) { + return true } else { - return false; + return false } } @@ -83,75 +82,73 @@ class Post { * @return {Array} markers that are completely contained by the range */ markersContainedByRange(range) { - const markers = []; + const markers = [] this.walkMarkerableSections(range, section => { - section._markersInRange( - range.trimTo(section), - (m, {isContained}) => { if (isContained) { markers.push(m); } } - ); - }); + section._markersInRange(range.trimTo(section), (m, { isContained }) => { + if (isContained) { + markers.push(m) + } + }) + }) - return markers; + return markers } markupsInRange(range) { - const markups = new Set(); + const markups = new Set() if (range.isCollapsed) { - let pos = range.head; + let pos = range.head if (pos.isMarkerable) { - let [back, forward] = [pos.markerIn(-1), pos.markerIn(1)]; + let [back, forward] = [pos.markerIn(-1), pos.markerIn(1)] if (back && forward && back === forward) { - back.markups.forEach(m => markups.add(m)); + back.markups.forEach(m => markups.add(m)) } else { - (back && back.markups || []).forEach(m => { + ;((back && back.markups) || []).forEach(m => { if (m.isForwardInclusive()) { - markups.add(m); + markups.add(m) } - }); - (forward && forward.markups || []).forEach(m => { + }) + ;((forward && forward.markups) || []).forEach(m => { if (m.isBackwardInclusive()) { - markups.add(m); + markups.add(m) } - }); + }) } } } else { - this.walkMarkerableSections(range, (section) => { - forEach( - section.markupsInRange(range.trimTo(section)), - m => markups.add(m) - ); - }); + this.walkMarkerableSections(range, section => { + forEach(section.markupsInRange(range.trimTo(section)), m => markups.add(m)) + }) } - return markups.toArray(); + return markups.toArray() } walkAllLeafSections(callback) { - let range = this.headPosition().toRange(this.tailPosition()); - return this.walkLeafSections(range, callback); + let range = this.headPosition().toRange(this.tailPosition()) + return this.walkLeafSections(range, callback) } walkLeafSections(range, callback) { - const { head, tail } = range; + const { head, tail } = range - let index = 0; - let nextSection, shouldStop; - let currentSection = head.section; + let index = 0 + let nextSection, shouldStop + let currentSection = head.section while (currentSection) { - nextSection = this._nextLeafSection(currentSection); - shouldStop = currentSection === tail.section; + nextSection = this._nextLeafSection(currentSection) + shouldStop = currentSection === tail.section - callback(currentSection, index); - index++; + callback(currentSection, index) + index++ if (shouldStop) { - break; + break } else { - currentSection = nextSection; + currentSection = nextSection } } } @@ -159,30 +156,32 @@ class Post { walkMarkerableSections(range, callback) { this.walkLeafSections(range, section => { if (section.isMarkerable) { - callback(section); + callback(section) } - }); + }) } // return the next section that has markers after this one, // possibly skipping non-markerable sections _nextLeafSection(section) { - if (!section) { return null; } + if (!section) { + return null + } - const next = section.next; + const next = section.next if (next) { if (next.isLeafSection) { - return next; + return next } else if (next.items) { - return next.items.head; + return next.items.head } else { - assert('Cannot determine next section from non-leaf-section', false); + 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); + return this._nextLeafSection(section.parent) } } @@ -191,53 +190,48 @@ class Post { * @return {Post} A new post, constrained to {range} */ trimTo(range) { - const post = this.builder.createPost(); - const { builder } = this; - const { head, tail } = range; - const tailNotSelected = tail.offset === 0 && head.section !== tail.section; + const post = this.builder.createPost() + const { builder } = this + const { head, tail } = range + const tailNotSelected = tail.offset === 0 && head.section !== tail.section let sectionParent = post, - listParent = null; + listParent = null this.walkLeafSections(range, section => { - let newSection; + let newSection if (section.isMarkerable) { if (section.isListItem) { if (listParent) { - sectionParent = null; + sectionParent = null } else { - listParent = builder.createListSection(section.parent.tagName); - post.sections.append(listParent); - sectionParent = null; + listParent = builder.createListSection(section.parent.tagName) + post.sections.append(listParent) + sectionParent = null } - newSection = builder.createListItem(); - listParent.items.append(newSection); + newSection = builder.createListItem() + listParent.items.append(newSection) } else { - listParent = null; - sectionParent = post; - const tagName = tailNotSelected && tail.section === section ? - 'p' : - section.tagName; - newSection = builder.createMarkupSection(tagName); + listParent = null + sectionParent = post + const tagName = tailNotSelected && tail.section === section ? 'p' : section.tagName + newSection = builder.createMarkupSection(tagName) } - let currentRange = range.trimTo(section); - forEach( - section.markersFor(currentRange.headSectionOffset, currentRange.tailSectionOffset), - m => newSection.markers.append(m) - ); + let currentRange = range.trimTo(section) + forEach(section.markersFor(currentRange.headSectionOffset, currentRange.tailSectionOffset), m => + newSection.markers.append(m) + ) } else { - newSection = tailNotSelected && tail.section === section ? - builder.createMarkupSection('p') : - section.clone(); + newSection = tailNotSelected && tail.section === section ? builder.createMarkupSection('p') : section.clone() - sectionParent = post; + sectionParent = post } if (sectionParent) { - sectionParent.sections.append(newSection); + sectionParent.sections.append(newSection) } - }); - return post; + }) + return post } } -export default Post; +export default Post diff --git a/src/js/models/render-node.js b/src/js/models/render-node.js index 6807e0a7c..8ad21aa9b 100644 --- a/src/js/models/render-node.js +++ b/src/js/models/render-node.js @@ -1,92 +1,95 @@ -import LinkedItem from 'mobiledoc-kit/utils/linked-item'; -import LinkedList from 'mobiledoc-kit/utils/linked-list'; -import { containsNode } from 'mobiledoc-kit/utils/dom-utils'; -import assert from 'mobiledoc-kit/utils/assert'; +import LinkedItem from 'mobiledoc-kit/utils/linked-item' +import LinkedList from 'mobiledoc-kit/utils/linked-list' +import { containsNode } from 'mobiledoc-kit/utils/dom-utils' +import assert from 'mobiledoc-kit/utils/assert' export default 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; + 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; + this.markupElement = null // RenderNodes for Atoms use these properties - this.headTextNode = null; - this.tailTextNode = null; - this.atomNode = null; + this.headTextNode = null + this.tailTextNode = null + this.atomNode = null // RenderNodes for cards use this property - this.cardNode = null; + this.cardNode = null } isAttached() { - assert('Cannot check if a renderNode is attached without an element.', - !!this.element); - return containsNode(this.renderTree.rootElement, this.element); + 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() - }); + adoptItem: item => (item.parent = this), + freeItem: item => item.destroy(), + }) } - return this._childNodes; + return this._childNodes } scheduleForRemoval() { - this.isRemoved = true; - if (this.parent) { this.parent.markDirty(); } + this.isRemoved = true + if (this.parent) { + this.parent.markDirty() + } } markDirty() { - this.isDirty = true; - if (this.parent) { this.parent.markDirty(); } + this.isDirty = true + if (this.parent) { + this.parent.markDirty() + } } get isRendered() { - return !!this.element; + return !!this.element } markClean() { - this.isDirty = false; + this.isDirty = false } set element(element) { - const currentElement = this._element; - this._element = element; + const currentElement = this._element + this._element = element if (currentElement) { - this.renderTree.removeElementRenderNode(currentElement); + this.renderTree.removeElementRenderNode(currentElement) } if (element) { - this.renderTree.setElementRenderNode(element, this); + this.renderTree.setElementRenderNode(element, this) } } get element() { - return this._element; + return this._element } set cursorElement(cursorElement) { - this._cursorElement = cursorElement; + this._cursorElement = cursorElement } get cursorElement() { - return this._cursorElement || this.element; + return this._cursorElement || this.element } destroy() { - this.element = null; - this.parent = null; - this.postNode = null; - this.renderTree = null; + this.element = null + this.parent = null + this.postNode = null + this.renderTree = null } reparsesMutationOfChildNode(node) { if (this.postNode.isCardSection) { - return !containsNode(this.cardNode.element, node); + return !containsNode(this.cardNode.element, node) } else if (this.postNode.isAtom) { - return !containsNode(this.atomNode.element, node); + return !containsNode(this.atomNode.element, node) } - return true; + return true } } diff --git a/src/js/models/render-tree.js b/src/js/models/render-tree.js index dbfc67fb2..d4d3d9195 100644 --- a/src/js/models/render-tree.js +++ b/src/js/models/render-tree.js @@ -1,70 +1,70 @@ -import RenderNode from 'mobiledoc-kit/models/render-node'; -import ElementMap from '../utils/element-map'; +import RenderNode from 'mobiledoc-kit/models/render-node' +import ElementMap from '../utils/element-map' export default class RenderTree { constructor(rootPostNode) { - this._rootNode = this.buildRenderNode(rootPostNode); - this._elements = new ElementMap(); + this._rootNode = this.buildRenderNode(rootPostNode) + this._elements = new ElementMap() } /* * @return {RenderNode} The root render node in this tree */ get rootNode() { - return this._rootNode; + return this._rootNode } /** * @return {Boolean} */ get isDirty() { - return this.rootNode && this.rootNode.isDirty; + return this.rootNode && this.rootNode.isDirty } /* * @return {DOMNode} The root DOM element in this tree */ get rootElement() { - return this.rootNode.element; + return this.rootNode.element } /* * @param {DOMNode} element * @return {RenderNode} The renderNode for this element, if any */ getElementRenderNode(element) { - return this._elements.get(element); + return this._elements.get(element) } setElementRenderNode(element, renderNode) { - this._elements.set(element, renderNode); + this._elements.set(element, renderNode) } removeElementRenderNode(element) { - this._elements.remove(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; + findRenderNodeFromElement(element, conditionFn = () => true) { + let renderNode while (element) { - renderNode = this.getElementRenderNode(element); + renderNode = this.getElementRenderNode(element) if (renderNode && conditionFn(renderNode)) { - return renderNode; + return renderNode } // continue loop - element = element.parentNode; + element = element.parentNode // stop if we are at the root element if (element === this.rootElement) { if (conditionFn(this.rootNode)) { - return this.rootNode; + return this.rootNode } else { - return; + return } } } } buildRenderNode(postNode) { - const renderNode = new RenderNode(postNode, this); - postNode.renderNode = renderNode; - return renderNode; + const renderNode = new RenderNode(postNode, this) + postNode.renderNode = renderNode + return renderNode } } diff --git a/src/js/models/types.js b/src/js/models/types.js index aa2f6f0c9..4105d6297 100644 --- a/src/js/models/types.js +++ b/src/js/models/types.js @@ -1,9 +1,9 @@ -export const MARKUP_SECTION_TYPE = 'markup-section'; -export const LIST_SECTION_TYPE = 'list-section'; -export const MARKUP_TYPE = 'markup'; -export const MARKER_TYPE = 'marker'; -export const POST_TYPE = 'post'; -export const LIST_ITEM_TYPE = 'list-item'; -export const CARD_TYPE = 'card-section'; -export const IMAGE_SECTION_TYPE = 'image-section'; -export const ATOM_TYPE = 'atom'; \ No newline at end of file +export const MARKUP_SECTION_TYPE = 'markup-section' +export const LIST_SECTION_TYPE = 'list-section' +export const MARKUP_TYPE = 'markup' +export const MARKER_TYPE = 'marker' +export const POST_TYPE = 'post' +export const LIST_ITEM_TYPE = 'list-item' +export const CARD_TYPE = 'card-section' +export const IMAGE_SECTION_TYPE = 'image-section' +export const ATOM_TYPE = 'atom' diff --git a/src/js/parsers/dom.js b/src/js/parsers/dom.js index 91447d772..d20318c37 100644 --- a/src/js/parsers/dom.js +++ b/src/js/parsers/dom.js @@ -1,99 +1,77 @@ -import { - NO_BREAK_SPACE, - TAB_CHARACTER, - ATOM_CLASS_NAME -} from '../renderers/editor-dom'; -import { - MARKUP_SECTION_TYPE, - LIST_SECTION_TYPE, - LIST_ITEM_TYPE -} from '../models/types'; -import { - isTextNode, - isCommentNode, - isElementNode, - getAttributes, - normalizeTagName -} from '../utils/dom-utils'; -import { - any, - detect, - forEach -} from '../utils/array-utils'; -import { TAB } from 'mobiledoc-kit/utils/characters'; -import { ZWNJ } from 'mobiledoc-kit/renderers/editor-dom'; - -import SectionParser from 'mobiledoc-kit/parsers/section'; -import Markup from 'mobiledoc-kit/models/markup'; - -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'); +import { NO_BREAK_SPACE, TAB_CHARACTER, ATOM_CLASS_NAME } from '../renderers/editor-dom' +import { MARKUP_SECTION_TYPE, LIST_SECTION_TYPE, LIST_ITEM_TYPE } from '../models/types' +import { isTextNode, isCommentNode, isElementNode, getAttributes, normalizeTagName } from '../utils/dom-utils' +import { any, detect, forEach } from '../utils/array-utils' +import { TAB } from 'mobiledoc-kit/utils/characters' +import { ZWNJ } from 'mobiledoc-kit/renderers/editor-dom' + +import SectionParser from 'mobiledoc-kit/parsers/section' +import Markup from 'mobiledoc-kit/models/markup' + +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') export function transformHTMLText(textContent) { - let text = textContent; - text = text.replace(NO_BREAK_SPACE_REGEX, ' '); - text = text.replace(TAB_CHARACTER_REGEX, TAB); - return text; + let text = textContent + text = text.replace(NO_BREAK_SPACE_REGEX, ' ') + text = text.replace(TAB_CHARACTER_REGEX, TAB) + return text } export 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+$/, ''); + 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); + 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); + let childNodes = element.childNodes || [] + let googleDocsContainer = detect(childNodes, isGoogleDocsContainer) if (googleDocsContainer) { - return googleDocsContainer; + return googleDocsContainer } else { - return element; + return element } } const TAG_REMAPPING = { - 'b': 'strong', - 'i': 'em' -}; + b: 'strong', + i: 'em', +} function remapTagName(tagName) { - let normalized = normalizeTagName(tagName); - let remapped = TAG_REMAPPING[normalized]; - return remapped || normalized; + let normalized = normalizeTagName(tagName) + let remapped = TAG_REMAPPING[normalized] + return remapped || normalized } function trim(str) { - return str.replace(/^\s+/, '').replace(/\s+$/, ''); + 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); + let currentNode = parent + + if (isTextNode(currentNode) || (isElementNode(currentNode) && currentNode.classList.contains(ATOM_CLASS_NAME))) { + callback(currentNode) } else { - currentNode = currentNode.firstChild; + currentNode = currentNode.firstChild while (currentNode) { - walkMarkerableNodes(currentNode, callback); - currentNode = currentNode.nextSibling; + walkMarkerableNodes(currentNode, callback) + currentNode = currentNode.nextSibling } } } @@ -103,83 +81,83 @@ function walkMarkerableNodes(parent, callback) { * @private */ class DOMParser { - constructor(builder, options={}) { - this.builder = builder; - this.sectionParser = new SectionParser(this.builder, options); + constructor(builder, options = {}) { + this.builder = builder + this.sectionParser = new SectionParser(this.builder, options) } parse(element) { - const post = this.builder.createPost(); - let rootElement = detectRootElement(element); + const post = this.builder.createPost() + let rootElement = detectRootElement(element) this._eachChildNode(rootElement, child => { - let sections = this.parseSections(child); - this.appendSections(post, sections); - }); + 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)); + forEach(post.sections, section => trimSectionText(section)) - return post; + return post } appendSections(post, sections) { - forEach(sections, section => this.appendSection(post, section)); + forEach(sections, section => this.appendSection(post, section)) } appendSection(post, section) { if ( section.isBlank || - (section.isMarkerable && - trim(section.text) === "" && - !any(section.markers, marker => marker.isAtom)) + (section.isMarkerable && trim(section.text) === '' && !any(section.markers, marker => marker.isAtom)) ) { - return; + return } - let lastSection = post.sections.tail; - if (lastSection && - lastSection._inferredTagName && - section._inferredTagName && - lastSection.tagName === section.tagName) { - lastSection.join(section); + let lastSection = post.sections.tail + if ( + lastSection && + lastSection._inferredTagName && + section._inferredTagName && + lastSection.tagName === section.tagName + ) { + lastSection.join(section) } else { - post.sections.append(section); + post.sections.append(section) } } _eachChildNode(element, callback) { - let nodes = isTextNode(element) ? [element] : element.childNodes; - forEach(nodes, node => callback(node)); + let nodes = isTextNode(element) ? [element] : element.childNodes + forEach(nodes, node => callback(node)) } parseSections(element) { - return this.sectionParser.parse(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; + let markups = [] + let currentNode = textNode.parentNode while (currentNode && currentNode !== rootNode) { - let markup = this.markupFromNode(currentNode); + let markup = this.markupFromNode(currentNode) if (markup) { - markups.push(markup); + markups.push(markup) } - currentNode = currentNode.parentNode; + currentNode = currentNode.parentNode } - return markups; + 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); + let tagName = remapTagName(node.tagName) + let attributes = getAttributes(node) + return this.builder.createMarkup(tagName, attributes) } } @@ -188,135 +166,134 @@ class DOMParser { reparseSection(section, renderTree) { switch (section.type) { case LIST_SECTION_TYPE: - return this.reparseListSection(section, renderTree); + return this.reparseListSection(section, renderTree) case LIST_ITEM_TYPE: - return this.reparseListItem(section, renderTree); + return this.reparseListItem(section, renderTree) case MARKUP_SECTION_TYPE: - return this.reparseMarkupSection(section, renderTree); + return this.reparseMarkupSection(section, renderTree) default: - return; // can only parse the above types + return // can only parse the above types } } reparseMarkupSection(section, renderTree) { - return this._reparseSectionContainingMarkers(section, renderTree); + return this._reparseSectionContainingMarkers(section, renderTree) } reparseListItem(listItem, renderTree) { - return this._reparseSectionContainingMarkers(listItem, renderTree); + return this._reparseSectionContainingMarkers(listItem, renderTree) } reparseListSection(listSection, renderTree) { - listSection.items.forEach(li => this.reparseListItem(li, renderTree)); + listSection.items.forEach(li => this.reparseListItem(li, renderTree)) } _reparseSectionContainingMarkers(section, renderTree) { - let element = section.renderNode.element; - let seenRenderNodes = []; - let previousMarker; + let element = section.renderNode.element + let seenRenderNodes = [] + let previousMarker - walkMarkerableNodes(element, (node) => { - let marker; - let renderNode = renderTree.getElementRenderNode(node); + 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); + let text = transformHTMLText(node.textContent) + let markups = this.collectMarkups(node, element) if (text.length) { - marker = renderNode.postNode; - marker.value = text; - marker.markups = markups; + marker = renderNode.postNode + marker.value = text + marker.markups = markups } else { - renderNode.scheduleForRemoval(); + renderNode.scheduleForRemoval() } } else if (renderNode.postNode.isAtom) { - let { headTextNode, tailTextNode } = renderNode; + let { headTextNode, tailTextNode } = renderNode if (headTextNode.textContent !== ZWNJ) { - let value = headTextNode.textContent.replace(new RegExp(ZWNJ, 'g'), ''); - headTextNode.textContent = ZWNJ; + let value = headTextNode.textContent.replace(new RegExp(ZWNJ, 'g'), '') + headTextNode.textContent = ZWNJ if (previousMarker && previousMarker.isMarker) { - previousMarker.value += value; + previousMarker.value += value if (previousMarker.renderNode) { - previousMarker.renderNode.markDirty(); + 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); + 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; + 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; + let nextMarker = renderNode.postNode.next if (nextMarker.renderNode) { - let nextValue = nextMarker.renderNode.element.textContent; - nextMarker.renderNode.element.textContent = value + nextValue; + let nextValue = nextMarker.renderNode.element.textContent + nextMarker.renderNode.element.textContent = value + nextValue } else { - let nextValue = value + nextMarker.value; - nextMarker.value = nextValue; + 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); + let postNode = renderNode.postNode + let newMarkups = postNode.markups.slice() + let newMarker = this.builder.createMarker(value, newMarkups) - section.markers.insertAfter(newMarker, postNode); + section.markers.insertAfter(newMarker, postNode) - let newRenderNode = renderTree.buildRenderNode(newMarker); - seenRenderNodes.push(newRenderNode); + let newRenderNode = renderTree.buildRenderNode(newMarker) + seenRenderNodes.push(newRenderNode) - newRenderNode.markDirty(); - section.renderNode.markDirty(); + newRenderNode.markDirty() + section.renderNode.markDirty() - section.renderNode.childNodes.insertAfter(newRenderNode, renderNode); + section.renderNode.childNodes.insertAfter(newRenderNode, renderNode) } } if (renderNode) { - marker = renderNode.postNode; + 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); + 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); + seenRenderNodes.push(renderNode) } - previousMarker = marker; - }); + previousMarker = marker + }) - let renderNode = section.renderNode.childNodes.head; + let renderNode = section.renderNode.childNodes.head while (renderNode) { if (seenRenderNodes.indexOf(renderNode) === -1) { - renderNode.scheduleForRemoval(); + renderNode.scheduleForRemoval() } - renderNode = renderNode.next; + renderNode = renderNode.next } } } -export default DOMParser; +export default DOMParser diff --git a/src/js/parsers/html.js b/src/js/parsers/html.js index b12472761..20cb92b43 100644 --- a/src/js/parsers/html.js +++ b/src/js/parsers/html.js @@ -1,12 +1,12 @@ -import { parseHTML } from '../utils/dom-utils'; -import assert from '../utils/assert'; -import DOMParser from './dom'; +import { parseHTML } from '../utils/dom-utils' +import assert from '../utils/assert' +import DOMParser from './dom' export default class HTMLParser { - constructor(builder, options={}) { - assert('Must pass builder to HTMLParser', builder); - this.builder = builder; - this.options = options; + constructor(builder, options = {}) { + assert('Must pass builder to HTMLParser', builder) + this.builder = builder + this.options = options } /** @@ -14,8 +14,8 @@ export default class HTMLParser { * @return {Post} A post abstract */ parse(html) { - let dom = parseHTML(html); - let parser = new DOMParser(this.builder, this.options); - return parser.parse(dom); + let dom = parseHTML(html) + let parser = new DOMParser(this.builder, this.options) + return parser.parse(dom) } } diff --git a/src/js/parsers/mobiledoc/0-2.js b/src/js/parsers/mobiledoc/0-2.js index 9e77348e4..3410c7f00 100644 --- a/src/js/parsers/mobiledoc/0-2.js +++ b/src/js/parsers/mobiledoc/0-2.js @@ -2,120 +2,120 @@ import { MOBILEDOC_MARKUP_SECTION_TYPE, MOBILEDOC_IMAGE_SECTION_TYPE, MOBILEDOC_LIST_SECTION_TYPE, - MOBILEDOC_CARD_SECTION_TYPE -} from 'mobiledoc-kit/renderers/mobiledoc/0-2'; -import { kvArrayToObject, filter } from "../../utils/array-utils"; -import assert from 'mobiledoc-kit/utils/assert'; + MOBILEDOC_CARD_SECTION_TYPE, +} from 'mobiledoc-kit/renderers/mobiledoc/0-2' +import { kvArrayToObject, filter } from '../../utils/array-utils' +import assert from 'mobiledoc-kit/utils/assert' /* * Parses from mobiledoc -> post */ export default class MobiledocParser { constructor(builder) { - this.builder = builder; + this.builder = builder } /** * @param {Mobiledoc} * @return {Post} */ - parse({sections: sectionData}) { + parse({ sections: sectionData }) { try { - const markerTypes = sectionData[0]; - const sections = sectionData[1]; + const markerTypes = sectionData[0] + const sections = sectionData[1] - const post = this.builder.createPost(); + const post = this.builder.createPost() - this.markups = []; - this.markerTypes = this.parseMarkerTypes(markerTypes); - this.parseSections(sections, post); + this.markups = [] + this.markerTypes = this.parseMarkerTypes(markerTypes) + this.parseSections(sections, post) - return post; + return post } catch (e) { - assert(`Unable to parse mobiledoc: ${e.message}`, false); + assert(`Unable to parse mobiledoc: ${e.message}`, false) } } parseMarkerTypes(markerTypes) { - return markerTypes.map((markerType) => this.parseMarkerType(markerType)); + return markerTypes.map(markerType => this.parseMarkerType(markerType)) } parseMarkerType([tagName, attributesArray]) { - const attributesObject = kvArrayToObject(attributesArray || []); - return this.builder.createMarkup(tagName, attributesObject); + const attributesObject = kvArrayToObject(attributesArray || []) + return this.builder.createMarkup(tagName, attributesObject) } parseSections(sections, post) { - sections.forEach((section) => this.parseSection(section, post)); + sections.forEach(section => this.parseSection(section, post)) } parseSection(section, post) { - let [type] = section; - switch(type) { + let [type] = section + switch (type) { case MOBILEDOC_MARKUP_SECTION_TYPE: - this.parseMarkupSection(section, post); - break; + this.parseMarkupSection(section, post) + break case MOBILEDOC_IMAGE_SECTION_TYPE: - this.parseImageSection(section, post); - break; + this.parseImageSection(section, post) + break case MOBILEDOC_CARD_SECTION_TYPE: - this.parseCardSection(section, post); - break; + this.parseCardSection(section, post) + break case MOBILEDOC_LIST_SECTION_TYPE: - this.parseListSection(section, post); - break; + this.parseListSection(section, post) + break default: - assert(`Unexpected section type ${type}`, false); + assert(`Unexpected section type ${type}`, false) } } parseCardSection([, name, payload], post) { - const section = this.builder.createCardSection(name, payload); - post.sections.append(section); + const section = this.builder.createCardSection(name, payload) + post.sections.append(section) } parseImageSection([, src], post) { - const section = this.builder.createImageSection(src); - post.sections.append(section); + 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); + 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); - }); + section.markers.remove(m) + }) } parseListSection([, tagName, items], post) { - const section = this.builder.createListSection(tagName); - post.sections.append(section); - this.parseListItems(items, section); + const section = this.builder.createListSection(tagName) + post.sections.append(section) + this.parseListItems(items, section) } parseListItems(items, section) { - items.forEach(i => this.parseListItem(i, section)); + items.forEach(i => this.parseListItem(i, section)) } parseListItem(markers, section) { - const item = this.builder.createListItem(); - this.parseMarkers(markers, item); - section.items.append(item); + const item = this.builder.createListItem() + this.parseMarkers(markers, item) + section.items.append(item) } parseMarkers(markers, parent) { - markers.forEach(m => this.parseMarker(m, 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); + 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) } } diff --git a/src/js/parsers/mobiledoc/0-3-1.js b/src/js/parsers/mobiledoc/0-3-1.js index 398cfa0fb..80c810767 100644 --- a/src/js/parsers/mobiledoc/0-3-1.js +++ b/src/js/parsers/mobiledoc/0-3-1.js @@ -4,17 +4,17 @@ import { MOBILEDOC_LIST_SECTION_TYPE, MOBILEDOC_CARD_SECTION_TYPE, MOBILEDOC_MARKUP_MARKER_TYPE, - MOBILEDOC_ATOM_MARKER_TYPE -} from 'mobiledoc-kit/renderers/mobiledoc/0-3-1'; -import { kvArrayToObject, filter } from "../../utils/array-utils"; -import assert from 'mobiledoc-kit/utils/assert'; + MOBILEDOC_ATOM_MARKER_TYPE, +} from 'mobiledoc-kit/renderers/mobiledoc/0-3-1' +import { kvArrayToObject, filter } from '../../utils/array-utils' +import assert from 'mobiledoc-kit/utils/assert' /* * Parses from mobiledoc -> post */ export default class MobiledocParser { constructor(builder) { - this.builder = builder; + this.builder = builder } /** @@ -23,144 +23,144 @@ export default class MobiledocParser { */ parse({ sections, markups: markerTypes, cards: cardTypes, atoms: atomTypes }) { try { - const post = this.builder.createPost(); + 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); + this.markups = [] + this.markerTypes = this.parseMarkerTypes(markerTypes) + this.cardTypes = this.parseCardTypes(cardTypes) + this.atomTypes = this.parseAtomTypes(atomTypes) + this.parseSections(sections, post) - return post; + return post } catch (e) { - assert(`Unable to parse mobiledoc: ${e.message}`, false); + assert(`Unable to parse mobiledoc: ${e.message}`, false) } } parseMarkerTypes(markerTypes) { - return markerTypes.map((markerType) => this.parseMarkerType(markerType)); + return markerTypes.map(markerType => this.parseMarkerType(markerType)) } parseMarkerType([tagName, attributesArray]) { - const attributesObject = kvArrayToObject(attributesArray || []); - return this.builder.createMarkup(tagName, attributesObject); + const attributesObject = kvArrayToObject(attributesArray || []) + return this.builder.createMarkup(tagName, attributesObject) } parseCardTypes(cardTypes) { - return cardTypes.map((cardType) => this.parseCardType(cardType)); + return cardTypes.map(cardType => this.parseCardType(cardType)) } parseCardType([cardName, cardPayload]) { - return [cardName, cardPayload]; + return [cardName, cardPayload] } parseAtomTypes(atomTypes) { - return atomTypes.map((atomType) => this.parseAtomType(atomType)); + return atomTypes.map(atomType => this.parseAtomType(atomType)) } parseAtomType([atomName, atomValue, atomPayload]) { - return [atomName, atomValue, atomPayload]; + return [atomName, atomValue, atomPayload] } parseSections(sections, post) { - sections.forEach((section) => this.parseSection(section, post)); + sections.forEach(section => this.parseSection(section, post)) } parseSection(section, post) { - let [type] = section; - switch(type) { + let [type] = section + switch (type) { case MOBILEDOC_MARKUP_SECTION_TYPE: - this.parseMarkupSection(section, post); - break; + this.parseMarkupSection(section, post) + break case MOBILEDOC_IMAGE_SECTION_TYPE: - this.parseImageSection(section, post); - break; + this.parseImageSection(section, post) + break case MOBILEDOC_CARD_SECTION_TYPE: - this.parseCardSection(section, post); - break; + this.parseCardSection(section, post) + break case MOBILEDOC_LIST_SECTION_TYPE: - this.parseListSection(section, post); - break; + this.parseListSection(section, post) + break default: - assert('Unexpected section type ${type}', false); + assert('Unexpected section type ${type}', false) } } getAtomTypeFromIndex(index) { - const atomType = this.atomTypes[index]; - assert(`No atom definition found at index ${index}`, !!atomType); - return atomType; + 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; + 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); + 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); + 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); + 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); - }); + section.markers.remove(m) + }) } parseListSection([, tagName, items], post) { - const section = this.builder.createListSection(tagName); - post.sections.append(section); - this.parseListItems(items, section); + const section = this.builder.createListSection(tagName) + post.sections.append(section) + this.parseListItems(items, section) } parseListItems(items, section) { - items.forEach(i => this.parseListItem(i, section)); + items.forEach(i => this.parseListItem(i, section)) } parseListItem(markers, section) { - const item = this.builder.createListItem(); - this.parseMarkers(markers, item); - section.items.append(item); + const item = this.builder.createListItem() + this.parseMarkers(markers, item) + section.items.append(item) } parseMarkers(markers, parent) { - markers.forEach(m => this.parseMarker(m, parent)); + markers.forEach(m => this.parseMarker(m, parent)) } parseMarker([type, markerTypeIndexes, closeCount, value], parent) { markerTypeIndexes.forEach(index => { - this.markups.push(this.markerTypes[index]); - }); + this.markups.push(this.markerTypes[index]) + }) - const marker = this.buildMarkerType(type, value); - parent.markers.append(marker); + const marker = this.buildMarkerType(type, value) + parent.markers.append(marker) - this.markups = this.markups.slice(0, this.markups.length-closeCount); + 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()); + 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()); + const [atomName, atomValue, atomPayload] = this.getAtomTypeFromIndex(value) + return this.builder.createAtom(atomName, atomValue, atomPayload, this.markups.slice()) } default: - assert(`Unexpected marker type ${type}`, false); + assert(`Unexpected marker type ${type}`, false) } } } diff --git a/src/js/parsers/mobiledoc/0-3-2.js b/src/js/parsers/mobiledoc/0-3-2.js index 7f9c65344..1b5d6e74d 100644 --- a/src/js/parsers/mobiledoc/0-3-2.js +++ b/src/js/parsers/mobiledoc/0-3-2.js @@ -4,19 +4,19 @@ import { MOBILEDOC_LIST_SECTION_TYPE, MOBILEDOC_CARD_SECTION_TYPE, MOBILEDOC_MARKUP_MARKER_TYPE, - MOBILEDOC_ATOM_MARKER_TYPE -} from '../../renderers/mobiledoc/0-3-2'; + MOBILEDOC_ATOM_MARKER_TYPE, +} from '../../renderers/mobiledoc/0-3-2' -import { kvArrayToObject, filter } from '../../utils/array-utils'; -import assert from '../../utils/assert'; -import { entries } from '../../utils/object-utils'; +import { kvArrayToObject, filter } from '../../utils/array-utils' +import assert from '../../utils/assert' +import { entries } from '../../utils/object-utils' /* * Parses from mobiledoc -> post */ export default class MobiledocParser { constructor(builder) { - this.builder = builder; + this.builder = builder } /** @@ -25,154 +25,154 @@ export default class MobiledocParser { */ parse({ sections, markups: markerTypes, cards: cardTypes, atoms: atomTypes }) { try { - const post = this.builder.createPost(); + 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); + this.markups = [] + this.markerTypes = this.parseMarkerTypes(markerTypes) + this.cardTypes = this.parseCardTypes(cardTypes) + this.atomTypes = this.parseAtomTypes(atomTypes) + this.parseSections(sections, post) - return post; + return post } catch (e) { - assert(`Unable to parse mobiledoc: ${e.message}`, false); + assert(`Unable to parse mobiledoc: ${e.message}`, false) } } parseMarkerTypes(markerTypes) { - return markerTypes.map((markerType) => this.parseMarkerType(markerType)); + return markerTypes.map(markerType => this.parseMarkerType(markerType)) } parseMarkerType([tagName, attributesArray]) { - const attributesObject = kvArrayToObject(attributesArray || []); - return this.builder.createMarkup(tagName, attributesObject); + const attributesObject = kvArrayToObject(attributesArray || []) + return this.builder.createMarkup(tagName, attributesObject) } parseCardTypes(cardTypes) { - return cardTypes.map((cardType) => this.parseCardType(cardType)); + return cardTypes.map(cardType => this.parseCardType(cardType)) } parseCardType([cardName, cardPayload]) { - return [cardName, cardPayload]; + return [cardName, cardPayload] } parseAtomTypes(atomTypes) { - return atomTypes.map((atomType) => this.parseAtomType(atomType)); + return atomTypes.map(atomType => this.parseAtomType(atomType)) } parseAtomType([atomName, atomValue, atomPayload]) { - return [atomName, atomValue, atomPayload]; + return [atomName, atomValue, atomPayload] } parseSections(sections, post) { - sections.forEach((section) => this.parseSection(section, post)); + sections.forEach(section => this.parseSection(section, post)) } parseSection(section, post) { - let [type] = section; - switch(type) { + let [type] = section + switch (type) { case MOBILEDOC_MARKUP_SECTION_TYPE: - this.parseMarkupSection(section, post); - break; + this.parseMarkupSection(section, post) + break case MOBILEDOC_IMAGE_SECTION_TYPE: - this.parseImageSection(section, post); - break; + this.parseImageSection(section, post) + break case MOBILEDOC_CARD_SECTION_TYPE: - this.parseCardSection(section, post); - break; + this.parseCardSection(section, post) + break case MOBILEDOC_LIST_SECTION_TYPE: - this.parseListSection(section, post); - break; + this.parseListSection(section, post) + break default: - assert('Unexpected section type ${type}', false); + assert('Unexpected section type ${type}', false) } } getAtomTypeFromIndex(index) { - const atomType = this.atomTypes[index]; - assert(`No atom definition found at index ${index}`, !!atomType); - return atomType; + 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; + 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); + 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); + 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); + const section = this.builder.createMarkupSection(tagName) + post.sections.append(section) if (attributesArray) { entries(kvArrayToObject(attributesArray)).forEach(([key, value]) => { - section.setAttribute(key, value); - }); + section.setAttribute(key, value) + }) } - this.parseMarkers(markers, 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); - }); + section.markers.remove(m) + }) } parseListSection([, tagName, items, attributesArray], post) { - const section = this.builder.createListSection(tagName); - post.sections.append(section); + const section = this.builder.createListSection(tagName) + post.sections.append(section) if (attributesArray) { entries(kvArrayToObject(attributesArray)).forEach(([key, value]) => { - section.setAttribute(key, value); - }); + section.setAttribute(key, value) + }) } - this.parseListItems(items, section); + this.parseListItems(items, section) } parseListItems(items, section) { - items.forEach(i => this.parseListItem(i, section)); + items.forEach(i => this.parseListItem(i, section)) } parseListItem(markers, section) { - const item = this.builder.createListItem(); - this.parseMarkers(markers, item); - section.items.append(item); + const item = this.builder.createListItem() + this.parseMarkers(markers, item) + section.items.append(item) } parseMarkers(markers, parent) { - markers.forEach(m => this.parseMarker(m, parent)); + markers.forEach(m => this.parseMarker(m, parent)) } parseMarker([type, markerTypeIndexes, closeCount, value], parent) { markerTypeIndexes.forEach(index => { - this.markups.push(this.markerTypes[index]); - }); + this.markups.push(this.markerTypes[index]) + }) - const marker = this.buildMarkerType(type, value); - parent.markers.append(marker); + const marker = this.buildMarkerType(type, value) + parent.markers.append(marker) - this.markups = this.markups.slice(0, this.markups.length-closeCount); + 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()); + 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()); + const [atomName, atomValue, atomPayload] = this.getAtomTypeFromIndex(value) + return this.builder.createAtom(atomName, atomValue, atomPayload, this.markups.slice()) } default: - assert(`Unexpected marker type ${type}`, false); + assert(`Unexpected marker type ${type}`, false) } } } diff --git a/src/js/parsers/mobiledoc/0-3.js b/src/js/parsers/mobiledoc/0-3.js index 00addadfd..03b6f00c1 100644 --- a/src/js/parsers/mobiledoc/0-3.js +++ b/src/js/parsers/mobiledoc/0-3.js @@ -4,17 +4,17 @@ import { MOBILEDOC_LIST_SECTION_TYPE, MOBILEDOC_CARD_SECTION_TYPE, MOBILEDOC_MARKUP_MARKER_TYPE, - MOBILEDOC_ATOM_MARKER_TYPE -} from 'mobiledoc-kit/renderers/mobiledoc/0-3'; -import { kvArrayToObject, filter } from "../../utils/array-utils"; -import assert from 'mobiledoc-kit/utils/assert'; + MOBILEDOC_ATOM_MARKER_TYPE, +} from 'mobiledoc-kit/renderers/mobiledoc/0-3' +import { kvArrayToObject, filter } from '../../utils/array-utils' +import assert from 'mobiledoc-kit/utils/assert' /* * Parses from mobiledoc -> post */ export default class MobiledocParser { constructor(builder) { - this.builder = builder; + this.builder = builder } /** @@ -23,144 +23,144 @@ export default class MobiledocParser { */ parse({ sections, markups: markerTypes, cards: cardTypes, atoms: atomTypes }) { try { - const post = this.builder.createPost(); + 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); + this.markups = [] + this.markerTypes = this.parseMarkerTypes(markerTypes) + this.cardTypes = this.parseCardTypes(cardTypes) + this.atomTypes = this.parseAtomTypes(atomTypes) + this.parseSections(sections, post) - return post; + return post } catch (e) { - assert(`Unable to parse mobiledoc: ${e.message}`, false); + assert(`Unable to parse mobiledoc: ${e.message}`, false) } } parseMarkerTypes(markerTypes) { - return markerTypes.map((markerType) => this.parseMarkerType(markerType)); + return markerTypes.map(markerType => this.parseMarkerType(markerType)) } parseMarkerType([tagName, attributesArray]) { - const attributesObject = kvArrayToObject(attributesArray || []); - return this.builder.createMarkup(tagName, attributesObject); + const attributesObject = kvArrayToObject(attributesArray || []) + return this.builder.createMarkup(tagName, attributesObject) } parseCardTypes(cardTypes) { - return cardTypes.map((cardType) => this.parseCardType(cardType)); + return cardTypes.map(cardType => this.parseCardType(cardType)) } parseCardType([cardName, cardPayload]) { - return [cardName, cardPayload]; + return [cardName, cardPayload] } parseAtomTypes(atomTypes) { - return atomTypes.map((atomType) => this.parseAtomType(atomType)); + return atomTypes.map(atomType => this.parseAtomType(atomType)) } parseAtomType([atomName, atomValue, atomPayload]) { - return [atomName, atomValue, atomPayload]; + return [atomName, atomValue, atomPayload] } parseSections(sections, post) { - sections.forEach((section) => this.parseSection(section, post)); + sections.forEach(section => this.parseSection(section, post)) } parseSection(section, post) { - let [type] = section; - switch(type) { + let [type] = section + switch (type) { case MOBILEDOC_MARKUP_SECTION_TYPE: - this.parseMarkupSection(section, post); - break; + this.parseMarkupSection(section, post) + break case MOBILEDOC_IMAGE_SECTION_TYPE: - this.parseImageSection(section, post); - break; + this.parseImageSection(section, post) + break case MOBILEDOC_CARD_SECTION_TYPE: - this.parseCardSection(section, post); - break; + this.parseCardSection(section, post) + break case MOBILEDOC_LIST_SECTION_TYPE: - this.parseListSection(section, post); - break; + this.parseListSection(section, post) + break default: - assert('Unexpected section type ${type}', false); + assert('Unexpected section type ${type}', false) } } getAtomTypeFromIndex(index) { - const atomType = this.atomTypes[index]; - assert(`No atom definition found at index ${index}`, !!atomType); - return atomType; + 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; + 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); + 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); + 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); + 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); - }); + section.markers.remove(m) + }) } parseListSection([, tagName, items], post) { - const section = this.builder.createListSection(tagName); - post.sections.append(section); - this.parseListItems(items, section); + const section = this.builder.createListSection(tagName) + post.sections.append(section) + this.parseListItems(items, section) } parseListItems(items, section) { - items.forEach(i => this.parseListItem(i, section)); + items.forEach(i => this.parseListItem(i, section)) } parseListItem(markers, section) { - const item = this.builder.createListItem(); - this.parseMarkers(markers, item); - section.items.append(item); + const item = this.builder.createListItem() + this.parseMarkers(markers, item) + section.items.append(item) } parseMarkers(markers, parent) { - markers.forEach(m => this.parseMarker(m, parent)); + markers.forEach(m => this.parseMarker(m, parent)) } parseMarker([type, markerTypeIndexes, closeCount, value], parent) { markerTypeIndexes.forEach(index => { - this.markups.push(this.markerTypes[index]); - }); + this.markups.push(this.markerTypes[index]) + }) - const marker = this.buildMarkerType(type, value); - parent.markers.append(marker); + const marker = this.buildMarkerType(type, value) + parent.markers.append(marker) - this.markups = this.markups.slice(0, this.markups.length-closeCount); + 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()); + 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()); + const [atomName, atomValue, atomPayload] = this.getAtomTypeFromIndex(value) + return this.builder.createAtom(atomName, atomValue, atomPayload, this.markups.slice()) } default: - assert(`Unexpected marker type ${type}`, false); + assert(`Unexpected marker type ${type}`, false) } } } diff --git a/src/js/parsers/mobiledoc/index.js b/src/js/parsers/mobiledoc/index.js index 5c111ca72..50194df92 100644 --- a/src/js/parsers/mobiledoc/index.js +++ b/src/js/parsers/mobiledoc/index.js @@ -1,33 +1,32 @@ -import MobiledocParser_0_2 from './0-2'; -import MobiledocParser_0_3 from './0-3'; -import MobiledocParser_0_3_1 from './0-3-1'; -import MobiledocParser_0_3_2 from './0-3-2'; +import MobiledocParser_0_2 from './0-2' +import MobiledocParser_0_3 from './0-3' +import MobiledocParser_0_3_1 from './0-3-1' +import MobiledocParser_0_3_2 from './0-3-2' -import { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_2 } from 'mobiledoc-kit/renderers/mobiledoc/0-2'; -import { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3 } from 'mobiledoc-kit/renderers/mobiledoc/0-3'; -import { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3_1 } from 'mobiledoc-kit/renderers/mobiledoc/0-3-1'; -import { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3_2 } from 'mobiledoc-kit/renderers/mobiledoc/0-3-2'; -import assert from 'mobiledoc-kit/utils/assert'; +import { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_2 } from 'mobiledoc-kit/renderers/mobiledoc/0-2' +import { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3 } from 'mobiledoc-kit/renderers/mobiledoc/0-3' +import { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3_1 } from 'mobiledoc-kit/renderers/mobiledoc/0-3-1' +import { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3_2 } from 'mobiledoc-kit/renderers/mobiledoc/0-3-2' +import assert from 'mobiledoc-kit/utils/assert' function parseVersion(mobiledoc) { - return mobiledoc.version; + return mobiledoc.version } export default { parse(builder, mobiledoc) { - let version = parseVersion(mobiledoc); + let version = parseVersion(mobiledoc) switch (version) { case MOBILEDOC_VERSION_0_2: - return new MobiledocParser_0_2(builder).parse(mobiledoc); + return new MobiledocParser_0_2(builder).parse(mobiledoc) case MOBILEDOC_VERSION_0_3: - return new MobiledocParser_0_3(builder).parse(mobiledoc); + return new MobiledocParser_0_3(builder).parse(mobiledoc) case MOBILEDOC_VERSION_0_3_1: - return new MobiledocParser_0_3_1(builder).parse(mobiledoc); + return new MobiledocParser_0_3_1(builder).parse(mobiledoc) case MOBILEDOC_VERSION_0_3_2: - return new MobiledocParser_0_3_2(builder).parse(mobiledoc); + return new MobiledocParser_0_3_2(builder).parse(mobiledoc) default: - assert(`Unknown version of mobiledoc parser requested: ${version}`, - false); + assert(`Unknown version of mobiledoc parser requested: ${version}`, false) } - } -}; + }, +} diff --git a/src/js/parsers/section.js b/src/js/parsers/section.js index 93d6fcc29..3a1c70f0b 100644 --- a/src/js/parsers/section.js +++ b/src/js/parsers/section.js @@ -1,54 +1,26 @@ -import { - DEFAULT_TAG_NAME, - VALID_MARKUP_SECTION_TAGNAMES -} from 'mobiledoc-kit/models/markup-section'; - -import { - VALID_LIST_SECTION_TAGNAMES -} from 'mobiledoc-kit/models/list-section'; - -import { - VALID_LIST_ITEM_TAGNAMES -} from 'mobiledoc-kit/models/list-item'; - -import { - LIST_SECTION_TYPE, - LIST_ITEM_TYPE, - MARKUP_SECTION_TYPE -} from 'mobiledoc-kit/models/types'; - -import { - VALID_MARKUP_TAGNAMES -} from 'mobiledoc-kit/models/markup'; - -import { - getAttributes, - normalizeTagName, - isTextNode, - isCommentNode, - NODE_TYPES -} from 'mobiledoc-kit/utils/dom-utils'; - -import { - any, - forEach, - contains -} from 'mobiledoc-kit/utils/array-utils'; - -import { - transformHTMLText, - trimSectionText -} from '../parsers/dom'; - -import assert from '../utils/assert'; - -const SKIPPABLE_ELEMENT_TAG_NAMES = [ - 'style', 'head', 'title', 'meta' -].map(normalizeTagName); - -const NEWLINES = /\s*\n\s*/g; +import { DEFAULT_TAG_NAME, VALID_MARKUP_SECTION_TAGNAMES } from 'mobiledoc-kit/models/markup-section' + +import { VALID_LIST_SECTION_TAGNAMES } from 'mobiledoc-kit/models/list-section' + +import { VALID_LIST_ITEM_TAGNAMES } from 'mobiledoc-kit/models/list-item' + +import { LIST_SECTION_TYPE, LIST_ITEM_TYPE, MARKUP_SECTION_TYPE } from 'mobiledoc-kit/models/types' + +import { VALID_MARKUP_TAGNAMES } from 'mobiledoc-kit/models/markup' + +import { getAttributes, normalizeTagName, isTextNode, isCommentNode, NODE_TYPES } from 'mobiledoc-kit/utils/dom-utils' + +import { any, forEach, contains } from 'mobiledoc-kit/utils/array-utils' + +import { transformHTMLText, trimSectionText } from '../parsers/dom' + +import assert from '../utils/assert' + +const SKIPPABLE_ELEMENT_TAG_NAMES = ['style', 'head', 'title', 'meta'].map(normalizeTagName) + +const NEWLINES = /\s*\n\s*/g function sanitize(text) { - return text.replace(NEWLINES, ' '); + return text.replace(NEWLINES, ' ') } /** @@ -57,124 +29,124 @@ function sanitize(text) { * @private */ class SectionParser { - constructor(builder, options={}) { - this.builder = builder; - this.plugins = options.plugins || []; + constructor(builder, options = {}) { + this.builder = builder + this.plugins = options.plugins || [] } parse(element) { if (this._isSkippable(element)) { - return []; + return [] } - this.sections = []; - this.state = {}; + this.sections = [] + this.state = {} - this._updateStateFromElement(element); + this._updateStateFromElement(element) - let finished = false; + 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); + finished = this.runPlugins(element) } if (!finished) { - let childNodes = isTextNode(element) ? [element] : element.childNodes; + let childNodes = isTextNode(element) ? [element] : element.childNodes forEach(childNodes, el => { - this.parseNode(el); - }); + this.parseNode(el) + }) } - this._closeCurrentSection(); + this._closeCurrentSection() - return this.sections; + return this.sections } runPlugins(node) { - let isNodeFinished = false; + let isNodeFinished = false let env = { - addSection: (section) => { + addSection: section => { // avoid creating empty paragraphs due to wrapper elements around // parser-plugin-handled elements if (this.state.section && this.state.section.isMarkerable && !this.state.section.text && !this.state.text) { - this.state.section = null; + this.state.section = null } else { - this._closeCurrentSection(); + this._closeCurrentSection() } - this.sections.push(section); + this.sections.push(section) }, - addMarkerable: (marker) => { - let { state } = this; - let { section } = state; + addMarkerable: marker => { + let { state } = this + let { section } = state // if the first element doesn't create it's own state and it's plugin // handler uses `addMarkerable` we won't have a section yet if (!section) { - state.text = ''; - state.section = this.builder.createMarkupSection(normalizeTagName('p')); - section = state.section; + state.text = '' + state.section = this.builder.createMarkupSection(normalizeTagName('p')) + section = state.section } assert( 'Markerables can only be appended to markup sections and list item sections', section && section.isMarkerable - ); + ) if (state.text) { - this._createMarker(); + this._createMarker() } - section.markers.append(marker); + section.markers.append(marker) }, nodeFinished() { - isNodeFinished = true; - } - }; - for (let i=0; i. // deals with typical case of
  • Text

  • Text

  • if ( @@ -212,99 +180,99 @@ class SectionParser { tagName === 'p' && !node.nextSibling && contains(VALID_LIST_ITEM_TAGNAMES, normalizeTagName(node.parentElement.tagName)) - ) { - this.parseElementNode(node); - return; + ) { + 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; + this.state.section = null } else { - this._closeCurrentSection(); + this._closeCurrentSection() } - this._updateStateFromElement(node); + 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(); + this._closeCurrentSection() - forEach(node.childNodes, (node) => { - this.parseNode(node); - }); - return; + forEach(node.childNodes, node => { + this.parseNode(node) + }) + return } } switch (node.nodeType) { case NODE_TYPES.TEXT: - this.parseTextNode(node); - break; + this.parseTextNode(node) + break case NODE_TYPES.ELEMENT: - this.parseElementNode(node); - break; + this.parseElementNode(node) + break } } parseElementNode(element) { - let { state } = this; + let { state } = this - const markups = this._markupsFromElement(element); + const markups = this._markupsFromElement(element) if (markups.length && state.text.length && state.section.isMarkerable) { - this._createMarker(); + this._createMarker() } - state.markups.push(...markups); + state.markups.push(...markups) - forEach(element.childNodes, (node) => { - this.parseNode(node); - }); + 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(); + this._createMarker() } // pop the current markups from the stack - state.markups.splice(-markups.length, markups.length); + state.markups.splice(-markups.length, markups.length) } parseTextNode(textNode) { - let { state } = this; - state.text += sanitize(textNode.textContent); + let { state } = this + state.text += sanitize(textNode.textContent) } _updateStateFromElement(element) { if (isCommentNode(element)) { - return; + return } - let { state } = this; - state.section = this._createSectionFromElement(element); - state.markups = this._markupsFromElement(element); - state.text = ''; + 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]; + let { sections, state } = this + let lastSection = sections[sections.length - 1] if (!state.section) { - return; + return } // close a trailing text node if it exists if (state.text.length && state.section.isMarkerable) { - this._createMarker(); + 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); + trimSectionText(state.section) + lastSection.items.append(state.section) } else { // avoid creating empty markup sections, especially useful for indented source if ( @@ -312,85 +280,83 @@ class SectionParser { !state.section.text.trim() && !any(state.section.markers, marker => marker.isAtom) ) { - state.section = null; - state.text = ''; - return; + 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.pop() } - sections.push(state.section); + sections.push(state.section) } - state.section = null; - state.text = ''; + state.section = null + state.text = '' } _markupsFromElement(element) { - let { builder } = this; - let markups = []; + let { builder } = this + let markups = [] if (isTextNode(element)) { - return markups; + return markups } - const tagName = normalizeTagName(element.tagName); + const tagName = normalizeTagName(element.tagName) if (this._isValidMarkupForElement(tagName, element)) { - markups.push(builder.createMarkup(tagName, getAttributes(element))); + markups.push(builder.createMarkup(tagName, getAttributes(element))) } - this._markupsFromElementStyle(element).forEach( - markup => markups.push(markup) - ); + this._markupsFromElementStyle(element).forEach(markup => markups.push(markup)) - return markups; + return markups } _isValidMarkupForElement(tagName, element) { if (VALID_MARKUP_TAGNAMES.indexOf(tagName) === -1) { - return false; + return false } else if (tagName === 'b') { // google docs add a that should not // create a "b" markup - return element.style.fontWeight !== 'normal'; + return element.style.fontWeight !== 'normal' } - return true; + return true } _markupsFromElementStyle(element) { - let { builder } = this; - let markups = []; - let { fontStyle, fontWeight } = element.style; + let { builder } = this + let markups = [] + let { fontStyle, fontWeight } = element.style if (fontStyle === 'italic') { - markups.push(builder.createMarkup('em')); + markups.push(builder.createMarkup('em')) } if (fontWeight === 'bold' || fontWeight === '700') { - markups.push(builder.createMarkup('strong')); + markups.push(builder.createMarkup('strong')) } - return markups; + 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 = ''; + 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; + tagName, + inferredTagName = false if (isTextNode(element)) { - tagName = DEFAULT_TAG_NAME; - sectionType = MARKUP_SECTION_TYPE; - inferredTagName = true; + tagName = DEFAULT_TAG_NAME + sectionType = MARKUP_SECTION_TYPE + inferredTagName = true } else { - tagName = normalizeTagName(element.tagName); + tagName = normalizeTagName(element.tagName) // blockquote>p is valid html and should be treated as a blockquote section // rather than a plain markup section @@ -399,58 +365,58 @@ class SectionParser { element.parentElement && normalizeTagName(element.parentElement.tagName) === 'blockquote' ) { - tagName = 'blockquote'; + tagName = 'blockquote' } if (contains(VALID_LIST_SECTION_TAGNAMES, tagName)) { - sectionType = LIST_SECTION_TYPE; + sectionType = LIST_SECTION_TYPE } else if (contains(VALID_LIST_ITEM_TAGNAMES, tagName)) { - sectionType = LIST_ITEM_TYPE; + sectionType = LIST_ITEM_TYPE } else if (contains(VALID_MARKUP_SECTION_TAGNAMES, tagName)) { - sectionType = MARKUP_SECTION_TYPE; + sectionType = MARKUP_SECTION_TYPE } else { - sectionType = MARKUP_SECTION_TYPE; - tagName = DEFAULT_TAG_NAME; - inferredTagName = true; + sectionType = MARKUP_SECTION_TYPE + tagName = DEFAULT_TAG_NAME + inferredTagName = true } } - return {sectionType, tagName, inferredTagName}; + return { sectionType, tagName, inferredTagName } } _createSectionFromElement(element) { if (isCommentNode(element)) { - return; + return } - let { builder } = this; - let section; - let {tagName, sectionType, inferredTagName} = - this._getSectionDetails(element); + let { builder } = this + let section + let { tagName, sectionType, inferredTagName } = this._getSectionDetails(element) switch (sectionType) { case LIST_SECTION_TYPE: - section = builder.createListSection(tagName); - break; + section = builder.createListSection(tagName) + break case LIST_ITEM_TYPE: - section = builder.createListItem(); - break; + section = builder.createListItem() + break case MARKUP_SECTION_TYPE: - section = builder.createMarkupSection(tagName); - section._inferredTagName = inferredTagName; - break; + section = builder.createMarkupSection(tagName) + section._inferredTagName = inferredTagName + break default: - assert('Cannot parse section from element', false); + assert('Cannot parse section from element', false) } - return section; + return section } _isSkippable(element) { - return element.nodeType === NODE_TYPES.ELEMENT && - contains(SKIPPABLE_ELEMENT_TAG_NAMES, - normalizeTagName(element.tagName)); + return ( + element.nodeType === NODE_TYPES.ELEMENT && + contains(SKIPPABLE_ELEMENT_TAG_NAMES, normalizeTagName(element.tagName)) + ) } } -export default SectionParser; +export default SectionParser diff --git a/src/js/parsers/text.js b/src/js/parsers/text.js index 6c4a9546d..65cbe72ad 100644 --- a/src/js/parsers/text.js +++ b/src/js/parsers/text.js @@ -1,33 +1,27 @@ -import assert from 'mobiledoc-kit/utils/assert'; -import { - MARKUP_SECTION_TYPE, - LIST_SECTION_TYPE -} from 'mobiledoc-kit/models/types'; -import { - DEFAULT_TAG_NAME as DEFAULT_MARKUP_SECTION_TAG_NAME -} from 'mobiledoc-kit/models/markup-section'; +import assert from 'mobiledoc-kit/utils/assert' +import { MARKUP_SECTION_TYPE, LIST_SECTION_TYPE } from 'mobiledoc-kit/models/types' +import { DEFAULT_TAG_NAME as DEFAULT_MARKUP_SECTION_TAG_NAME } from 'mobiledoc-kit/models/markup-section' -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 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') -export const SECTION_BREAK = LF; +export const SECTION_BREAK = LF function normalizeLineEndings(text) { - return text.replace(CR_LF_REGEX, LF) - .replace(CR_REGEX, LF); + return text.replace(CR_LF_REGEX, LF).replace(CR_REGEX, LF) } export default class TextParser { constructor(builder, options) { - this.builder = builder; - this.options = options; + this.builder = builder + this.options = options - this.post = this.builder.createPost(); - this.prevSection = null; + this.post = this.builder.createPost() + this.prevSection = null } /** @@ -35,62 +29,63 @@ export default class TextParser { * @return {Post} a post abstract */ parse(text) { - text = normalizeLineEndings(text); + text = normalizeLineEndings(text) text.split(SECTION_BREAK).forEach(text => { - let section = this._parseSection(text); - this._appendSection(section); - }); + let section = this._parseSection(text) + this._appendSection(section) + }) - return this.post; + return this.post } _parseSection(text) { let tagName = DEFAULT_MARKUP_SECTION_TAG_NAME, - type = MARKUP_SECTION_TYPE, - section; + type = MARKUP_SECTION_TYPE, + section if (UL_LI_REGEX.test(text)) { - tagName = 'ul'; - type = LIST_SECTION_TYPE; - text = text.match(UL_LI_REGEX)[1]; + 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]; + tagName = 'ol' + type = LIST_SECTION_TYPE + text = text.match(OL_LI_REGEX)[1] } - let markers = [this.builder.createMarker(text)]; + 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; + 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; + section = this.builder.createMarkupSection(tagName, markers) + break default: - assert(`Unknown type encountered ${type}`, false); + assert(`Unknown type encountered ${type}`, false) } - return section; + return section } _appendSection(section) { let isSameListSection = section.isListSection && - this.prevSection && this.prevSection.isListSection && - this.prevSection.tagName === section.tagName; + this.prevSection && + this.prevSection.isListSection && + this.prevSection.tagName === section.tagName if (isSameListSection) { section.items.forEach(item => { - this.prevSection.items.append(item.clone()); - }); + this.prevSection.items.append(item.clone()) + }) } else { - this.post.sections.insertAfter(section, this.prevSection); - this.prevSection = section; + this.post.sections.insertAfter(section, this.prevSection) + this.prevSection = section } } } diff --git a/src/js/renderers/editor-dom.js b/src/js/renderers/editor-dom.js index 3d4bb83e4..95f001dbf 100644 --- a/src/js/renderers/editor-dom.js +++ b/src/js/renderers/editor-dom.js @@ -1,6 +1,6 @@ -import CardNode from 'mobiledoc-kit/models/card-node'; -import { detect, forEach } from 'mobiledoc-kit/utils/array-utils'; -import AtomNode from 'mobiledoc-kit/models/atom-node'; +import CardNode from 'mobiledoc-kit/models/card-node' +import { detect, forEach } from 'mobiledoc-kit/utils/array-utils' +import AtomNode from 'mobiledoc-kit/models/atom-node' import { POST_TYPE, MARKUP_SECTION_TYPE, @@ -9,48 +9,47 @@ import { MARKER_TYPE, IMAGE_SECTION_TYPE, CARD_TYPE, - ATOM_TYPE -} from '../models/types'; -import { startsWith, endsWith } from '../utils/string-utils'; -import { addClassName, removeClassName } from '../utils/dom-utils'; -import { MARKUP_SECTION_ELEMENT_NAMES } from '../models/markup-section'; -import assert from '../utils/assert'; -import { TAB } from 'mobiledoc-kit/utils/characters'; - -export const CARD_ELEMENT_CLASS_NAME = '__mobiledoc-card'; -export const NO_BREAK_SPACE = '\u00A0'; -export const TAB_CHARACTER = '\u2003'; -export const SPACE = ' '; -export const ZWNJ = '\u200c'; -export const ATOM_CLASS_NAME = '-mobiledoc-kit__atom'; -export const EDITOR_HAS_NO_CONTENT_CLASS_NAME = '__has-no-content'; -export const EDITOR_ELEMENT_CLASS_NAME = '__mobiledoc-editor'; + ATOM_TYPE, +} from '../models/types' +import { startsWith, endsWith } from '../utils/string-utils' +import { addClassName, removeClassName } from '../utils/dom-utils' +import { MARKUP_SECTION_ELEMENT_NAMES } from '../models/markup-section' +import assert from '../utils/assert' +import { TAB } from 'mobiledoc-kit/utils/characters' + +export const CARD_ELEMENT_CLASS_NAME = '__mobiledoc-card' +export const NO_BREAK_SPACE = '\u00A0' +export const TAB_CHARACTER = '\u2003' +export const SPACE = ' ' +export const ZWNJ = '\u200c' +export const ATOM_CLASS_NAME = '-mobiledoc-kit__atom' +export const EDITOR_HAS_NO_CONTENT_CLASS_NAME = '__has-no-content' +export const EDITOR_ELEMENT_CLASS_NAME = '__mobiledoc-editor' function createElementFromMarkup(doc, markup) { - let element = doc.createElement(markup.tagName); + let element = doc.createElement(markup.tagName) Object.keys(markup.attributes).forEach(k => { - element.setAttribute(k, markup.attributes[k]); - }); - return element; + 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); -}; +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); + 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 @@ -58,76 +57,80 @@ function renderHTMLText(marker) { // 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; + 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); + if ( + marker.isMarker && + startsWithSpace(text) && + (!marker.prev || (marker.prev.isMarker && endsWithSpace(marker.prev.value))) + ) { + text = NO_BREAK_SPACE + text.substr(1) } - return text; + 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; + while ( + parentElement && + element.parentNode !== parentElement && + element.parentNode !== document.body // ensure the while loop stops + ) { + element = element.parentNode } - return element; + return element } function setSectionAttributesOnElement(section, element) { section.eachAttribute((key, value) => { - element.setAttribute(key, value); - }); + element.setAttribute(key, value) + }) } function renderMarkupSection(section) { - let element; + let element if (MARKUP_SECTION_ELEMENT_NAMES.indexOf(section.tagName) !== -1) { - element = document.createElement(section.tagName); + element = document.createElement(section.tagName) } else { - element = document.createElement('div'); - addClassName(element, section.tagName); + element = document.createElement('div') + addClassName(element, section.tagName) } - setSectionAttributesOnElement(section, element); + setSectionAttributesOnElement(section, element) - return element; + return element } function renderListSection(section) { - let element = document.createElement(section.tagName); + let element = document.createElement(section.tagName) - setSectionAttributesOnElement(section, element); + setSectionAttributesOnElement(section, element) - return element; + return element } function renderListItem() { - return document.createElement('li'); + return document.createElement('li') } function renderCursorPlaceholder() { - return document.createElement('br'); + return document.createElement('br') } function renderInlineCursorPlaceholder() { - return document.createTextNode(ZWNJ); + 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 }; + 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 } } /** @@ -136,65 +139,64 @@ function renderCard() { * @private */ function wrapElement(element, openedMarkups) { - let wrappedElement = element; + 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; + 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; + return wrappedElement } // Attach the element to its parent element at the correct position based on the // previousRenderNode -function attachElementToParent(element, parentElement, previousRenderNode=null) { +function attachElementToParent(element, parentElement, previousRenderNode = null) { if (previousRenderNode) { - let previousSibling = previousRenderNode.element; - let previousSiblingPenultimate = penultimateParentOf(previousSibling, - parentElement); - parentElement.insertBefore(element, previousSiblingPenultimate.nextSibling); + let previousSibling = previousRenderNode.element + let previousSiblingPenultimate = penultimateParentOf(previousSibling, parentElement) + parentElement.insertBefore(element, previousSiblingPenultimate.nextSibling) } else { - parentElement.insertBefore(element, parentElement.firstChild); + parentElement.insertBefore(element, parentElement.firstChild) } } function renderAtom(atom, element, previousRenderNode) { - let atomElement = document.createElement('span'); - atomElement.contentEditable = false; + let atomElement = document.createElement('span') + atomElement.contentEditable = false - let wrapper = document.createElement('span'); - addClassName(wrapper, ATOM_CLASS_NAME); - let headTextNode = renderInlineCursorPlaceholder(); - let tailTextNode = renderInlineCursorPlaceholder(); + 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); + wrapper.appendChild(headTextNode) + wrapper.appendChild(atomElement) + wrapper.appendChild(tailTextNode) - let wrappedElement = wrapElement(wrapper, atom.openedMarkups); - attachElementToParent(wrappedElement, element, previousRenderNode); + let wrappedElement = wrapElement(wrapper, atom.openedMarkups) + attachElementToParent(wrappedElement, element, previousRenderNode) return { markupElement: wrappedElement, wrapper, atomElement, headTextNode, - tailTextNode - }; + tailTextNode, + } } function getNextMarkerElement(renderNode) { - let element = renderNode.element.parentNode; - let marker = renderNode.postNode; - let closedCount = marker.closedMarkups.length; + let element = renderNode.element.parentNode + let marker = renderNode.postNode + let closedCount = marker.closedMarkups.length while (closedCount--) { - element = element.parentNode; + element = element.parentNode } - return element; + return element } /** @@ -210,294 +212,272 @@ function getNextMarkerElement(renderNode) { * @private */ function renderMarker(marker, parentElement, previousRenderNode) { - let text = renderHTMLText(marker); + let text = renderHTMLText(marker) - let element = document.createTextNode(text); - let markupElement = wrapElement(element, marker.openedMarkups); - attachElementToParent(markupElement, parentElement, previousRenderNode); + let element = document.createTextNode(text) + let markupElement = wrapElement(element, marker.openedMarkups) + attachElementToParent(markupElement, parentElement, previousRenderNode) - return { element, markupElement }; + 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; +function attachRenderNodeElementToDOM(renderNode, originalElement = null) { + const element = renderNode.element + const hasRendered = !!originalElement if (hasRendered) { - let parentElement = renderNode.parent.element; - parentElement.replaceChild(element, originalElement); + let parentElement = renderNode.parent.element + parentElement.replaceChild(element, originalElement) } else { - let parentElement, nextSiblingElement; + let parentElement, nextSiblingElement if (renderNode.prev) { - let previousElement = renderNode.prev.element; - parentElement = previousElement.parentNode; - nextSiblingElement = previousElement.nextSibling; + let previousElement = renderNode.prev.element + parentElement = previousElement.parentNode + nextSiblingElement = previousElement.nextSibling } else { - parentElement = renderNode.parent.element; - nextSiblingElement = parentElement.firstChild; + parentElement = renderNode.parent.element + nextSiblingElement = parentElement.firstChild } - parentElement.insertBefore(element, nextSiblingElement); + parentElement.insertBefore(element, nextSiblingElement) } } function removeRenderNodeSectionFromParent(renderNode, section) { - const parent = renderNode.parent.postNode; - parent.sections.remove(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); + renderNode.element.parentNode.removeChild(renderNode.element) } } -function validateCards(cards=[]) { +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; + 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=[]) { +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; + 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 { 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; + 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); + 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 - ); + assert(`Unknown card "${cardName}" found, but no unknownCardHandler is defined`, !!this.unknownCardHandler) return { name: cardName, type: 'dom', render: this.unknownCardHandler, - edit: this.unknownCardHandler - }; + edit: this.unknownCardHandler, + } } _findAtom(atomName) { - let atom = detect(this.atoms, atom => atom.name === atomName); - return atom || this._createUnknownAtom(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 - ); + assert(`Unknown atom "${atomName}" found, but no unknownAtomHandler is defined`, !!this.unknownAtomHandler) return { name: atomName, type: 'dom', - render: this.unknownAtomHandler - }; + render: this.unknownAtomHandler, + } } [POST_TYPE](renderNode, post, visit) { if (!renderNode.element) { - renderNode.element = document.createElement('div'); + renderNode.element = document.createElement('div') } - addClassName(renderNode.element, EDITOR_ELEMENT_CLASS_NAME); + addClassName(renderNode.element, EDITOR_ELEMENT_CLASS_NAME) if (post.hasContent) { - removeClassName(renderNode.element, EDITOR_HAS_NO_CONTENT_CLASS_NAME); + removeClassName(renderNode.element, EDITOR_HAS_NO_CONTENT_CLASS_NAME) } else { - addClassName(renderNode.element, EDITOR_HAS_NO_CONTENT_CLASS_NAME); + addClassName(renderNode.element, EDITOR_HAS_NO_CONTENT_CLASS_NAME) } - visit(renderNode, post.sections); + visit(renderNode, post.sections) } [MARKUP_SECTION_TYPE](renderNode, section, visit) { - const originalElement = renderNode.element; + 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); + renderNode.element = renderMarkupSection(section) + renderNode.cursorElement = null + attachRenderNodeElementToDOM(renderNode, originalElement) if (section.isBlank) { - let cursorPlaceholder = renderCursorPlaceholder(); - renderNode.element.appendChild(cursorPlaceholder); - renderNode.cursorElement = cursorPlaceholder; + let cursorPlaceholder = renderCursorPlaceholder() + renderNode.element.appendChild(cursorPlaceholder) + renderNode.cursorElement = cursorPlaceholder } else { - const visitAll = true; - visit(renderNode, section.markers, visitAll); + const visitAll = true + visit(renderNode, section.markers, visitAll) } } [LIST_SECTION_TYPE](renderNode, section, visit) { - const originalElement = renderNode.element; + const originalElement = renderNode.element - renderNode.element = renderListSection(section); - attachRenderNodeElementToDOM(renderNode, originalElement); + renderNode.element = renderListSection(section) + attachRenderNodeElementToDOM(renderNode, originalElement) - const visitAll = true; - visit(renderNode, section.items, visitAll); + 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); + renderNode.element = renderListItem() + renderNode.cursorElement = null + attachRenderNodeElementToDOM(renderNode, null) if (item.isBlank) { - let cursorPlaceholder = renderCursorPlaceholder(); - renderNode.element.appendChild(cursorPlaceholder); - renderNode.cursorElement = cursorPlaceholder; + let cursorPlaceholder = renderCursorPlaceholder() + renderNode.element.appendChild(cursorPlaceholder) + renderNode.cursorElement = cursorPlaceholder } else { - const visitAll = true; - visit(renderNode, item.markers, visitAll); + const visitAll = true + visit(renderNode, item.markers, visitAll) } } [MARKER_TYPE](renderNode, marker) { - let parentElement; + let parentElement if (renderNode.prev) { - parentElement = getNextMarkerElement(renderNode.prev); + parentElement = getNextMarkerElement(renderNode.prev) } else { - parentElement = renderNode.parent.element; + parentElement = renderNode.parent.element } - let { element, markupElement } = - renderMarker(marker, parentElement, renderNode.prev); + let { element, markupElement } = renderMarker(marker, parentElement, renderNode.prev) - renderNode.element = element; - renderNode.markupElement = markupElement; + 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; + renderNode.element.src = section.src } } else { - let element = document.createElement('img'); - element.src = section.src; + let element = document.createElement('img') + element.src = section.src if (renderNode.prev) { - let previousElement = renderNode.prev.element; - let nextElement = previousElement.nextSibling; + let previousElement = renderNode.prev.element + let nextElement = previousElement.nextSibling if (nextElement) { - nextElement.parentNode.insertBefore(element, nextElement); + nextElement.parentNode.insertBefore(element, nextElement) } } if (!element.parentNode) { - renderNode.parent.element.appendChild(element); + renderNode.parent.element.appendChild(element) } - renderNode.element = element; + renderNode.element = element } } [CARD_TYPE](renderNode, section) { - const originalElement = renderNode.element; - const {editor, options} = this; + const originalElement = renderNode.element + const { editor, options } = this - const card = this._findCard(section.name); + const card = this._findCard(section.name) - let { wrapper, cardElement } = renderCard(); - renderNode.element = wrapper; - attachRenderNodeElementToDOM(renderNode, originalElement); + let { wrapper, cardElement } = renderCard() + renderNode.element = wrapper + attachRenderNodeElementToDOM(renderNode, originalElement) - const cardNode = new CardNode( - editor, card, section, cardElement, options); - renderNode.cardNode = cardNode; + const cardNode = new CardNode(editor, card, section, cardElement, options) + renderNode.cardNode = cardNode - const initialMode = section._initialMode; - cardNode[initialMode](); + const initialMode = section._initialMode + cardNode[initialMode]() } [ATOM_TYPE](renderNode, atomModel) { - let parentElement; + let parentElement if (renderNode.prev) { - parentElement = getNextMarkerElement(renderNode.prev); + parentElement = getNextMarkerElement(renderNode.prev) } else { - parentElement = renderNode.parent.element; + 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; + 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); + atomNode = new AtomNode(editor, atom, atomModel, atomElement, options) } else { // retarget atomNode to new atom element - atomNode.element = atomElement; + atomNode.element = atomElement } - atomNode.render(); + atomNode.render() - renderNode.atomNode = atomNode; - renderNode.element = wrapper; - renderNode.headTextNode = headTextNode; - renderNode.tailTextNode = tailTextNode; - renderNode.markupElement = markupElement; + 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); + assert('post destruction is not supported by the renderer', false) }, [MARKUP_SECTION_TYPE](renderNode, section) { - removeRenderNodeSectionFromParent(renderNode, section); - removeRenderNodeElementFromParent(renderNode); + removeRenderNodeSectionFromParent(renderNode, section) + removeRenderNodeElementFromParent(renderNode) }, [LIST_SECTION_TYPE](renderNode, section) { - removeRenderNodeSectionFromParent(renderNode, section); - removeRenderNodeElementFromParent(renderNode); + removeRenderNodeSectionFromParent(renderNode, section) + removeRenderNodeElementFromParent(renderNode) }, [LIST_ITEM_TYPE](renderNode, li) { - removeRenderNodeSectionFromParent(renderNode, li); - removeRenderNodeElementFromParent(renderNode); + removeRenderNodeSectionFromParent(renderNode, li) + removeRenderNodeElementFromParent(renderNode) }, [MARKER_TYPE](renderNode, marker) { @@ -507,57 +487,57 @@ let destroyHooks = { // 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; + return } - let { markupElement } = renderNode; + let { markupElement } = renderNode if (marker.section) { - marker.section.markers.remove(marker); + marker.section.markers.remove(marker) } if (markupElement.parentNode) { // if no parentNode, the browser already removed this element - markupElement.parentNode.removeChild(markupElement); + markupElement.parentNode.removeChild(markupElement) } }, [IMAGE_SECTION_TYPE](renderNode, section) { - removeRenderNodeSectionFromParent(renderNode, section); - removeRenderNodeElementFromParent(renderNode); + removeRenderNodeSectionFromParent(renderNode, section) + removeRenderNodeElementFromParent(renderNode) }, [CARD_TYPE](renderNode, section) { if (renderNode.cardNode) { - renderNode.cardNode.teardown(); + renderNode.cardNode.teardown() } - removeRenderNodeSectionFromParent(renderNode, section); - removeRenderNodeElementFromParent(renderNode); + removeRenderNodeSectionFromParent(renderNode, section) + removeRenderNodeElementFromParent(renderNode) }, [ATOM_TYPE](renderNode, atom) { if (renderNode.atomNode) { - renderNode.atomNode.teardown(); + renderNode.atomNode.teardown() } // an atom is a kind of marker so just call its destroy hook vs copying here - destroyHooks[MARKER_TYPE](renderNode, atom); - } -}; + 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; +function removeDestroyedChildren(parentNode, forceRemoval = false) { + let child = parentNode.childNodes.head + let nextChild, method while (child) { - nextChild = child.next; + 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); + 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; + child = nextChild } } @@ -565,58 +545,57 @@ function removeDestroyedChildren(parentNode, forceRemoval=false) { // create one, insert it into the tree, and return it function lookupNode(renderTree, parentNode, postNode, previousNode) { if (postNode.renderNode) { - return postNode.renderNode; + return postNode.renderNode } else { - const renderNode = renderTree.buildRenderNode(postNode); - parentNode.childNodes.insertAfter(renderNode, previousNode); - return renderNode; + const renderNode = renderTree.buildRenderNode(postNode) + parentNode.childNodes.insertAfter(renderNode, previousNode) + return renderNode } } export default class Renderer { constructor(editor, cards, atoms, unknownCardHandler, unknownAtomHandler, options) { - this.editor = editor; - this.visitor = new Visitor(editor, cards, atoms, unknownCardHandler, unknownAtomHandler, options); - this.nodes = []; - this.hasRendered = false; + this.editor = editor + this.visitor = new Visitor(editor, cards, atoms, unknownCardHandler, unknownAtomHandler, options) + this.nodes = [] + this.hasRendered = false } destroy() { if (!this.hasRendered) { - return; + return } - let renderNode = this.renderTree.rootNode; - let force = true; - removeDestroyedChildren(renderNode, force); + let renderNode = this.renderTree.rootNode + let force = true + removeDestroyedChildren(renderNode, force) } - visit(renderTree, parentNode, postNodes, visitAll=false) { - let previousNode; + visit(renderTree, parentNode, postNodes, visitAll = false) { + let previousNode postNodes.forEach(postNode => { - let node = lookupNode(renderTree, parentNode, postNode, previousNode); + let node = lookupNode(renderTree, parentNode, postNode, previousNode) if (node.isDirty || visitAll) { - this.nodes.push(node); + this.nodes.push(node) } - previousNode = node; - }); + previousNode = node + }) } render(renderTree) { - this.hasRendered = true; - this.renderTree = renderTree; - let renderNode = renderTree.rootNode; - let method, postNode; + 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(); + 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() } } } diff --git a/src/js/renderers/mobiledoc/0-2.js b/src/js/renderers/mobiledoc/0-2.js index 5e85061bd..a01c7b9ee 100644 --- a/src/js/renderers/mobiledoc/0-2.js +++ b/src/js/renderers/mobiledoc/0-2.js @@ -1,5 +1,5 @@ -import {visit, visitArray, compile} from '../../utils/compiler'; -import { objectToSortedKVArray } from '../../utils/array-utils'; +import { visit, visitArray, compile } from '../../utils/compiler' +import { objectToSortedKVArray } from '../../utils/array-utils' import { POST_TYPE, MARKUP_SECTION_TYPE, @@ -8,103 +8,103 @@ import { MARKER_TYPE, MARKUP_TYPE, IMAGE_SECTION_TYPE, - CARD_TYPE -} from '../../models/types'; + CARD_TYPE, +} from '../../models/types' -export const MOBILEDOC_VERSION = '0.2.0'; -export const MOBILEDOC_MARKUP_SECTION_TYPE = 1; -export const MOBILEDOC_IMAGE_SECTION_TYPE = 2; -export const MOBILEDOC_LIST_SECTION_TYPE = 3; -export const MOBILEDOC_CARD_SECTION_TYPE = 10; +export const MOBILEDOC_VERSION = '0.2.0' +export const MOBILEDOC_MARKUP_SECTION_TYPE = 1 +export const MOBILEDOC_IMAGE_SECTION_TYPE = 2 +export const MOBILEDOC_LIST_SECTION_TYPE = 3 +export const MOBILEDOC_CARD_SECTION_TYPE = 10 const visitor = { [POST_TYPE](node, opcodes) { - opcodes.push(['openPost']); - visitArray(visitor, node.sections, opcodes); + opcodes.push(['openPost']) + visitArray(visitor, node.sections, opcodes) }, [MARKUP_SECTION_TYPE](node, opcodes) { - opcodes.push(['openMarkupSection', node.tagName]); - visitArray(visitor, node.markers, 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); + opcodes.push(['openListSection', node.tagName]) + visitArray(visitor, node.items, opcodes) }, [LIST_ITEM_TYPE](node, opcodes) { - opcodes.push(['openListItem']); - visitArray(visitor, node.markers, opcodes); + opcodes.push(['openListItem']) + visitArray(visitor, node.markers, opcodes) }, [IMAGE_SECTION_TYPE](node, opcodes) { - opcodes.push(['openImageSection', node.src]); + opcodes.push(['openImageSection', node.src]) }, [CARD_TYPE](node, opcodes) { - opcodes.push(['openCardSection', node.name, node.payload]); + opcodes.push(['openCardSection', node.name, node.payload]) }, [MARKER_TYPE](node, opcodes) { - opcodes.push(['openMarker', node.closedMarkups.length, node.value]); - visitArray(visitor, node.openedMarkups, 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)]); - } -}; + opcodes.push(['openMarkup', node.tagName, objectToSortedKVArray(node.attributes)]) + }, +} const postOpcodeCompiler = { openMarker(closeCount, value) { - this.markupMarkerIds = []; - this.markers.push([ - this.markupMarkerIds, - 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]); + 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]); + this.items = [] + this.sections.push([MOBILEDOC_LIST_SECTION_TYPE, tagName, this.items]) }, openListItem() { - this.markers = []; - this.items.push(this.markers); + this.markers = [] + this.items.push(this.markers) }, openImageSection(url) { - this.sections.push([MOBILEDOC_IMAGE_SECTION_TYPE, url]); + this.sections.push([MOBILEDOC_IMAGE_SECTION_TYPE, url]) }, openCardSection(name, payload) { - this.sections.push([MOBILEDOC_CARD_SECTION_TYPE, name, payload]); + this.sections.push([MOBILEDOC_CARD_SECTION_TYPE, name, payload]) }, openPost() { - this.markerTypes = []; - this.sections = []; + this.markerTypes = [] + this.sections = [] this.result = { version: MOBILEDOC_VERSION, - sections: [this.markerTypes, this.sections] - }; + sections: [this.markerTypes, this.sections], + } }, openMarkup(tagName, attributes) { - const index = this._findOrAddMarkerTypeIndex(tagName, attributes); - this.markupMarkerIds.push(index); + const index = this._findOrAddMarkerTypeIndex(tagName, attributes) + this.markupMarkerIds.push(index) }, _findOrAddMarkerTypeIndex(tagName, attributesArray) { - if (!this._markerTypeCache) { this._markerTypeCache = {}; } - const key = `${tagName}-${attributesArray.join('-')}`; + if (!this._markerTypeCache) { + this._markerTypeCache = {} + } + const key = `${tagName}-${attributesArray.join('-')}` - let index = this._markerTypeCache[key]; + let index = this._markerTypeCache[key] if (index === undefined) { - let markerType = [tagName]; - if (attributesArray.length) { markerType.push(attributesArray); } - this.markerTypes.push(markerType); + let markerType = [tagName] + if (attributesArray.length) { + markerType.push(attributesArray) + } + this.markerTypes.push(markerType) - index = this.markerTypes.length - 1; - this._markerTypeCache[key] = index; + index = this.markerTypes.length - 1 + this._markerTypeCache[key] = index } - return index; - } -}; + return index + }, +} /** * Render from post -> mobiledoc @@ -115,10 +115,10 @@ export default { * @return {Mobiledoc} */ render(post) { - let opcodes = []; - visit(visitor, post, opcodes); - let compiler = Object.create(postOpcodeCompiler); - compile(compiler, opcodes); - return compiler.result; - } -}; + let opcodes = [] + visit(visitor, post, opcodes) + let compiler = Object.create(postOpcodeCompiler) + compile(compiler, opcodes) + return compiler.result + }, +} diff --git a/src/js/renderers/mobiledoc/0-3-1.js b/src/js/renderers/mobiledoc/0-3-1.js index 74c109ff4..170a6eb44 100644 --- a/src/js/renderers/mobiledoc/0-3-1.js +++ b/src/js/renderers/mobiledoc/0-3-1.js @@ -1,5 +1,5 @@ -import {visit, visitArray, compile} from '../../utils/compiler'; -import { objectToSortedKVArray } from '../../utils/array-utils'; +import { visit, visitArray, compile } from '../../utils/compiler' +import { objectToSortedKVArray } from '../../utils/array-utils' import { POST_TYPE, MARKUP_SECTION_TYPE, @@ -9,137 +9,131 @@ import { MARKUP_TYPE, IMAGE_SECTION_TYPE, CARD_TYPE, - ATOM_TYPE -} from '../../models/types'; + ATOM_TYPE, +} from '../../models/types' -export const MOBILEDOC_VERSION = '0.3.1'; -export const MOBILEDOC_MARKUP_SECTION_TYPE = 1; -export const MOBILEDOC_IMAGE_SECTION_TYPE = 2; -export const MOBILEDOC_LIST_SECTION_TYPE = 3; -export const MOBILEDOC_CARD_SECTION_TYPE = 10; +export const MOBILEDOC_VERSION = '0.3.1' +export const MOBILEDOC_MARKUP_SECTION_TYPE = 1 +export const MOBILEDOC_IMAGE_SECTION_TYPE = 2 +export const MOBILEDOC_LIST_SECTION_TYPE = 3 +export const MOBILEDOC_CARD_SECTION_TYPE = 10 -export const MOBILEDOC_MARKUP_MARKER_TYPE = 0; -export const MOBILEDOC_ATOM_MARKER_TYPE = 1; +export const MOBILEDOC_MARKUP_MARKER_TYPE = 0 +export const MOBILEDOC_ATOM_MARKER_TYPE = 1 const visitor = { [POST_TYPE](node, opcodes) { - opcodes.push(['openPost']); - visitArray(visitor, node.sections, opcodes); + opcodes.push(['openPost']) + visitArray(visitor, node.sections, opcodes) }, [MARKUP_SECTION_TYPE](node, opcodes) { - opcodes.push(['openMarkupSection', node.tagName]); - visitArray(visitor, node.markers, 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); + opcodes.push(['openListSection', node.tagName]) + visitArray(visitor, node.items, opcodes) }, [LIST_ITEM_TYPE](node, opcodes) { - opcodes.push(['openListItem']); - visitArray(visitor, node.markers, opcodes); + opcodes.push(['openListItem']) + visitArray(visitor, node.markers, opcodes) }, [IMAGE_SECTION_TYPE](node, opcodes) { - opcodes.push(['openImageSection', node.src]); + opcodes.push(['openImageSection', node.src]) }, [CARD_TYPE](node, opcodes) { - opcodes.push(['openCardSection', node.name, node.payload]); + opcodes.push(['openCardSection', node.name, node.payload]) }, [MARKER_TYPE](node, opcodes) { - opcodes.push(['openMarker', node.closedMarkups.length, node.value]); - visitArray(visitor, node.openedMarkups, 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)]); + 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, node.openedMarkups, opcodes); - } -}; + opcodes.push(['openAtom', node.closedMarkups.length, node.name, node.value, node.payload]) + visitArray(visitor, node.openedMarkups, opcodes) + }, +} const postOpcodeCompiler = { openMarker(closeCount, value) { - this.markupMarkerIds = []; - this.markers.push([ - MOBILEDOC_MARKUP_MARKER_TYPE, - this.markupMarkerIds, - 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, tagName, this.markers]); + 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]); + this.items = [] + this.sections.push([MOBILEDOC_LIST_SECTION_TYPE, tagName, this.items]) }, openListItem() { - this.markers = []; - this.items.push(this.markers); + this.markers = [] + this.items.push(this.markers) }, openImageSection(url) { - this.sections.push([MOBILEDOC_IMAGE_SECTION_TYPE, url]); + this.sections.push([MOBILEDOC_IMAGE_SECTION_TYPE, url]) }, openCardSection(name, payload) { - const index = this._addCardTypeIndex(name, payload); - this.sections.push([MOBILEDOC_CARD_SECTION_TYPE, index]); + const index = this._addCardTypeIndex(name, payload) + this.sections.push([MOBILEDOC_CARD_SECTION_TYPE, 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 - ]); + 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.atomTypes = [] + this.cardTypes = [] + this.markerTypes = [] + this.sections = [] this.result = { version: MOBILEDOC_VERSION, atoms: this.atomTypes, cards: this.cardTypes, markups: this.markerTypes, - sections: this.sections - }; + sections: this.sections, + } }, openMarkup(tagName, attributes) { - const index = this._findOrAddMarkerTypeIndex(tagName, attributes); - this.markupMarkerIds.push(index); + 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; + 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; + 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('-')}`; + if (!this._markerTypeCache) { + this._markerTypeCache = {} + } + const key = `${tagName}-${attributesArray.join('-')}` - let index = this._markerTypeCache[key]; + let index = this._markerTypeCache[key] if (index === undefined) { - let markerType = [tagName]; - if (attributesArray.length) { markerType.push(attributesArray); } - this.markerTypes.push(markerType); + let markerType = [tagName] + if (attributesArray.length) { + markerType.push(attributesArray) + } + this.markerTypes.push(markerType) - index = this.markerTypes.length - 1; - this._markerTypeCache[key] = index; + index = this.markerTypes.length - 1 + this._markerTypeCache[key] = index } - return index; - } -}; + return index + }, +} /** * Render from post -> mobiledoc @@ -150,10 +144,10 @@ export default { * @return {Mobiledoc} */ render(post) { - let opcodes = []; - visit(visitor, post, opcodes); - let compiler = Object.create(postOpcodeCompiler); - compile(compiler, opcodes); - return compiler.result; - } -}; + let opcodes = [] + visit(visitor, post, opcodes) + let compiler = Object.create(postOpcodeCompiler) + compile(compiler, opcodes) + return compiler.result + }, +} diff --git a/src/js/renderers/mobiledoc/0-3-2.js b/src/js/renderers/mobiledoc/0-3-2.js index 9656fb80f..b9f3edae0 100644 --- a/src/js/renderers/mobiledoc/0-3-2.js +++ b/src/js/renderers/mobiledoc/0-3-2.js @@ -1,5 +1,5 @@ -import {visit, visitArray, compile} from '../../utils/compiler'; -import { objectToSortedKVArray } from '../../utils/array-utils'; +import { visit, visitArray, compile } from '../../utils/compiler' +import { objectToSortedKVArray } from '../../utils/array-utils' import { POST_TYPE, MARKUP_SECTION_TYPE, @@ -9,145 +9,139 @@ import { MARKUP_TYPE, IMAGE_SECTION_TYPE, CARD_TYPE, - ATOM_TYPE -} from '../../models/types'; + ATOM_TYPE, +} from '../../models/types' -export const MOBILEDOC_VERSION = '0.3.2'; -export const MOBILEDOC_MARKUP_SECTION_TYPE = 1; -export const MOBILEDOC_IMAGE_SECTION_TYPE = 2; -export const MOBILEDOC_LIST_SECTION_TYPE = 3; -export const MOBILEDOC_CARD_SECTION_TYPE = 10; +export const MOBILEDOC_VERSION = '0.3.2' +export const MOBILEDOC_MARKUP_SECTION_TYPE = 1 +export const MOBILEDOC_IMAGE_SECTION_TYPE = 2 +export const MOBILEDOC_LIST_SECTION_TYPE = 3 +export const MOBILEDOC_CARD_SECTION_TYPE = 10 -export const MOBILEDOC_MARKUP_MARKER_TYPE = 0; -export const MOBILEDOC_ATOM_MARKER_TYPE = 1; +export const MOBILEDOC_MARKUP_MARKER_TYPE = 0 +export const MOBILEDOC_ATOM_MARKER_TYPE = 1 const visitor = { [POST_TYPE](node, opcodes) { - opcodes.push(['openPost']); - visitArray(visitor, node.sections, opcodes); + opcodes.push(['openPost']) + visitArray(visitor, node.sections, opcodes) }, [MARKUP_SECTION_TYPE](node, opcodes) { - opcodes.push(['openMarkupSection', node.tagName, objectToSortedKVArray(node.attributes)]); - visitArray(visitor, node.markers, opcodes); + opcodes.push(['openMarkupSection', node.tagName, objectToSortedKVArray(node.attributes)]) + visitArray(visitor, node.markers, opcodes) }, [LIST_SECTION_TYPE](node, opcodes) { - opcodes.push(['openListSection', node.tagName, objectToSortedKVArray(node.attributes)]); - visitArray(visitor, node.items, opcodes); + opcodes.push(['openListSection', node.tagName, objectToSortedKVArray(node.attributes)]) + visitArray(visitor, node.items, opcodes) }, [LIST_ITEM_TYPE](node, opcodes) { - opcodes.push(['openListItem']); - visitArray(visitor, node.markers, opcodes); + opcodes.push(['openListItem']) + visitArray(visitor, node.markers, opcodes) }, [IMAGE_SECTION_TYPE](node, opcodes) { - opcodes.push(['openImageSection', node.src]); + opcodes.push(['openImageSection', node.src]) }, [CARD_TYPE](node, opcodes) { - opcodes.push(['openCardSection', node.name, node.payload]); + opcodes.push(['openCardSection', node.name, node.payload]) }, [MARKER_TYPE](node, opcodes) { - opcodes.push(['openMarker', node.closedMarkups.length, node.value]); - visitArray(visitor, node.openedMarkups, 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)]); + 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, node.openedMarkups, opcodes); - } -}; + opcodes.push(['openAtom', node.closedMarkups.length, node.name, node.value, node.payload]) + visitArray(visitor, node.openedMarkups, opcodes) + }, +} const postOpcodeCompiler = { openMarker(closeCount, value) { - this.markupMarkerIds = []; - this.markers.push([ - MOBILEDOC_MARKUP_MARKER_TYPE, - this.markupMarkerIds, - closeCount, - value || '' - ]); + this.markupMarkerIds = [] + this.markers.push([MOBILEDOC_MARKUP_MARKER_TYPE, this.markupMarkerIds, closeCount, value || '']) }, openMarkupSection(tagName, attributes) { - this.markers = []; + this.markers = [] if (attributes && attributes.length !== 0) { - this.sections.push([MOBILEDOC_MARKUP_SECTION_TYPE, tagName, this.markers, attributes]); + this.sections.push([MOBILEDOC_MARKUP_SECTION_TYPE, tagName, this.markers, attributes]) } else { - this.sections.push([MOBILEDOC_MARKUP_SECTION_TYPE, tagName, this.markers]); + this.sections.push([MOBILEDOC_MARKUP_SECTION_TYPE, tagName, this.markers]) } }, openListSection(tagName, attributes) { - this.items = []; + this.items = [] if (attributes && attributes.length !== 0) { - this.sections.push([MOBILEDOC_LIST_SECTION_TYPE, tagName, this.items, attributes]); + this.sections.push([MOBILEDOC_LIST_SECTION_TYPE, tagName, this.items, attributes]) } else { - this.sections.push([MOBILEDOC_LIST_SECTION_TYPE, tagName, this.items]); + this.sections.push([MOBILEDOC_LIST_SECTION_TYPE, tagName, this.items]) } }, openListItem() { - this.markers = []; - this.items.push(this.markers); + this.markers = [] + this.items.push(this.markers) }, openImageSection(url) { - this.sections.push([MOBILEDOC_IMAGE_SECTION_TYPE, url]); + this.sections.push([MOBILEDOC_IMAGE_SECTION_TYPE, url]) }, openCardSection(name, payload) { - const index = this._addCardTypeIndex(name, payload); - this.sections.push([MOBILEDOC_CARD_SECTION_TYPE, index]); + const index = this._addCardTypeIndex(name, payload) + this.sections.push([MOBILEDOC_CARD_SECTION_TYPE, 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 - ]); + 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.atomTypes = [] + this.cardTypes = [] + this.markerTypes = [] + this.sections = [] this.result = { version: MOBILEDOC_VERSION, atoms: this.atomTypes, cards: this.cardTypes, markups: this.markerTypes, - sections: this.sections - }; + sections: this.sections, + } }, openMarkup(tagName, attributes) { - const index = this._findOrAddMarkerTypeIndex(tagName, attributes); - this.markupMarkerIds.push(index); + 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; + 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; + 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('-')}`; + if (!this._markerTypeCache) { + this._markerTypeCache = {} + } + const key = `${tagName}-${attributesArray.join('-')}` - let index = this._markerTypeCache[key]; + let index = this._markerTypeCache[key] if (index === undefined) { - let markerType = [tagName]; - if (attributesArray.length) { markerType.push(attributesArray); } - this.markerTypes.push(markerType); + let markerType = [tagName] + if (attributesArray.length) { + markerType.push(attributesArray) + } + this.markerTypes.push(markerType) - index = this.markerTypes.length - 1; - this._markerTypeCache[key] = index; + index = this.markerTypes.length - 1 + this._markerTypeCache[key] = index } - return index; - } -}; + return index + }, +} /** * Render from post -> mobiledoc @@ -158,10 +152,10 @@ export default { * @return {Mobiledoc} */ render(post) { - let opcodes = []; - visit(visitor, post, opcodes); - let compiler = Object.create(postOpcodeCompiler); - compile(compiler, opcodes); - return compiler.result; - } -}; + let opcodes = [] + visit(visitor, post, opcodes) + let compiler = Object.create(postOpcodeCompiler) + compile(compiler, opcodes) + return compiler.result + }, +} diff --git a/src/js/renderers/mobiledoc/0-3.js b/src/js/renderers/mobiledoc/0-3.js index b11b2a855..cdd4a793b 100644 --- a/src/js/renderers/mobiledoc/0-3.js +++ b/src/js/renderers/mobiledoc/0-3.js @@ -1,5 +1,5 @@ -import {visit, visitArray, compile} from '../../utils/compiler'; -import { objectToSortedKVArray } from '../../utils/array-utils'; +import { visit, visitArray, compile } from '../../utils/compiler' +import { objectToSortedKVArray } from '../../utils/array-utils' import { POST_TYPE, MARKUP_SECTION_TYPE, @@ -9,137 +9,131 @@ import { MARKUP_TYPE, IMAGE_SECTION_TYPE, CARD_TYPE, - ATOM_TYPE -} from '../../models/types'; + ATOM_TYPE, +} from '../../models/types' -export const MOBILEDOC_VERSION = '0.3.0'; -export const MOBILEDOC_MARKUP_SECTION_TYPE = 1; -export const MOBILEDOC_IMAGE_SECTION_TYPE = 2; -export const MOBILEDOC_LIST_SECTION_TYPE = 3; -export const MOBILEDOC_CARD_SECTION_TYPE = 10; +export const MOBILEDOC_VERSION = '0.3.0' +export const MOBILEDOC_MARKUP_SECTION_TYPE = 1 +export const MOBILEDOC_IMAGE_SECTION_TYPE = 2 +export const MOBILEDOC_LIST_SECTION_TYPE = 3 +export const MOBILEDOC_CARD_SECTION_TYPE = 10 -export const MOBILEDOC_MARKUP_MARKER_TYPE = 0; -export const MOBILEDOC_ATOM_MARKER_TYPE = 1; +export const MOBILEDOC_MARKUP_MARKER_TYPE = 0 +export const MOBILEDOC_ATOM_MARKER_TYPE = 1 const visitor = { [POST_TYPE](node, opcodes) { - opcodes.push(['openPost']); - visitArray(visitor, node.sections, opcodes); + opcodes.push(['openPost']) + visitArray(visitor, node.sections, opcodes) }, [MARKUP_SECTION_TYPE](node, opcodes) { - opcodes.push(['openMarkupSection', node.tagName]); - visitArray(visitor, node.markers, 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); + opcodes.push(['openListSection', node.tagName]) + visitArray(visitor, node.items, opcodes) }, [LIST_ITEM_TYPE](node, opcodes) { - opcodes.push(['openListItem']); - visitArray(visitor, node.markers, opcodes); + opcodes.push(['openListItem']) + visitArray(visitor, node.markers, opcodes) }, [IMAGE_SECTION_TYPE](node, opcodes) { - opcodes.push(['openImageSection', node.src]); + opcodes.push(['openImageSection', node.src]) }, [CARD_TYPE](node, opcodes) { - opcodes.push(['openCardSection', node.name, node.payload]); + opcodes.push(['openCardSection', node.name, node.payload]) }, [MARKER_TYPE](node, opcodes) { - opcodes.push(['openMarker', node.closedMarkups.length, node.value]); - visitArray(visitor, node.openedMarkups, 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)]); + 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, node.openedMarkups, opcodes); - } -}; + opcodes.push(['openAtom', node.closedMarkups.length, node.name, node.value, node.payload]) + visitArray(visitor, node.openedMarkups, opcodes) + }, +} const postOpcodeCompiler = { openMarker(closeCount, value) { - this.markupMarkerIds = []; - this.markers.push([ - MOBILEDOC_MARKUP_MARKER_TYPE, - this.markupMarkerIds, - 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, tagName, this.markers]); + 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]); + this.items = [] + this.sections.push([MOBILEDOC_LIST_SECTION_TYPE, tagName, this.items]) }, openListItem() { - this.markers = []; - this.items.push(this.markers); + this.markers = [] + this.items.push(this.markers) }, openImageSection(url) { - this.sections.push([MOBILEDOC_IMAGE_SECTION_TYPE, url]); + this.sections.push([MOBILEDOC_IMAGE_SECTION_TYPE, url]) }, openCardSection(name, payload) { - const index = this._addCardTypeIndex(name, payload); - this.sections.push([MOBILEDOC_CARD_SECTION_TYPE, index]); + const index = this._addCardTypeIndex(name, payload) + this.sections.push([MOBILEDOC_CARD_SECTION_TYPE, 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 - ]); + 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.atomTypes = [] + this.cardTypes = [] + this.markerTypes = [] + this.sections = [] this.result = { version: MOBILEDOC_VERSION, atoms: this.atomTypes, cards: this.cardTypes, markups: this.markerTypes, - sections: this.sections - }; + sections: this.sections, + } }, openMarkup(tagName, attributes) { - const index = this._findOrAddMarkerTypeIndex(tagName, attributes); - this.markupMarkerIds.push(index); + 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; + 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; + 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('-')}`; + if (!this._markerTypeCache) { + this._markerTypeCache = {} + } + const key = `${tagName}-${attributesArray.join('-')}` - let index = this._markerTypeCache[key]; + let index = this._markerTypeCache[key] if (index === undefined) { - let markerType = [tagName]; - if (attributesArray.length) { markerType.push(attributesArray); } - this.markerTypes.push(markerType); + let markerType = [tagName] + if (attributesArray.length) { + markerType.push(attributesArray) + } + this.markerTypes.push(markerType) - index = this.markerTypes.length - 1; - this._markerTypeCache[key] = index; + index = this.markerTypes.length - 1 + this._markerTypeCache[key] = index } - return index; - } -}; + return index + }, +} /** * Render from post -> mobiledoc @@ -150,10 +144,10 @@ export default { * @return {Mobiledoc} */ render(post) { - let opcodes = []; - visit(visitor, post, opcodes); - let compiler = Object.create(postOpcodeCompiler); - compile(compiler, opcodes); - return compiler.result; - } -}; + let opcodes = [] + visit(visitor, post, opcodes) + let compiler = Object.create(postOpcodeCompiler) + compile(compiler, opcodes) + return compiler.result + }, +} diff --git a/src/js/renderers/mobiledoc/index.js b/src/js/renderers/mobiledoc/index.js index da1622900..a0b86ae8a 100644 --- a/src/js/renderers/mobiledoc/index.js +++ b/src/js/renderers/mobiledoc/index.js @@ -1,26 +1,26 @@ -import MobiledocRenderer_0_2, { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_2 } from './0-2'; -import MobiledocRenderer_0_3, { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3 } from './0-3'; -import MobiledocRenderer_0_3_1, { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3_1 } from './0-3-1'; -import MobiledocRenderer_0_3_2, { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3_2 } from './0-3-2'; -import assert from 'mobiledoc-kit/utils/assert'; +import MobiledocRenderer_0_2, { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_2 } from './0-2' +import MobiledocRenderer_0_3, { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3 } from './0-3' +import MobiledocRenderer_0_3_1, { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3_1 } from './0-3-1' +import MobiledocRenderer_0_3_2, { MOBILEDOC_VERSION as MOBILEDOC_VERSION_0_3_2 } from './0-3-2' +import assert from 'mobiledoc-kit/utils/assert' -export const MOBILEDOC_VERSION = MOBILEDOC_VERSION_0_3_2; +export const MOBILEDOC_VERSION = MOBILEDOC_VERSION_0_3_2 export default { render(post, version) { switch (version) { case MOBILEDOC_VERSION_0_2: - return MobiledocRenderer_0_2.render(post); + return MobiledocRenderer_0_2.render(post) case MOBILEDOC_VERSION_0_3: - return MobiledocRenderer_0_3.render(post); + return MobiledocRenderer_0_3.render(post) case MOBILEDOC_VERSION_0_3_1: - return MobiledocRenderer_0_3_1.render(post); + return MobiledocRenderer_0_3_1.render(post) case undefined: case null: case MOBILEDOC_VERSION_0_3_2: - return MobiledocRenderer_0_3_2.render(post); + return MobiledocRenderer_0_3_2.render(post) default: - assert(`Unknown version of mobiledoc renderer requested: ${version}`, false); + assert(`Unknown version of mobiledoc renderer requested: ${version}`, false) } - } -}; + }, +} diff --git a/src/js/utils/array-utils.js b/src/js/utils/array-utils.js index 9ab77d945..54380d4a5 100644 --- a/src/js/utils/array-utils.js +++ b/src/js/utils/array-utils.js @@ -1,40 +1,44 @@ function detect(enumerable, callback) { if (enumerable.detect) { - return enumerable.detect(callback); + return enumerable.detect(callback) } else { - for (let i=0; i { - if (conditionFn(i)) { filtered.push(i); } - }); - return filtered; + if (conditionFn(i)) { + filtered.push(i) + } + }) + return filtered } /** @@ -65,14 +71,14 @@ function filter(enumerable, conditionFn) { * @private */ function commonItemLength(listA, listB) { - let offset = 0; + let offset = 0 while (offset < listA.length && offset < listB.length) { if (listA[offset] !== listB[offset]) { - break; + break } - offset++; + offset++ } - return offset; + return offset } /** @@ -80,27 +86,27 @@ function commonItemLength(listA, listB) { * @private */ function commonItems(listA, listB) { - let offset = 0; + let offset = 0 while (offset < listA.length && offset < listB.length) { if (listA[offset] !== listB[offset]) { - break; + break } - offset++; + offset++ } - return listA.slice(0, offset); + return listA.slice(0, offset) } // return new array without falsy items like ruby's `compact` function compact(enumerable) { - return filter(enumerable, i => !!i); + return filter(enumerable, i => !!i) } function reduce(enumerable, callback, initialValue) { - let previousValue = initialValue; + let previousValue = initialValue forEach(enumerable, (val, index) => { - previousValue = callback(previousValue, val, index); - }); - return previousValue; + previousValue = callback(previousValue, val, index) + }) + return previousValue } /** @@ -109,51 +115,56 @@ function reduce(enumerable, callback, initialValue) { * @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; + const obj = {} + for (let i = 0; i < array.length; i += 2) { + let [key, value] = [array[i], array[i + 1]] + obj[key] = value } - return obj; + return obj } function objectToSortedKVArray(obj) { - const keys = Object.keys(obj).sort(); - const result = []; + const keys = Object.keys(obj).sort() + const result = [] keys.forEach(k => { - result.push(k); - result.push(obj[k]); - }); - return result; + 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; } + 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; } + for (let i = 0; i < l1; i++) { + if (arr1[i] !== arr2[i]) { + return false + } } - return true; + return true } // return an object with only the valid keys -function filterObject(object, validKeys=[]) { - let result = {}; +function filterObject(object, validKeys = []) { + let result = {} forEach( filter(Object.keys(object), key => validKeys.indexOf(key) !== -1), - key => result[key] = object[key] - ); - return result; + key => (result[key] = object[key]) + ) + return result } function contains(array, item) { - return array.indexOf(item) !== -1; + return array.indexOf(item) !== -1 } function values(object) { - return Object.keys(object).map(key => object[key]); + return Object.keys(object).map(key => object[key]) } export { @@ -172,5 +183,5 @@ export { toArray, filterObject, contains, - values -}; + values, +} diff --git a/src/js/utils/assert.js b/src/js/utils/assert.js index 3e3df8f0d..b35ec961c 100644 --- a/src/js/utils/assert.js +++ b/src/js/utils/assert.js @@ -1,7 +1,7 @@ -import MobiledocError from './mobiledoc-error'; +import MobiledocError from './mobiledoc-error' -export default function(message, conditional) { +export default function (message, conditional) { if (!conditional) { - throw new MobiledocError(message); + throw new MobiledocError(message) } } diff --git a/src/js/utils/browser.js b/src/js/utils/browser.js index 5c296f0b0..559b05b8c 100644 --- a/src/js/utils/browser.js +++ b/src/js/utils/browser.js @@ -1,8 +1,8 @@ export default { isMac() { - return (typeof window !== 'undefined') && window.navigator && /Mac/.test(window.navigator.platform); + return typeof window !== 'undefined' && window.navigator && /Mac/.test(window.navigator.platform) }, isWin() { - return (typeof window !== 'undefined') && window.navigator && /Win/.test(window.navigator.platform); - } -}; + return typeof window !== 'undefined' && window.navigator && /Win/.test(window.navigator.platform) + }, +} diff --git a/src/js/utils/characters.js b/src/js/utils/characters.js index d906126d8..f3f373d01 100644 --- a/src/js/utils/characters.js +++ b/src/js/utils/characters.js @@ -1,3 +1,3 @@ -export const TAB = '\t'; -export const ENTER = '\n'; -export const SPACE = ' '; +export const TAB = '\t' +export const ENTER = '\n' +export const SPACE = ' ' diff --git a/src/js/utils/compiler.js b/src/js/utils/compiler.js index 2b549d903..d2e23967e 100644 --- a/src/js/utils/compiler.js +++ b/src/js/utils/compiler.js @@ -1,33 +1,33 @@ -import { forEach } from './array-utils'; -import assert from './assert'; +import { forEach } from './array-utils' +import assert from './assert' export function visit(visitor, node, opcodes) { - const method = node.type; - assert(`Cannot visit unknown type ${method}`, !!visitor[method]); - visitor[method](node, opcodes); + const method = node.type + assert(`Cannot visit unknown type ${method}`, !!visitor[method]) + visitor[method](node, opcodes) } export function compile(compiler, opcodes) { - for (var i=0, l=opcodes.length; i { - visit(visitor, node, opcodes); - }); + visit(visitor, node, opcodes) + }) } diff --git a/src/js/utils/copy.js b/src/js/utils/copy.js index a5005b4ad..3a28e9023 100644 --- a/src/js/utils/copy.js +++ b/src/js/utils/copy.js @@ -1,11 +1,9 @@ function shallowCopyObject(object) { - let copy = {}; + let copy = {} Object.keys(object).forEach(key => { - copy[key] = object[key]; - }); - return copy; + copy[key] = object[key] + }) + return copy } -export { - shallowCopyObject -}; +export { shallowCopyObject } diff --git a/src/js/utils/cursor.js b/src/js/utils/cursor.js index 15d3d145a..858f8c4bd 100644 --- a/src/js/utils/cursor.js +++ b/src/js/utils/cursor.js @@ -1,24 +1,21 @@ -import { - clearSelection, - comparePosition -} from '../utils/selection-utils'; -import { containsNode } from '../utils/dom-utils'; -import Position from './cursor/position'; -import Range from './cursor/range'; -import { DIRECTION } from '../utils/key'; -import { constrainSelectionTo } from '../utils/selection-utils'; - -export { Position, Range }; +import { clearSelection, comparePosition } from '../utils/selection-utils' +import { containsNode } from '../utils/dom-utils' +import Position from './cursor/position' +import Range from './cursor/range' +import { DIRECTION } from '../utils/key' +import { constrainSelectionTo } from '../utils/selection-utils' + +export { Position, Range } const Cursor = class Cursor { constructor(editor) { - this.editor = editor; - this.renderTree = editor._renderTree; - this.post = editor.post; + this.editor = editor + this.renderTree = editor._renderTree + this.post = editor.post } clearSelection() { - clearSelection(); + clearSelection() } /** @@ -26,111 +23,111 @@ const Cursor = class Cursor { * editor's element or a selection that is contained in the editor's element */ hasCursor() { - return this.editor.hasRendered && - (this._hasCollapsedSelection() || this._hasSelection()); + return this.editor.hasRendered && (this._hasCollapsedSelection() || this._hasSelection()) } hasSelection() { - return this.editor.hasRendered && - this._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); + let { renderTree } = this + let renderNode = renderTree.findRenderNodeFromElement(element) if (renderNode && renderNode.postNode.isCardSection) { - let renderedElement = renderNode.element; + 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; + if ( + element !== renderedElement && + element !== renderedElement.firstChild && + element !== renderedElement.lastChild + ) { + return false } } - return !!renderNode; + return !!renderNode } /* * @return {Range} Cursor#Range object */ get offsets() { - if (!this.hasCursor()) { return Range.blankRange(); } + if (!this.hasCursor()) { + return Range.blankRange() + } - let { selection, renderTree } = this; - let parentNode = this.editor.element; - selection = constrainSelectionTo(selection, parentNode); + let { selection, renderTree } = this + let parentNode = this.editor.element + selection = constrainSelectionTo(selection, parentNode) - const { - headNode, headOffset, tailNode, tailOffset, direction - } = comparePosition(selection); + const { headNode, headOffset, tailNode, tailOffset, direction } = comparePosition(selection) - const headPosition = Position.fromNode(renderTree, headNode, headOffset); - const tailPosition = Position.fromNode(renderTree, tailNode, tailOffset); + const headPosition = Position.fromNode(renderTree, headNode, headOffset) + const tailPosition = Position.fromNode(renderTree, tailNode, tailOffset) - return new Range(headPosition, tailPosition, direction); + return new Range(headPosition, tailPosition, direction) } _findNodeForPosition(position) { - let { section } = position; - let node, offset; + let { section } = position + let node, offset if (section.isCardSection) { - offset = 0; + offset = 0 if (position.offset === 0) { - node = section.renderNode.element.firstChild; + node = section.renderNode.element.firstChild } else { - node = section.renderNode.element.lastChild; + node = section.renderNode.element.lastChild } } else if (section.isBlank) { - node = section.renderNode.cursorElement; - offset = 0; + node = section.renderNode.cursorElement + offset = 0 } else { - let {marker, offsetInMarker} = position; + 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; + offset = 0 + node = marker.renderNode.tailTextNode } else { - offset = 0; - node = marker.renderNode.headTextNode; + offset = 0 + node = marker.renderNode.headTextNode } } else { - node = marker.renderNode.element; - offset = offsetInMarker; + node = marker.renderNode.element + offset = offsetInMarker } } - return {node, offset}; + return { node, offset } } selectRange(range) { if (range.isBlank) { - this.clearSelection(); - return; + 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); + 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(); + this.editor._ensureFocus() } get selection() { - return window.getSelection(); + return window.getSelection() } selectedText() { // FIXME remove this - return this.selection.toString(); + return this.selection.toString() } /** @@ -141,46 +138,51 @@ const Cursor = class Cursor { * @param {integer} direction forward or backward, default forward * @private */ - _moveToNode(node, offset, endNode, endOffset, direction=DIRECTION.FORWARD) { - this.clearSelection(); + _moveToNode(node, offset, endNode, endOffset, direction = DIRECTION.FORWARD) { + this.clearSelection() if (direction === DIRECTION.BACKWARD) { - [node, offset, endNode, endOffset] = [ endNode, endOffset, node, offset ]; + ;[node, offset, endNode, endOffset] = [endNode, endOffset, node, offset] } - const range = document.createRange(); - range.setStart(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); + this.selection.addRange(range) + this.selection.extend(endNode, endOffset) } else { - range.setEnd(endNode, endOffset); - this.selection.addRange(range); + range.setEnd(endNode, endOffset) + this.selection.addRange(range) } } _hasSelection() { - const element = this.editor.element; - const { _selectionRange } = this; - if (!_selectionRange || _selectionRange.collapsed) { return false; } + 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); + return containsNode(element, this.selection.anchorNode) && containsNode(element, this.selection.focusNode) } _hasCollapsedSelection() { - const { _selectionRange } = this; - if (!_selectionRange) { return false; } + const { _selectionRange } = this + if (!_selectionRange) { + return false + } - const element = this.editor.element; - return containsNode(element, this.selection.anchorNode); + 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); + const { selection } = this + if (selection.rangeCount === 0) { + return null + } + return selection.getRangeAt(0) } -}; +} -export default Cursor; +export default Cursor diff --git a/src/js/utils/cursor/position.js b/src/js/utils/cursor/position.js index 14847c0b1..bff47ac76 100644 --- a/src/js/utils/cursor/position.js +++ b/src/js/utils/cursor/position.js @@ -1,68 +1,61 @@ -import { isTextNode } from 'mobiledoc-kit/utils/dom-utils'; -import assert from 'mobiledoc-kit/utils/assert'; -import { - HIGH_SURROGATE_RANGE, - LOW_SURROGATE_RANGE -} from 'mobiledoc-kit/models/marker'; -import { containsNode } from 'mobiledoc-kit/utils/dom-utils'; -import { findOffsetInNode } from 'mobiledoc-kit/utils/selection-utils'; -import { DIRECTION } from 'mobiledoc-kit/utils/key'; -import Range from './range'; - -const { FORWARD, BACKWARD } = DIRECTION; +import { isTextNode } from 'mobiledoc-kit/utils/dom-utils' +import assert from 'mobiledoc-kit/utils/assert' +import { HIGH_SURROGATE_RANGE, LOW_SURROGATE_RANGE } from 'mobiledoc-kit/models/marker' +import { containsNode } from 'mobiledoc-kit/utils/dom-utils' +import { findOffsetInNode } from 'mobiledoc-kit/utils/selection-utils' +import { DIRECTION } from 'mobiledoc-kit/utils/key' +import Range from './range' + +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]|_|:/; +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 - ); + let renderNode = renderTree.findRenderNodeFromElement(node, renderNode => renderNode.postNode.isSection) - return renderNode && renderNode.postNode; + return renderNode && renderNode.postNode } function findOffsetInMarkerable(markerable, node, offset) { - let offsetInSection = 0; - let marker = markerable.markers.head; + let offsetInSection = 0 + let marker = markerable.markers.head while (marker) { - let markerNode = marker.renderNode.element; + let markerNode = marker.renderNode.element if (markerNode === node) { - return offsetInSection + offset; + return offsetInSection + offset } else if (marker.isAtom) { if (marker.renderNode.headTextNode === node) { - return offsetInSection; + return offsetInSection } else if (marker.renderNode.tailTextNode === node) { - return offsetInSection + 1; + return offsetInSection + 1 } } - offsetInSection += marker.length; - marker = marker.next; + offsetInSection += marker.length + marker = marker.next } - return offsetInSection; + return offsetInSection } function findOffsetInSection(section, node, offset) { if (section.isMarkerable) { - return findOffsetInMarkerable(section, node, offset); + return findOffsetInMarkerable(section, node, offset) } else { - assert('findOffsetInSection must be called with markerable or card section', - section.isCardSection); + assert('findOffsetInSection must be called with markerable or card section', section.isCardSection) - let wrapperNode = section.renderNode.element; - let endTextNode = wrapperNode.lastChild; + let wrapperNode = section.renderNode.element + let endTextNode = wrapperNode.lastChild if (node === endTextNode) { - return 1; + return 1 } - return 0; + return 0 } } -let Position, BlankPosition; +let Position, BlankPosition Position = class Position { /** @@ -71,17 +64,15 @@ Position = class Position { * Two positions (a head and a tail) make up a {@link Range}. * @constructor */ - constructor(section, offset=0, isBlank=false) { + 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')); + 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; + this.section = section + this.offset = offset + this.isBlank = isBlank } /** @@ -91,18 +82,18 @@ Position = class Position { * @return {Position|null} */ static atPoint(x, y, editor) { - let { _renderTree, element: rootElement } = editor; - let elementFromPoint = document.elementFromPoint(x, y); + let { _renderTree, element: rootElement } = editor + let elementFromPoint = document.elementFromPoint(x, y) if (!containsNode(rootElement, elementFromPoint)) { - return; + return } - let { node, offset } = findOffsetInNode(elementFromPoint, {left: x, top: y}); - return Position.fromNode(_renderTree, node, offset); + let { node, offset } = findOffsetInNode(elementFromPoint, { left: x, top: y }) + return Position.fromNode(_renderTree, node, offset) } static blankPosition() { - return new BlankPosition(); + return new BlankPosition() } /** @@ -112,23 +103,23 @@ Position = class Position { * @return {Range} * @public */ - toRange(tail=this, direction=null) { - return new Range(this, tail, direction); + toRange(tail = this, direction = null) { + return new Range(this, tail, direction) } get leafSectionIndex() { - let post = this.section.post; - let leafSectionIndex; + let post = this.section.post + let leafSectionIndex post.walkAllLeafSections((section, index) => { if (section === this.section) { - leafSectionIndex = index; + leafSectionIndex = index } - }); - return leafSectionIndex; + }) + return leafSectionIndex } get isMarkerable() { - return this.section && this.section.isMarkerable; + return this.section && this.section.isMarkerable } /** @@ -137,7 +128,7 @@ Position = class Position { * @return {Marker|undefined} */ get marker() { - return this.isMarkerable && this.markerPosition.marker; + return this.isMarkerable && this.markerPosition.marker } /** @@ -150,55 +141,58 @@ Position = class Position { * @return {Marker|undefined} */ markerIn(direction) { - if (!this.isMarkerable) { return; } + if (!this.isMarkerable) { + return + } - let { marker, offsetInMarker } = this; - if (!marker) { return; } + let { marker, offsetInMarker } = this + if (!marker) { + return + } if (offsetInMarker > 0 && offsetInMarker < marker.length) { - return marker; + return marker } else if (offsetInMarker === 0) { - return direction === BACKWARD ? marker : marker.prev; + return direction === BACKWARD ? marker : marker.prev } else if (offsetInMarker === marker.length) { - return direction === FORWARD ? marker.next : marker; + return direction === FORWARD ? marker.next : marker } } get offsetInMarker() { - return this.markerPosition.offset; + return this.markerPosition.offset } isEqual(position) { - return this.section === position.section && - this.offset === position.offset; + 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 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 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 this.isEqual(this.section.headPosition()) } /** * @return {Boolean} If this position is at the tail of its section */ isTail() { - return this.isEqual(this.section.tailPosition()); + return this.isEqual(this.section.tailPosition()) } /** @@ -211,14 +205,14 @@ Position = class Position { * at the end of the post. */ move(units) { - assert('Must pass integer to Position#move', typeof units === 'number'); + assert('Must pass integer to Position#move', typeof units === 'number') if (units < 0) { - return this.moveLeft().move(++units); + return this.moveLeft().move(++units) } else if (units > 0) { - return this.moveRight().move(--units); + return this.moveRight().move(--units) } else { - return this; + return this } } @@ -227,72 +221,68 @@ Position = class Position { * @return {Position} The result of moving 1 "word" unit in `direction` */ moveWord(direction) { - let isPostBoundary = direction === BACKWARD ? this.isHeadOfPost() : this.isTailOfPost(); + let isPostBoundary = direction === BACKWARD ? this.isHeadOfPost() : this.isTailOfPost() if (isPostBoundary) { - return this; + return this } if (!this.isMarkerable) { - return this.move(direction); + return this.move(direction) } - let pos = this; + 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(); - }; + 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); - }; + 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); - }; + 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 true } - return dir === BACKWARD ? pos.move(BACKWARD).marker.isAtom : pos.marker.isAtom; - }; + 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); + return pos.move(direction).moveWord(direction) } - let seekWord = (pos) => { - return !isBoundary(pos, direction) && - !isAtom(pos, direction) && - !WORD_CHAR_REGEX.test(peekChar(pos, 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); + pos = pos.move(direction) } if (isAtom(pos, direction)) { - return pos.move(direction); + return pos.move(direction) } - let seekBoundary = (pos) => { - return !isBoundary(pos, direction) && - !isAtom(pos, direction) && - WORD_CHAR_REGEX.test(peekChar(pos, 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); + pos = pos.move(direction) } - return pos; + return pos } /** @@ -303,17 +293,17 @@ Position = class Position { */ moveLeft() { if (this.isHead()) { - let prev = this.section.previousLeafSection(); - return prev ? prev.tailPosition() : this; + let prev = this.section.previousLeafSection() + return prev ? prev.tailPosition() : this } else { - let offset = this.offset - 1; + let offset = this.offset - 1 if (this.isMarkerable && this.marker) { - let code = this.marker.value.charCodeAt(offset); + let code = this.marker.value.charCodeAt(offset) if (code >= LOW_SURROGATE_RANGE[0] && code <= LOW_SURROGATE_RANGE[1]) { - offset = offset - 1; + offset = offset - 1 } } - return new Position(this.section, offset); + return new Position(this.section, offset) } } @@ -325,66 +315,64 @@ Position = class Position { */ moveRight() { if (this.isTail()) { - let next = this.section.nextLeafSection(); - return next ? next.headPosition() : this; + let next = this.section.nextLeafSection() + return next ? next.headPosition() : this } else { - let offset = this.offset + 1; + let offset = this.offset + 1 if (this.isMarkerable && this.marker) { - let code = this.marker.value.charCodeAt(offset - 1); + let code = this.marker.value.charCodeAt(offset - 1) if (code >= HIGH_SURROGATE_RANGE[0] && code <= HIGH_SURROGATE_RANGE[1]) { - offset = offset + 1; + offset = offset + 1 } } - return new Position(this.section, offset); + return new Position(this.section, offset) } } static fromNode(renderTree, node, offset) { if (isTextNode(node)) { - return Position.fromTextNode(renderTree, node, offset); + return Position.fromTextNode(renderTree, node, offset) } else { - return Position.fromElementNode(renderTree, node, offset); + return Position.fromElementNode(renderTree, node, offset) } } static fromTextNode(renderTree, textNode, offsetInNode) { - const renderNode = renderTree.getElementRenderNode(textNode); - let section, offsetInSection; + const renderNode = renderTree.getElementRenderNode(textNode) + let section, offsetInSection if (renderNode) { - const marker = renderNode.postNode; - section = marker.section; + 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); + 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); + section = findParentSectionFromNode(renderTree, textNode) + assert(`Could not find parent section for un-mapped text node "${textNode.textContent}"`, !!section) - offsetInSection = findOffsetInSection(section, textNode, offsetInNode); + offsetInSection = findOffsetInSection(section, textNode, offsetInNode) } - return new Position(section, offsetInSection); + return new Position(section, offsetInSection) } static fromElementNode(renderTree, elementNode, offset) { - let position; + 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(); + 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); + 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 @@ -394,72 +382,92 @@ Position = class Position { // 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(); + 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; + let renderNode = renderTree.getElementRenderNode(elementNode) + let postNode = renderNode && renderNode.postNode if (postNode && postNode.isAtom) { - let sectionOffset = section.offsetOfMarker(postNode); + let sectionOffset = section.offsetOfMarker(postNode) if (offset > 1) { // we are on the tail side of the atom - sectionOffset += postNode.length; + sectionOffset += postNode.length } - position = new Position(section, sectionOffset); + 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(); + 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(); + position = section.headPosition() } } } - return position; + 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); + 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); + super(null, 0, true) } isEqual(other) { - return other && other.isBlank; + return other && other.isBlank } - toRange() { return Range.blankRange(); } - get leafSectionIndex() { assert('must implement get leafSectionIndex', false); } + 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 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 {}; } -}; + get markerPosition() { + return {} + } +} -export default Position; +export default Position diff --git a/src/js/utils/cursor/range.js b/src/js/utils/cursor/range.js index 00a2726b6..4de098e5d 100644 --- a/src/js/utils/cursor/range.js +++ b/src/js/utils/cursor/range.js @@ -1,6 +1,6 @@ -import Position from './position'; -import { DIRECTION } from '../key'; -import assert from 'mobiledoc-kit/utils/assert'; +import Position from './position' +import { DIRECTION } from '../key' +import assert from 'mobiledoc-kit/utils/assert' /** * A logical range of a {@link Post}. @@ -15,15 +15,15 @@ class Range { * @return {Range} * @private */ - constructor(head, tail=head, direction=null) { + constructor(head, tail = head, direction = null) { /** @property {Position} head */ - this.head = head; + this.head = head /** @property {Position} tail */ - this.tail = tail; + this.tail = tail /** @property {Direction} direction */ - this.direction = direction; + this.direction = direction } /** @@ -36,16 +36,12 @@ class Range { * @param {Direction} [direction=null] * @return {Range} */ - static create(headSection, headOffset, tailSection=headSection, tailOffset=headOffset, direction=null) { - return new Range( - new Position(headSection, headOffset), - new Position(tailSection, tailOffset), - direction - ); + static create(headSection, headOffset, tailSection = headSection, tailOffset = headOffset, direction = null) { + return new Range(new Position(headSection, headOffset), new Position(tailSection, tailOffset), direction) } static blankRange() { - return new Range(Position.blankPosition(), Position.blankPosition()); + return new Range(Position.blankPosition(), Position.blankPosition()) } /** @@ -59,14 +55,12 @@ class Range { * @private */ trimTo(section) { - const length = section.length; + 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; + 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); + return Range.create(section, headOffset, section, tailOffset) } /** @@ -79,19 +73,21 @@ class Range { * @public */ extend(units) { - assert(`Must pass integer to Range#extend`, typeof units === 'number'); + assert(`Must pass integer to Range#extend`, typeof units === 'number') - if (units === 0) { return this; } + if (units === 0) { + return this + } - let { head, tail, direction: currentDirection } = this; + let { head, tail, direction: currentDirection } = this switch (currentDirection) { case DIRECTION.FORWARD: - return new Range(head, tail.move(units), currentDirection); + return new Range(head, tail.move(units), currentDirection) case DIRECTION.BACKWARD: - return new Range(head.move(units), tail, currentDirection); + 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); + let newDirection = units > 0 ? DIRECTION.FORWARD : DIRECTION.BACKWARD + return new Range(head, tail, newDirection).extend(units) } } } @@ -106,15 +102,17 @@ class 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); + 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; + let { focusedPosition, isCollapsed } = this if (isCollapsed) { - return new Range(focusedPosition.move(direction)); + return new Range(focusedPosition.move(direction)) } else { - return this._collapse(direction); + return this._collapse(direction) } } @@ -127,85 +125,81 @@ class Range { * @public */ expandByMarker(detectMarker) { - let { - head, - tail, - direction - } = this; - let {section: headSection} = head; + 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'); + throw new Error( + '#expandByMarker does not work across sections. Perhaps you should confirm the range is collapsed' + ) } let firstNotMatchingDetect = i => { - return !detectMarker(i); - }; + return !detectMarker(i) + } - let headMarker = headSection.markers.detect(firstNotMatchingDetect, head.marker, true); + let headMarker = headSection.markers.detect(firstNotMatchingDetect, head.marker, true) if (!headMarker && detectMarker(headSection.markers.head)) { - headMarker = headSection.markers.head; + headMarker = headSection.markers.head } else { - headMarker = headMarker.next || head.marker; + headMarker = headMarker.next || head.marker } - let headPosition = new Position(headSection, headSection.offsetOfMarker(headMarker)); + let headPosition = new Position(headSection, headSection.offsetOfMarker(headMarker)) - let tailMarker = tail.section.markers.detect(firstNotMatchingDetect, tail.marker); + let tailMarker = tail.section.markers.detect(firstNotMatchingDetect, tail.marker) if (!tailMarker && detectMarker(headSection.markers.tail)) { - tailMarker = headSection.markers.tail; + tailMarker = headSection.markers.tail } else { - tailMarker = tailMarker.prev || tail.marker; + tailMarker = tailMarker.prev || tail.marker } - let tailPosition = new Position(tail.section, tail.section.offsetOfMarker(tailMarker) + tailMarker.length); + let tailPosition = new Position(tail.section, tail.section.offsetOfMarker(tailMarker) + tailMarker.length) - return headPosition.toRange(tailPosition, direction); + return headPosition.toRange(tailPosition, direction) } _collapse(direction) { - return new Range(direction === DIRECTION.BACKWARD ? this.head : this.tail); + return new Range(direction === DIRECTION.BACKWARD ? this.head : this.tail) } get focusedPosition() { - return this.direction === DIRECTION.BACKWARD ? this.head : this.tail; + return this.direction === DIRECTION.BACKWARD ? this.head : this.tail } isEqual(other) { - return other && - this.head.isEqual(other.head) && - this.tail.isEqual(other.tail); + return other && this.head.isEqual(other.head) && this.tail.isEqual(other.tail) } get isBlank() { - return this.head.isBlank && this.tail.isBlank; + return this.head.isBlank && this.tail.isBlank } // "legacy" APIs get headSection() { - return this.head.section; + return this.head.section } get tailSection() { - return this.tail.section; + return this.tail.section } get headSectionOffset() { - return this.head.offset; + return this.head.offset } get tailSectionOffset() { - return this.tail.offset; + return this.tail.offset } get isCollapsed() { - return this.head.isEqual(this.tail); + return this.head.isEqual(this.tail) } get headMarker() { - return this.head.marker; + return this.head.marker } get tailMarker() { - return this.tail.marker; + return this.tail.marker } get headMarkerOffset() { - return this.head.offsetInMarker; + return this.head.offsetInMarker } get tailMarkerOffset() { - return this.tail.offsetInMarker; + return this.tail.offsetInMarker } } -export default Range; +export default Range diff --git a/src/js/utils/deprecate.js b/src/js/utils/deprecate.js index e8c2fe35b..11da51f75 100644 --- a/src/js/utils/deprecate.js +++ b/src/js/utils/deprecate.js @@ -7,9 +7,9 @@ * conditional is false: * `deprecate('Deprecated only if foo !== bar', foo === bar)` */ -export default function deprecate(message, conditional=false) { +export default function deprecate(message, conditional = false) { if (!conditional) { // eslint-disable-next-line no-console - console.log(`[mobiledoc-kit] [DEPRECATED]: ${message}`); + console.log(`[mobiledoc-kit] [DEPRECATED]: ${message}`) } } diff --git a/src/js/utils/dom-utils.js b/src/js/utils/dom-utils.js index 0206ed310..34fe21834 100644 --- a/src/js/utils/dom-utils.js +++ b/src/js/utils/dom-utils.js @@ -1,49 +1,48 @@ -import { forEach } from './array-utils'; +import { forEach } from './array-utils' export const NODE_TYPES = { ELEMENT: 1, TEXT: 3, - COMMENT: 8 -}; + COMMENT: 8, +} function isTextNode(node) { - return node.nodeType === NODE_TYPES.TEXT; + return node.nodeType === NODE_TYPES.TEXT } function isCommentNode(node) { - return node.nodeType === NODE_TYPES.COMMENT; + return node.nodeType === NODE_TYPES.COMMENT } function isElementNode(node) { - return node.nodeType === NODE_TYPES.ELEMENT; + return node.nodeType === NODE_TYPES.ELEMENT } // perform a pre-order tree traversal of the dom, calling `callbackFn(node)` // for every node for which `conditionFn(node)` is true -function walkDOM(topNode, callbackFn=()=>{}, conditionFn=()=>true) { - let currentNode = topNode; +function walkDOM(topNode, callbackFn = () => {}, conditionFn = () => true) { + let currentNode = topNode if (conditionFn(currentNode)) { - callbackFn(currentNode); + callbackFn(currentNode) } - currentNode = currentNode.firstChild; + currentNode = currentNode.firstChild while (currentNode) { - walkDOM(currentNode, callbackFn, conditionFn); - currentNode = currentNode.nextSibling; + walkDOM(currentNode, callbackFn, conditionFn) + currentNode = currentNode.nextSibling } } -function walkTextNodes(topNode, callbackFn=()=>{}) { - const conditionFn = (node) => isTextNode(node); - walkDOM(topNode, callbackFn, conditionFn); +function walkTextNodes(topNode, callbackFn = () => {}) { + const conditionFn = node => isTextNode(node) + walkDOM(topNode, callbackFn, conditionFn) } - function clearChildNodes(element) { while (element.childNodes.length) { - element.removeChild(element.childNodes[0]); + element.removeChild(element.childNodes[0]) } } @@ -56,10 +55,10 @@ function clearChildNodes(element) { */ function containsNode(parentNode, childNode) { if (parentNode === childNode) { - return true; + return true } - const position = parentNode.compareDocumentPosition(childNode); - return !!(position & Node.DOCUMENT_POSITION_CONTAINED_BY); + const position = parentNode.compareDocumentPosition(childNode) + return !!(position & Node.DOCUMENT_POSITION_CONTAINED_BY) } /** @@ -70,37 +69,37 @@ function containsNode(parentNode, childNode) { * @private */ function getAttributes(element) { - const result = {}; + const result = {} if (element.hasAttributes()) { - forEach(element.attributes, ({name,value}) => { - result[name] = value; - }); + forEach(element.attributes, ({ name, value }) => { + result[name] = value + }) } - return result; + return result } function addClassName(element, className) { - element.classList.add(className); + element.classList.add(className) } function removeClassName(element, className) { - element.classList.remove(className); + element.classList.remove(className) } function normalizeTagName(tagName) { - return tagName.toLowerCase(); + return tagName.toLowerCase() } function parseHTML(html) { - const div = document.createElement('div'); - div.innerHTML = html; - return div; + const div = document.createElement('div') + div.innerHTML = html + return div } function serializeHTML(node) { - const div = document.createElement('div'); - div.appendChild(node); - return div.innerHTML; + const div = document.createElement('div') + div.appendChild(node) + return div.innerHTML } export { @@ -116,5 +115,5 @@ export { isCommentNode, isElementNode, parseHTML, - serializeHTML -}; + serializeHTML, +} diff --git a/src/js/utils/element-map.js b/src/js/utils/element-map.js index f33767b99..36af65133 100644 --- a/src/js/utils/element-map.js +++ b/src/js/utils/element-map.js @@ -1,30 +1,29 @@ -import assert from 'mobiledoc-kit/utils/assert'; +import assert from 'mobiledoc-kit/utils/assert' // start at one to make the falsy semantics easier -let uuidGenerator = 1; +let uuidGenerator = 1 class ElementMap { constructor() { - this._map = {}; + this._map = {} } set(key, value) { - let uuid = key._uuid; + let uuid = key._uuid if (!uuid) { - key._uuid = uuid = '' + uuidGenerator++; + key._uuid = uuid = '' + uuidGenerator++ } - this._map[uuid] = value; + this._map[uuid] = value } get(key) { if (key._uuid) { - return this._map[key._uuid]; + return this._map[key._uuid] } - return null; + return null } remove(key) { - assert('tried to fetch a value for an element not seen before', !!key._uuid); - delete this._map[key._uuid]; + assert('tried to fetch a value for an element not seen before', !!key._uuid) + delete this._map[key._uuid] } - } -export default ElementMap; +export default ElementMap diff --git a/src/js/utils/element-utils.js b/src/js/utils/element-utils.js index cb0bd8df3..423f885ed 100644 --- a/src/js/utils/element-utils.js +++ b/src/js/utils/element-utils.js @@ -1,83 +1,83 @@ -import { dasherize } from 'mobiledoc-kit/utils/string-utils'; -import { - normalizeTagName -} from 'mobiledoc-kit/utils/dom-utils'; +import { dasherize } from 'mobiledoc-kit/utils/string-utils' +import { normalizeTagName } from 'mobiledoc-kit/utils/dom-utils' function getEventTargetMatchingTag(tagName, target, container) { - tagName = normalizeTagName(tagName); + 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; + return target } - target = target.parentNode; + 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; + 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; + offsetParentRect = offsetParent.getBoundingClientRect() + offset.left = offsetParentRect.left + offset.top = offsetParentRect.top } - return offset; + return offset } function getElementComputedStyleNumericProp(element, prop) { - return parseFloat(window.getComputedStyle(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; + 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 + rect.height - relativeOffset.top - topOffset); - style.left = left + 'px'; - style.top = top + 'px'; - return { left: left, top: top }; + topOffset = topOffset || 0 + leftOffset = leftOffset || 0 + left = round(rect.left - relativeOffset.left - leftOffset) + top = round(rect.top + rect.height - 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); + 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(), -elementMargin); + var elementMargin = getElementComputedStyleNumericProp(element, 'marginTop') + return positionElementHorizontallyCenteredToRect(element, belowElement.getBoundingClientRect(), -elementMargin) } function setData(element, name, value) { if (element.dataset) { - element.dataset[name] = value; + element.dataset[name] = value } else { - const dataName = dasherize(name); - return element.setAttribute(dataName, value); + const dataName = dasherize(name) + return element.setAttribute(dataName, value) } } function whenElementIsNotInDOM(element, callback) { - let isCanceled = false; + let isCanceled = false const observerFn = () => { - if (isCanceled) { return; } + if (isCanceled) { + return + } if (!element.parentNode) { - callback(); + callback() } else { - window.requestAnimationFrame(observerFn); + window.requestAnimationFrame(observerFn) } - }; - observerFn(); - return { cancel: () => isCanceled = true }; + } + observerFn() + return { cancel: () => (isCanceled = true) } } export { @@ -88,5 +88,5 @@ export { positionElementToRect, positionElementHorizontallyCenteredToRect, positionElementCenteredBelow, - whenElementIsNotInDOM -}; + whenElementIsNotInDOM, +} diff --git a/src/js/utils/environment.js b/src/js/utils/environment.js index 157cb8420..66f34fa04 100644 --- a/src/js/utils/environment.js +++ b/src/js/utils/environment.js @@ -1,5 +1,5 @@ export default { hasDOM() { - return typeof document !== 'undefined'; - } -}; + return typeof document !== 'undefined' + }, +} diff --git a/src/js/utils/fixed-queue.js b/src/js/utils/fixed-queue.js index 287bd6577..2a130b15b 100644 --- a/src/js/utils/fixed-queue.js +++ b/src/js/utils/fixed-queue.js @@ -1,29 +1,29 @@ export default class FixedQueue { - constructor(length=0) { - this._maxLength = length; - this._items = []; + constructor(length = 0) { + this._maxLength = length + this._items = [] } get length() { - return this._items.length; + return this._items.length } pop() { - return this._items.pop(); + return this._items.pop() } push(item) { - this._items.push(item); + this._items.push(item) if (this.length > this._maxLength) { - this._items.shift(); + this._items.shift() } } clear() { - this._items = []; + this._items = [] } toArray() { - return this._items; + return this._items } } diff --git a/src/js/utils/key.js b/src/js/utils/key.js index e6c8cfbb0..cd8b80e31 100644 --- a/src/js/utils/key.js +++ b/src/js/utils/key.js @@ -1,6 +1,6 @@ -import Keycodes from './keycodes'; -import Keys from './keys'; -import { TAB } from 'mobiledoc-kit/utils/characters'; +import Keycodes from './keycodes' +import Keys from './keys' +import { TAB } from 'mobiledoc-kit/utils/characters' /** * @typedef Direction @@ -10,55 +10,55 @@ import { TAB } from 'mobiledoc-kit/utils/characters'; */ export const DIRECTION = { FORWARD: 1, - BACKWARD: -1 -}; -import assert from './assert'; + BACKWARD: -1, +} +import assert from './assert' export const MODIFIERS = { META: 1, // also called "command" on OS X CTRL: 2, SHIFT: 4, - ALT: 8 // also called "option" on OS X -}; + ALT: 8, // also called "option" on OS X +} export function modifierMask(event) { - let { - metaKey, shiftKey, ctrlKey, altKey - } = 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); + 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 -}; + 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, +} export function specialCharacterToCode(specialCharacter) { - return SPECIAL_KEYS[specialCharacter]; + return SPECIAL_KEYS[specialCharacter] } // heuristic for determining if `event` is a key event function isKeyEvent(event) { - return /^key/.test(event.type); + return /^key/.test(event.type) } /** @@ -68,106 +68,106 @@ function isKeyEvent(event) { */ 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); + 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); + 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); + if (this.isTab()) { + return TAB + } + return String.fromCharCode(this.charCode) } // See https://caniuse.com/#feat=keyboardevent-key for browser support. isKeySupported() { - return this.key; + return this.key } isKey(identifier) { if (this.isKeySupported()) { - assert(`Must define Keys.${identifier}.`, Keys[identifier]); - return this.key === Keys[identifier]; + 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]; + assert(`Must define Keycodes.${identifier}.`, Keycodes[identifier]) + return this.keyCode === Keycodes[identifier] } } isEscape() { - return this.isKey('ESC'); + return this.isKey('ESC') } isDelete() { - return this.isKey('BACKSPACE') || this.isForwardDelete(); + return this.isKey('BACKSPACE') || this.isForwardDelete() } isForwardDelete() { - return this.isKey('DELETE'); + return this.isKey('DELETE') } isArrow() { - return this.isHorizontalArrow() || this.isVerticalArrow(); + return this.isHorizontalArrow() || this.isVerticalArrow() } isHorizontalArrow() { - return this.isLeftArrow() || this.isRightArrow(); + return this.isLeftArrow() || this.isRightArrow() } isHorizontalArrowWithoutModifiersOtherThanShift() { - return this.isHorizontalArrow() && - !(this.ctrlKey || this.metaKey || this.altKey); + return this.isHorizontalArrow() && !(this.ctrlKey || this.metaKey || this.altKey) } isVerticalArrow() { - return this.isKey('UP') || this.isKey('DOWN'); + return this.isKey('UP') || this.isKey('DOWN') } isLeftArrow() { - return this.isKey('LEFT'); + return this.isKey('LEFT') } isRightArrow() { - return this.isKey('RIGHT'); + return this.isKey('RIGHT') } isHome() { - return this.isKey('HOME'); + return this.isKey('HOME') } isEnd() { - return this.isKey('END'); + return this.isKey('END') } isPageUp() { - return this.isKey('PAGEUP'); + return this.isKey('PAGEUP') } isPageDown() { - return this.isKey('PAGEDOWN'); + return this.isKey('PAGEDOWN') } isInsert() { - return this.isKey('INS'); + return this.isKey('INS') } isClear() { - return this.isKey('CLEAR'); + return this.isKey('CLEAR') } isPause() { - return this.isKey('PAUSE'); + return this.isKey('PAUSE') } isSpace() { - return this.isKey('SPACE'); + return this.isKey('SPACE') } // In Firefox, pressing ctrl-TAB will switch to another open browser tab, but @@ -178,11 +178,11 @@ const Key = class Key { // modifier. // See: https://github.com/bustle/mobiledoc-kit/issues/565 isTab() { - return !this.hasAnyModifier() && this.isKey('TAB'); + return !this.hasAnyModifier() && this.isKey('TAB') } isEnter() { - return this.isKey('ENTER'); + return this.isKey('ENTER') } /* @@ -192,7 +192,7 @@ const Key = class Key { * @return {bool} */ isShiftKey() { - return this.isKey('SHIFT'); + return this.isKey('SHIFT') } /* @@ -201,7 +201,7 @@ const Key = class Key { * @return {bool} */ isAltKey() { - return this.isKey('ALT'); + return this.isKey('ALT') } /* @@ -210,21 +210,21 @@ const Key = class Key { * @return {bool} */ isCtrlKey() { - return this.isKey('CTRL'); + 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; + return this.keyCode === Keycodes.IME } get direction() { switch (true) { case this.isDelete(): - return this.isForwardDelete() ? DIRECTION.FORWARD : DIRECTION.BACKWARD; + return this.isForwardDelete() ? DIRECTION.FORWARD : DIRECTION.BACKWARD case this.isHorizontalArrow(): - return this.isRightArrow() ? DIRECTION.FORWARD : DIRECTION.BACKWARD; + return this.isRightArrow() ? DIRECTION.FORWARD : DIRECTION.BACKWARD } } @@ -237,74 +237,75 @@ const Key = class Key { * @return {bool} */ isShift() { - return this.shiftKey; + return this.shiftKey } hasModifier(modifier) { - return modifier & this.modifierMask; + return modifier & this.modifierMask } hasAnyModifier() { - return !!this.modifierMask; + return !!this.modifierMask } get ctrlKey() { - return MODIFIERS.CTRL & this.modifierMask; + return MODIFIERS.CTRL & this.modifierMask } get metaKey() { - return MODIFIERS.META & this.modifierMask; + return MODIFIERS.META & this.modifierMask } get shiftKey() { - return MODIFIERS.SHIFT & this.modifierMask; + return MODIFIERS.SHIFT & this.modifierMask } get altKey() { - return MODIFIERS.ALT & this.modifierMask; + return MODIFIERS.ALT & this.modifierMask } isPrintableKey() { return !( this.isArrow() || - this.isHome() || this.isEnd() || - this.isPageUp() || this.isPageDown() || - this.isInsert() || this.isClear() || this.isPause() || + 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'; + 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 + 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'); + 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); + 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 <= '"'); + const key = this.key + return (key >= ';' && key <= '`') || (key >= '[' && key <= '"') } else { - const code = this.keyCode; - return (code >= Keycodes[';'] && code <= Keycodes['`']) || - (code >= Keycodes['['] && code <= Keycodes['"']); + const code = this.keyCode + return (code >= Keycodes[';'] && code <= Keycodes['`']) || (code >= Keycodes['['] && code <= Keycodes['"']) } } @@ -314,12 +315,12 @@ const Key = class Key { */ isPrintable() { if (this.ctrlKey || this.metaKey) { - return false; + return false } // Firefox calls keypress events for some keys that should not be printable if (!this.isPrintableKey()) { - return false; + return false } return ( @@ -327,13 +328,13 @@ const Key = class Key { this.toString().length > 0 || this.isNumberKey() || this.isSpace() || - this.isTab() || + this.isTab() || this.isEnter() || this.isLetterKey() || this.isPunctuation() || this.isIME() - ); + ) } -}; +} -export default Key; +export default Key diff --git a/src/js/utils/keycodes.js b/src/js/utils/keycodes.js index d7d0fa50a..d79aaded7 100644 --- a/src/js/utils/keycodes.js +++ b/src/js/utils/keycodes.js @@ -1,43 +1,43 @@ export default { - 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, + 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, + 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 -}; + 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, +} diff --git a/src/js/utils/keys.js b/src/js/utils/keys.js index 1bbb47f61..6b79e0653 100644 --- a/src/js/utils/keys.js +++ b/src/js/utils/keys.js @@ -1,23 +1,23 @@ export default { - 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', + 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' -}; + LEFT: 'ArrowLeft', + RIGHT: 'ArrowRight', + UP: 'ArrowUp', + DOWN: 'ArrowDown', +} diff --git a/src/js/utils/linked-item.js b/src/js/utils/linked-item.js index 923ff20ab..ac2b86c28 100644 --- a/src/js/utils/linked-item.js +++ b/src/js/utils/linked-item.js @@ -1,6 +1,6 @@ export default class LinkedItem { constructor() { - this.next = null; - this.prev = null; + this.next = null + this.prev = null } } diff --git a/src/js/utils/linked-list.js b/src/js/utils/linked-list.js index f95240996..fea82bfcb 100644 --- a/src/js/utils/linked-list.js +++ b/src/js/utils/linked-list.js @@ -1,210 +1,212 @@ -import assert from './assert'; +import assert from './assert' -const PARENT_PROP = '__parent'; +const PARENT_PROP = '__parent' export default class LinkedList { constructor(options) { - this.head = null; - this.tail = null; - this.length = 0; + this.head = null + this.tail = null + this.length = 0 if (options) { - const {adoptItem, freeItem} = options; - this._adoptItem = adoptItem; - this._freeItem = freeItem; + const { adoptItem, freeItem } = options + this._adoptItem = adoptItem + this._freeItem = freeItem } } adoptItem(item) { - item[PARENT_PROP]= this; - this.length++; - if (this._adoptItem) { this._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); } + item[PARENT_PROP] = null + this.length-- + if (this._freeItem) { + this._freeItem(item) + } } get isEmpty() { - return this.length === 0; + return this.length === 0 } prepend(item) { - this.insertBefore(item, this.head); + this.insertBefore(item, this.head) } append(item) { - this.insertBefore(item, null); + this.insertBefore(item, null) } insertAfter(item, prevItem) { - let nextItem = prevItem ? prevItem.next : this.head; - this.insertBefore(item, nextItem); + let nextItem = prevItem ? prevItem.next : this.head + this.insertBefore(item, nextItem) } - _ensureItemIsNotAlreadyInList(item){ + _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); + this._ensureItemIsNotInList(item) + this.adoptItem(item) - let insertPos; + let insertPos if (nextItem && nextItem.prev) { - insertPos = 'middle'; + insertPos = 'middle' } else if (nextItem) { - insertPos = 'start'; + insertPos = 'start' } else { - insertPos = 'end'; + insertPos = 'end' } switch (insertPos) { case 'start': if (this.head) { - item.next = this.head; - this.head.prev = item; + item.next = this.head + this.head.prev = item } - this.head = item; + this.head = item - break; + break case 'middle': { - let prevItem = nextItem.prev; - item.next = nextItem; - item.prev = prevItem; - nextItem.prev = item; - prevItem.next = item; + let prevItem = nextItem.prev + item.next = nextItem + item.prev = prevItem + nextItem.prev = item + prevItem.next = item - break; + break } case 'end': { - let tail = this.tail; - item.prev = tail; + let tail = this.tail + item.prev = tail if (tail) { - tail.next = item; + tail.next = item } else { - this.head = item; + this.head = item } - this.tail = item; + this.tail = item - break; + break } } } remove(item) { if (!item[PARENT_PROP]) { - return; + return } - this._ensureItemIsInThisList(item); - this.freeItem(item); + this._ensureItemIsInThisList(item) + this.freeItem(item) - let [prev, next] = [item.prev, item.next]; - item.prev = null; - item.next = null; + let [prev, next] = [item.prev, item.next] + item.prev = null + item.next = null if (prev) { - prev.next = next; + prev.next = next } else { - this.head = next; + this.head = next } if (next) { - next.prev = prev; + next.prev = prev } else { - this.tail = prev; + this.tail = prev } } forEach(callback) { - let item = this.head; - let index = 0; + let item = this.head + let index = 0 while (item) { - callback(item, index++); - item = item.next; + callback(item, index++) + item = item.next } } map(callback) { - let result = []; - this.forEach(i => result.push(callback(i))); - return result; + let result = [] + this.forEach(i => result.push(callback(i))) + return result } walk(startItem, endItem, callback) { - let item = startItem || this.head; + let item = startItem || this.head while (item) { - callback(item); + callback(item) if (item === endItem) { - break; + break } - item = item.next; + item = item.next } } readRange(startItem, endItem) { - let items = []; - this.walk(startItem, endItem, (item) => { - items.push(item); - }); - return items; + let items = [] + this.walk(startItem, endItem, item => { + items.push(item) + }) + return items } toArray() { - return this.readRange(); + return this.readRange() } - detect(callback, item=this.head, reverse=false) { + detect(callback, item = this.head, reverse = false) { while (item) { if (callback(item)) { - return item; + return item } - item = reverse ? item.prev : item.next; + item = reverse ? item.prev : item.next } } any(callback) { - return !!this.detect(callback); + return !!this.detect(callback) } every(callback) { - let item = this.head; + let item = this.head while (item) { if (!callback(item)) { - return false; + return false } - item = item.next; + item = item.next } - return true; + return true } objectAt(targetIndex) { - let index = -1; + let index = -1 return this.detect(() => { - index++; - return (targetIndex === index); - }); + index++ + return targetIndex === index + }) } splice(targetItem, removalCount, newItems) { - let item = targetItem; - let nextItem = item.next; - let count = 0; + let item = targetItem + let nextItem = item.next + let count = 0 while (item && count < removalCount) { - count++; - nextItem = item.next; - this.remove(item); - item = nextItem; + count++ + nextItem = item.next + this.remove(item) + item = nextItem } - newItems.forEach((newItem) => { - this.insertBefore(newItem, nextItem); - }); + newItems.forEach(newItem => { + this.insertBefore(newItem, nextItem) + }) } removeBy(conditionFn) { - let item = this.head; + let item = this.head while (item) { - let nextItem = item.next; + let nextItem = item.next if (conditionFn(item)) { - this.remove(item); + this.remove(item) } - item = nextItem; + item = nextItem } } _ensureItemIsNotInList(item) { - assert('Cannot insert an item into a list if it is already in a list', - !item[PARENT_PROP]); + 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); + assert('Cannot remove item that is in another list', item[PARENT_PROP] === this) } } diff --git a/src/js/utils/log-manager.js b/src/js/utils/log-manager.js index b39a4b33d..92cdac8d9 100644 --- a/src/js/utils/log-manager.js +++ b/src/js/utils/log-manager.js @@ -1,47 +1,47 @@ class Logger { constructor(type, manager) { - this.type = type; - this.manager = manager; + this.type = type + this.manager = manager } isEnabled() { - return this.manager.isEnabled(this.type); + return this.manager.isEnabled(this.type) } log(...args) { - args.unshift(`[${this.type}]`); + args.unshift(`[${this.type}]`) if (this.isEnabled()) { - window.console.log(...args); + window.console.log(...args) } } } class LogManager { constructor() { - this.enabledTypes = []; - this.allEnabled = false; + this.enabledTypes = [] + this.allEnabled = false } for(type) { - return new Logger(type, this); + return new Logger(type, this) } enableAll() { - this.allEnabled = true; + this.allEnabled = true } enableTypes(types) { - this.enabledTypes = this.enabledTypes.concat(types); + this.enabledTypes = this.enabledTypes.concat(types) } disable() { - this.enabledTypes = []; - this.allEnabled = false; + this.enabledTypes = [] + this.allEnabled = false } isEnabled(type) { - return this.allEnabled || this.enabledTypes.indexOf(type) !== -1; + return this.allEnabled || this.enabledTypes.indexOf(type) !== -1 } } -export default LogManager; +export default LogManager diff --git a/src/js/utils/markuperable.js b/src/js/utils/markuperable.js index d54753aa6..42c8cd9de 100644 --- a/src/js/utils/markuperable.js +++ b/src/js/utils/markuperable.js @@ -1,71 +1,67 @@ -import { normalizeTagName } from '../utils/dom-utils'; -import { detect, commonItemLength, forEach, filter } from '../utils/array-utils'; +import { normalizeTagName } from '../utils/dom-utils' +import { detect, commonItemLength, forEach, filter } from '../utils/array-utils' export default class Markerupable { - clearMarkups() { - this.markups = []; + this.markups = [] } addMarkup(markup) { - this.markups.push(markup); + this.markups.push(markup) } addMarkupAtIndex(markup, index) { - this.markups.splice(index, 0, markup); + this.markups.splice(index, 0, markup) } removeMarkup(markupOrMarkupCallback) { - let callback; + let callback if (typeof markupOrMarkupCallback === 'function') { - callback = markupOrMarkupCallback; + callback = markupOrMarkupCallback } else { - let markup = markupOrMarkupCallback; - callback = (_markup) => _markup === markup; + let markup = markupOrMarkupCallback + callback = _markup => _markup === markup } - forEach( - filter(this.markups, callback), - m => this._removeMarkup(m) - ); + forEach(filter(this.markups, callback), m => this._removeMarkup(m)) } _removeMarkup(markup) { - const index = this.markups.indexOf(markup); + const index = this.markups.indexOf(markup) if (index !== -1) { - this.markups.splice(index, 1); + this.markups.splice(index, 1) } } hasMarkup(tagNameOrMarkup) { - return !!this.getMarkup(tagNameOrMarkup); + return !!this.getMarkup(tagNameOrMarkup) } getMarkup(tagNameOrMarkup) { if (typeof tagNameOrMarkup === 'string') { - let tagName = normalizeTagName(tagNameOrMarkup); - return detect(this.markups, markup => markup.tagName === tagName); + let tagName = normalizeTagName(tagNameOrMarkup) + return detect(this.markups, markup => markup.tagName === tagName) } else { - let targetMarkup = tagNameOrMarkup; - return detect(this.markups, markup => markup === targetMarkup); + let targetMarkup = tagNameOrMarkup + return detect(this.markups, markup => markup === targetMarkup) } } get openedMarkups() { - let count = 0; + let count = 0 if (this.prev) { - count = commonItemLength(this.markups, this.prev.markups); + count = commonItemLength(this.markups, this.prev.markups) } - return this.markups.slice(count); + return this.markups.slice(count) } get closedMarkups() { - let count = 0; + let count = 0 if (this.next) { - count = commonItemLength(this.markups, this.next.markups); + count = commonItemLength(this.markups, this.next.markups) } - return this.markups.slice(count); + return this.markups.slice(count) } } diff --git a/src/js/utils/merge.js b/src/js/utils/merge.js index 193091a73..1c3ef103c 100644 --- a/src/js/utils/merge.js +++ b/src/js/utils/merge.js @@ -1,13 +1,13 @@ function mergeWithOptions(original, updates, options) { - options = options || {}; - for(var prop in updates) { + options = options || {} + for (var prop in updates) { if (options.hasOwnProperty(prop)) { - original[prop] = options[prop]; + original[prop] = options[prop] } else if (updates.hasOwnProperty(prop)) { - original[prop] = updates[prop]; + original[prop] = updates[prop] } } - return original; + return original } /** @@ -15,7 +15,7 @@ function mergeWithOptions(original, updates, options) { * @private */ function merge(original, updates) { - return mergeWithOptions(original, updates); + return mergeWithOptions(original, updates) } -export { mergeWithOptions, merge }; +export { mergeWithOptions, merge } diff --git a/src/js/utils/mixin.js b/src/js/utils/mixin.js index e798ca71e..676f530c3 100644 --- a/src/js/utils/mixin.js +++ b/src/js/utils/mixin.js @@ -1,15 +1,15 @@ -const CONSTRUCTOR_FN_NAME = 'constructor'; +const CONSTRUCTOR_FN_NAME = 'constructor' export default function mixin(target, source) { - target = target.prototype; + target = target.prototype // Fallback to just `source` to allow mixing in a plain object (pojo) - source = source.prototype || source; + source = source.prototype || source - Object.getOwnPropertyNames(source).forEach((name) => { + Object.getOwnPropertyNames(source).forEach(name => { if (name !== CONSTRUCTOR_FN_NAME) { - const descriptor = Object.getOwnPropertyDescriptor(source, name); + const descriptor = Object.getOwnPropertyDescriptor(source, name) - Object.defineProperty(target, name, descriptor); + Object.defineProperty(target, name, descriptor) } - }); + }) } diff --git a/src/js/utils/mobiledoc-error.js b/src/js/utils/mobiledoc-error.js index 46c9f5f4f..83a2c6c1e 100644 --- a/src/js/utils/mobiledoc-error.js +++ b/src/js/utils/mobiledoc-error.js @@ -1,25 +1,17 @@ -var errorProps = [ - 'description', - 'fileName', - 'lineNumber', - 'message', - 'name', - 'number', - 'stack' -]; +var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'] function MobiledocError() { - let tmp = Error.apply(this, arguments); + let tmp = Error.apply(this, arguments) if (Error.captureStackTrace) { - Error.captureStackTrace(this, this.constructor); + 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]]; + this[errorProps[idx]] = tmp[errorProps[idx]] } } -MobiledocError.prototype = Object.create(Error.prototype); +MobiledocError.prototype = Object.create(Error.prototype) -export default MobiledocError; +export default MobiledocError diff --git a/src/js/utils/object-utils.js b/src/js/utils/object-utils.js index 1f5b5ff14..3bfb759b6 100644 --- a/src/js/utils/object-utils.js +++ b/src/js/utils/object-utils.js @@ -1,11 +1,11 @@ export function entries(obj) { - const ownProps = Object.keys(obj); - let i = ownProps.length; - const resArray = new Array(i); + const ownProps = Object.keys(obj) + let i = ownProps.length + const resArray = new Array(i) while (i--) { - resArray[i] = [ownProps[i], obj[ownProps[i]]]; + resArray[i] = [ownProps[i], obj[ownProps[i]]] } - return resArray; + return resArray } diff --git a/src/js/utils/parse-utils.js b/src/js/utils/parse-utils.js index 6c013f539..a2ccd9d32 100644 --- a/src/js/utils/parse-utils.js +++ b/src/js/utils/parse-utils.js @@ -1,30 +1,30 @@ /* global JSON */ -import mobiledocParsers from '../parsers/mobiledoc'; -import HTMLParser from '../parsers/html'; -import TextParser from '../parsers/text'; +import mobiledocParsers from '../parsers/mobiledoc' +import HTMLParser from '../parsers/html' +import TextParser from '../parsers/text' -export const MIME_TEXT_PLAIN = 'text/plain'; -export const MIME_TEXT_HTML = 'text/html'; -export const NONSTANDARD_IE_TEXT_TYPE = 'Text'; +export const MIME_TEXT_PLAIN = 'text/plain' +export const MIME_TEXT_HTML = 'text/html' +export const NONSTANDARD_IE_TEXT_TYPE = 'Text' -const MOBILEDOC_REGEX = new RegExp(/data\-mobiledoc='(.*?)'>/); +const MOBILEDOC_REGEX = new RegExp(/data\-mobiledoc='(.*?)'>/) /** * @return {Post} * @private */ function parsePostFromHTML(html, builder, plugins) { - let post; + let post if (MOBILEDOC_REGEX.test(html)) { - let mobiledocString = html.match(MOBILEDOC_REGEX)[1]; - let mobiledoc = JSON.parse(mobiledocString); - post = mobiledocParsers.parse(builder, mobiledoc); + 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); + post = new HTMLParser(builder, { plugins }).parse(html) } - return post; + return post } /** @@ -32,9 +32,9 @@ function parsePostFromHTML(html, builder, plugins) { * @private */ function parsePostFromText(text, builder, plugins) { - let parser = new TextParser(builder, {plugins}); - let post = parser.parse(text); - return post; + let parser = new TextParser(builder, { plugins }) + let post = parser.parse(text) + return post } /** @@ -42,21 +42,23 @@ function parsePostFromText(text, builder, plugins) { * @private */ export function getContentFromPasteEvent(event, window) { - let html = '', text = ''; + let html = '', + text = '' - let { clipboardData } = event; + 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 + 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); + html = window.clipboardData.getData(NONSTANDARD_IE_TEXT_TYPE) } - return { html, text }; + return { html, text } } /** @@ -64,21 +66,22 @@ export function getContentFromPasteEvent(event, window) { * @private */ function getContentFromDropEvent(event, logger) { - let html = '', text = ''; + let html = '', + text = '' try { - html = event.dataTransfer.getData(MIME_TEXT_HTML); - text = event.dataTransfer.getData(MIME_TEXT_PLAIN); + 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); + logger.log('Error getting drop data: ', e) } } - return { html, text }; + return { html, text } } /** @@ -87,22 +90,22 @@ function getContentFromDropEvent(event, logger) { * @param {Window} * @private */ -export function setClipboardData(event, {mobiledoc, html, text}, window) { +export function setClipboardData(event, { mobiledoc, html, text }, window) { if (mobiledoc && html) { - html = `
    ${html}
    `; + html = `
    ${html}
    ` } - let { clipboardData } = event; - let { clipboardData: nonstandardClipboardData } = window; + let { clipboardData } = event + let { clipboardData: nonstandardClipboardData } = window if (clipboardData && clipboardData.setData) { - clipboardData.setData(MIME_TEXT_HTML, html); - clipboardData.setData(MIME_TEXT_PLAIN, text); + 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); + nonstandardClipboardData.setData(NONSTANDARD_IE_TEXT_TYPE, html) } } @@ -112,13 +115,17 @@ export function setClipboardData(event, {mobiledoc, html, text}, window) { * @return {Post} * @private */ -export function parsePostFromPaste(pasteEvent, {builder, _parserPlugins: plugins}, {targetFormat}={targetFormat:'html'}) { - let { html, text } = getContentFromPasteEvent(pasteEvent, window); +export 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); + return parsePostFromHTML(html, builder, plugins) } else if (text && text.length) { - return parsePostFromText(text, builder, plugins); + return parsePostFromText(text, builder, plugins) } } @@ -129,13 +136,13 @@ export function parsePostFromPaste(pasteEvent, {builder, _parserPlugins: plugins * @return {Post} * @private */ -export function parsePostFromDrop(dropEvent, editor, {logger}={}) { - let { builder, _parserPlugins: plugins } = editor; - let { html, text } = getContentFromDropEvent(dropEvent, logger); +export 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); + return parsePostFromHTML(html, builder, plugins) } else if (text && text.length) { - return parsePostFromText(text, builder, plugins); + return parsePostFromText(text, builder, plugins) } } diff --git a/src/js/utils/placeholder-image-src.js b/src/js/utils/placeholder-image-src.js index 4ac64acec..67b6ae34c 100644 --- a/src/js/utils/placeholder-image-src.js +++ b/src/js/utils/placeholder-image-src.js @@ -1,3 +1,4 @@ -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="; +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=' -export default placeholderImageSrc; +export default placeholderImageSrc diff --git a/src/js/utils/selection-utils.js b/src/js/utils/selection-utils.js index d932c5c93..cb595c6ca 100644 --- a/src/js/utils/selection-utils.js +++ b/src/js/utils/selection-utils.js @@ -1,33 +1,32 @@ -import { DIRECTION } from '../utils/key'; -import { isTextNode, isElementNode } from 'mobiledoc-kit/utils/dom-utils'; +import { DIRECTION } from '../utils/key' +import { isTextNode, isElementNode } from 'mobiledoc-kit/utils/dom-utils' function clearSelection() { - window.getSelection().removeAllRanges(); + window.getSelection().removeAllRanges() } function textNodeRects(node) { - let range = document.createRange(); - range.setEnd(node, node.nodeValue.length); - range.setStart(node, 0); - return range.getClientRects(); + 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(); + 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(); + range.setEnd(node, i + 1) + range.setStart(node, i) + let rect = range.getBoundingClientRect() if (rect.top === rect.bottom) { - continue; + 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)}; + 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}; + return { node, offset: 0 } } /* @@ -37,77 +36,78 @@ function findOffsetInTextNode(node, coords) { */ /* eslint-disable complexity */ function findOffsetInNode(node, coords) { - let closest, dyClosest = 1e8, coordsClosest, offset = 0; + let closest, + dyClosest = 1e8, + coordsClosest, + offset = 0 for (let child = node.firstChild; child; child = child.nextSibling) { - let rects; + let rects if (isElementNode(child)) { - rects = child.getClientRects(); + rects = child.getClientRects() } else if (isTextNode(child)) { - rects = textNodeRects(child); + rects = textNodeRects(child) } else { - continue; + continue } for (let i = 0; i < rects.length; i++) { - let rect = rects[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; + 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; + 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); + offset = i + (coords.left >= (rect.left + rect.right) / 2 ? 1 : 0) } - continue; + continue } } - if (!closest && - (coords.top >= rect.bottom || coords.top >= rect.top && coords.left >= rect.right)) { - offset = i + 1; + if (!closest && (coords.top >= rect.bottom || (coords.top >= rect.top && coords.left >= rect.right))) { + offset = i + 1 } } } if (!closest) { - return {node, offset}; + return { node, offset } } if (isTextNode(closest)) { - return findOffsetInTextNode(closest, coordsClosest); + return findOffsetInTextNode(closest, coordsClosest) } if (closest.firstChild) { - return findOffsetInNode(closest, coordsClosest); + return findOffsetInNode(closest, coordsClosest) } - return {node, offset}; + return { node, offset } } /* eslint-enable complexity */ function constrainNodeTo(node, parentNode, existingOffset) { - let compare = parentNode.compareDocumentPosition(node); + let compare = parentNode.compareDocumentPosition(node) if (compare & Node.DOCUMENT_POSITION_CONTAINED_BY) { // the node is inside parentNode, do nothing - return { node, offset: existingOffset}; + return { node, offset: existingOffset } } else if (compare & Node.DOCUMENT_POSITION_CONTAINS) { // the node contains parentNode. This shouldn't happen. - return { node, offset: existingOffset}; + 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; + let child = parentNode.firstChild while (child.firstChild) { - child = child.firstChild; + child = child.firstChild } - return { node: child, offset: 0}; + 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; + let child = parentNode.lastChild while (child.lastChild) { - child = child.lastChild; + child = child.lastChild } - let offset = isTextNode(child) ? child.textContent.length : 1; - return {node: child, offset}; + let offset = isTextNode(child) ? child.textContent.length : 1 + return { node: child, offset } } else { - return { node, offset: existingOffset}; + return { node, offset: existingOffset } } } @@ -117,23 +117,21 @@ function constrainNodeTo(node, parentNode, existingOffset) { * 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); + 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 }; + return { anchorNode, anchorOffset, focusNode, focusOffset } } function comparePosition(selection) { - let { anchorNode, focusNode, anchorOffset, focusOffset } = selection; - let headNode, tailNode, headOffset, tailOffset, direction; + let { anchorNode, focusNode, anchorOffset, focusOffset } = selection + let headNode, tailNode, headOffset, tailOffset, direction - const position = anchorNode.compareDocumentPosition(focusNode); + 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 @@ -149,63 +147,65 @@ function comparePosition(selection) { // if (position & Node.DOCUMENT_POSITION_CONTAINS) { if (focusOffset < focusNode.childNodes.length) { - focusNode = focusNode.childNodes[focusOffset]; - focusOffset = 0; + 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; + focusNode = focusNode.lastChild } - focusOffset = focusNode.textContent.length; + focusOffset = focusNode.textContent.length } return comparePosition({ focusNode, focusOffset, - anchorNode, anchorOffset - }); + anchorNode, + anchorOffset, + }) } else if (position & Node.DOCUMENT_POSITION_CONTAINED_BY) { - let offset = anchorOffset - 1; + let offset = anchorOffset - 1 if (offset < 0) { - 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 + 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; + 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; + 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; + headOffset = focusOffset + tailOffset = anchorOffset + direction = DIRECTION.BACKWARD } else if (headOffset < tailOffset) { - direction = DIRECTION.FORWARD; + direction = DIRECTION.FORWARD } else { - direction = null; + direction = null } } - return {headNode, headOffset, tailNode, tailOffset, direction}; + return { headNode, headOffset, tailNode, tailOffset, direction } } -export { - clearSelection, - comparePosition, - findOffsetInNode, - constrainSelectionTo -}; +export { clearSelection, comparePosition, findOffsetInNode, constrainSelectionTo } diff --git a/src/js/utils/set.js b/src/js/utils/set.js index 599673fae..1a514ccaf 100644 --- a/src/js/utils/set.js +++ b/src/js/utils/set.js @@ -1,24 +1,24 @@ export default class Set { - constructor(items=[]) { - this.items = []; - items.forEach(i => this.add(i)); + constructor(items = []) { + this.items = [] + items.forEach(i => this.add(i)) } add(item) { if (!this.has(item)) { - this.items.push(item); + this.items.push(item) } } get length() { - return this.items.length; + return this.items.length } has(item) { - return this.items.indexOf(item) !== -1; + return this.items.indexOf(item) !== -1 } toArray() { - return this.items; + return this.items } } diff --git a/src/js/utils/string-utils.js b/src/js/utils/string-utils.js index d76137d65..9942a18a3 100644 --- a/src/js/utils/string-utils.js +++ b/src/js/utils/string-utils.js @@ -4,21 +4,21 @@ */ export function dasherize(string) { return string.replace(/[A-Z]/g, (match, offset) => { - const lower = match.toLowerCase(); + const lower = match.toLowerCase() - return (offset === 0 ? lower : '-' + lower); - }); + return offset === 0 ? lower : '-' + lower + }) } export function capitalize(string) { - return string.charAt(0).toUpperCase() + string.slice(1); + return string.charAt(0).toUpperCase() + string.slice(1) } export function startsWith(string, character) { - return string.charAt(0) === character; + return string.charAt(0) === character } export function endsWith(string, endString) { - let index = string.lastIndexOf(endString); - return index !== -1 && index === string.length - endString.length; + let index = string.lastIndexOf(endString) + return index !== -1 && index === string.length - endString.length } diff --git a/src/js/utils/to-range.js b/src/js/utils/to-range.js index 1fe59660b..086baf4fe 100644 --- a/src/js/utils/to-range.js +++ b/src/js/utils/to-range.js @@ -1,15 +1,15 @@ -import Range from 'mobiledoc-kit/utils/cursor/range'; -import Position from 'mobiledoc-kit/utils/cursor/position'; -import assert from 'mobiledoc-kit/utils/assert'; +import Range from 'mobiledoc-kit/utils/cursor/range' +import Position from 'mobiledoc-kit/utils/cursor/position' +import assert from 'mobiledoc-kit/utils/assert' export default function toRange(rangeLike) { - assert(`Must pass non-blank object to "toRange"`, !!rangeLike); + assert(`Must pass non-blank object to "toRange"`, !!rangeLike) if (rangeLike instanceof Range) { - return rangeLike; + return rangeLike } else if (rangeLike instanceof Position) { - return rangeLike.toRange(); + return rangeLike.toRange() } - assert(`Incorrect structure for rangeLike: ${rangeLike}`, false); + assert(`Incorrect structure for rangeLike: ${rangeLike}`, false) } diff --git a/src/js/version.js b/src/js/version.js index 2034b2955..55b1d631e 100644 --- a/src/js/version.js +++ b/src/js/version.js @@ -1 +1 @@ -export default '##VERSION##'; +export default '##VERSION##' diff --git a/src/js/views/tooltip.js b/src/js/views/tooltip.js index 7c1e4d2dc..944a90bef 100644 --- a/src/js/views/tooltip.js +++ b/src/js/views/tooltip.js @@ -1,88 +1,86 @@ -import View from './view'; -import { - positionElementCenteredBelow, - getEventTargetMatchingTag, - whenElementIsNotInDOM -} from '../utils/element-utils'; -import { editLink } from '../editor/ui'; +import View from './view' +import { positionElementCenteredBelow, getEventTargetMatchingTag, whenElementIsNotInDOM } from '../utils/element-utils' +import { editLink } from '../editor/ui' -const SHOW_DELAY = 200; -const HIDE_DELAY = 600; +const SHOW_DELAY = 200 +const HIDE_DELAY = 600 export default class Tooltip extends View { constructor(options) { - options.classNames = ['__mobiledoc-tooltip']; - super(options); + options.classNames = ['__mobiledoc-tooltip'] + super(options) - this.rootElement = options.rootElement; - this.editor = options.editor; + this.rootElement = options.rootElement + this.editor = options.editor - this.addListeners(options); + this.addListeners(options) } showLink(linkEl) { - const { editor, element: tooltipEl } = this; - const { tooltipPlugin } = editor; + const { editor, element: tooltipEl } = this + const { tooltipPlugin } = editor tooltipPlugin.renderLink(tooltipEl, linkEl, { editLink: () => { - editLink(linkEl, editor); - this.hide(); - } - }); + editLink(linkEl, editor) + this.hide() + }, + }) - this.show(); - positionElementCenteredBelow(this.element, linkEl); + this.show() + positionElementCenteredBelow(this.element, linkEl) - this.elementObserver = whenElementIsNotInDOM(linkEl, () => this.hide()); + this.elementObserver = whenElementIsNotInDOM(linkEl, () => this.hide()) } addListeners(options) { - const { rootElement, element: tooltipElement } = this; - let showTimeout, hideTimeout; + const { rootElement, element: tooltipElement } = this + let showTimeout, hideTimeout const scheduleHide = () => { - clearTimeout(hideTimeout); + clearTimeout(hideTimeout) hideTimeout = setTimeout(() => { - this.hide(); - }, HIDE_DELAY); - }; + this.hide() + }, HIDE_DELAY) + } this.addEventListener(tooltipElement, 'mouseenter', e => { - clearTimeout(hideTimeout); - }); + clearTimeout(hideTimeout) + }) this.addEventListener(tooltipElement, 'mouseleave', e => { - scheduleHide(); - }); + scheduleHide() + }) - this.addEventListener(rootElement, 'mouseover', (e) => { - let target = getEventTargetMatchingTag(options.showForTag, e.target, rootElement); + this.addEventListener(rootElement, 'mouseover', e => { + let target = getEventTargetMatchingTag(options.showForTag, e.target, rootElement) if (target && target.isContentEditable) { - clearTimeout(hideTimeout); + clearTimeout(hideTimeout) showTimeout = setTimeout(() => { - this.showLink(target); - }, SHOW_DELAY); + this.showLink(target) + }, SHOW_DELAY) } - }); + }) - this.addEventListener(rootElement, 'mouseout', (e) => { - clearTimeout(showTimeout); - if (this.elementObserver) { this.elementObserver.cancel(); } - scheduleHide(); - }); + this.addEventListener(rootElement, 'mouseout', e => { + clearTimeout(showTimeout) + if (this.elementObserver) { + this.elementObserver.cancel() + } + scheduleHide() + }) } } export const DEFAULT_TOOLTIP_PLUGIN = { renderLink(tooltipEl, linkEl, { editLink }) { - const { href } = linkEl; - tooltipEl.innerHTML = `${href}`; - const button = document.createElement('button'); + const { href } = linkEl + tooltipEl.innerHTML = `${href}` + const button = document.createElement('button') button.classList.add('__mobiledoc-tooltip__edit-link') - button.innerText = 'Edit Link'; - button.addEventListener('click', editLink); - tooltipEl.append(button); - } -}; + button.innerText = 'Edit Link' + button.addEventListener('click', editLink) + tooltipEl.append(button) + }, +} diff --git a/src/js/views/view.js b/src/js/views/view.js index a356095b1..1e85a613d 100644 --- a/src/js/views/view.js +++ b/src/js/views/view.js @@ -1,51 +1,51 @@ -import { addClassName } from '../utils/dom-utils'; +import { addClassName } from '../utils/dom-utils' class View { - constructor(options={}) { - options.tagName = options.tagName || 'div'; - options.container = options.container || document.body; + 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; + 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 = []; + 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]); + element.addEventListener(type, listener) + this._eventListeners.push([element, type, listener]) } removeAllEventListeners() { this._eventListeners.forEach(([element, type, listener]) => { - element.removeEventListener(type, listener); - }); + element.removeEventListener(type, listener) + }) } show() { - if(!this.isShowing) { - this.container.appendChild(this.element); - this.isShowing = true; - return true; + 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; + this.container.removeChild(this.element) + this.isShowing = false + return true } } destroy() { - this.removeAllEventListeners(); - this.hide(); - this.isDestroyed = true; + this.removeAllEventListeners() + this.hide() + this.isDestroyed = true } } -export default View; +export default View diff --git a/yarn.lock b/yarn.lock index 1aebcc8fd..b573366d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2571,6 +2571,11 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= +prettier@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" + integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg== + printf@^0.5.1: version "0.5.2" resolved "https://registry.yarnpkg.com/printf/-/printf-0.5.2.tgz#8546e01a1f647b1dff510ae92bdc92beb8c9b2f9" From 2ecfd5c74b36f49b80dd110b09f812491c3c40e0 Mon Sep 17 00:00:00 2001 From: Zahra Jabini Date: Wed, 27 May 2020 10:01:08 -0400 Subject: [PATCH 2/3] =?UTF-8?q?Adds=20eslint=20+=20fixes=20linting=20error?= =?UTF-8?q?s=20=F0=9F=A7=9E=E2=80=8D=E2=99=82=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.js | 629 ++++++++++++++++++-------------- package.json | 5 +- src/js/models/_section.js | 1 + src/js/parsers/dom.js | 2 +- src/js/utils/cursor/position.js | 2 + src/js/utils/key.js | 2 + src/js/utils/merge.js | 1 + src/js/utils/parse-utils.js | 2 +- src/js/views/tooltip.js | 10 +- yarn.lock | 620 ++++++++++++++++++++++++++++++- 10 files changed, 972 insertions(+), 302 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index b4508e22f..d150fc2c3 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,286 +1,351 @@ module.exports = { - "env": { - "browser": true - }, - "overrides": [ - { - "files": ["rollup.config.js"], - "env": { + "env": { + "browser": true, + "es6": true, "node": true - } - } - ], - "extends": "eslint:recommended", - "parserOptions": { - "ecmaVersion": 2017, - "sourceType": "module" - }, - "root": true, - "rules": { - "accessor-pairs": "error", - "array-bracket-spacing": "off", - "array-callback-return": "error", - "arrow-body-style": "off", - "arrow-parens": "off", - "arrow-spacing": "off", - "block-scoped-var": "error", - "block-spacing": [ - "error", - "always" - ], - "brace-style": [ - "error", - "1tbs", - { - "allowSingleLine": true - } - ], - "callback-return": "off", - "camelcase": "off", - "capitalized-comments": "off", - "class-methods-use-this": "off", - "comma-dangle": "off", - "comma-spacing": "off", - "comma-style": [ - "error", - "last" - ], - "complexity": "error", - "computed-property-spacing": [ - "error", - "never" - ], - "consistent-return": "off", - "consistent-this": "off", - "curly": "error", - "default-case": "off", - "dot-location": [ - "error", - "property" - ], - "dot-notation": [ - "error", - { - "allowKeywords": true - } - ], - "eol-last": "off", - "eqeqeq": "error", - "func-call-spacing": "error", - "func-name-matching": "error", - "func-names": [ - "error", - "never" - ], - "func-style": "off", - "generator-star-spacing": "error", - "global-require": "error", - "guard-for-in": "error", - "handle-callback-err": "error", - "id-blacklist": "error", - "id-length": "off", - "id-match": "error", - "indent": "off", - "init-declarations": "off", - "jsx-quotes": "error", - "key-spacing": "off", - "keyword-spacing": "off", - "line-comment-position": "off", - "linebreak-style": [ - "error", - "unix" - ], - "lines-around-comment": "off", - "lines-around-directive": "error", - "max-depth": "off", - "max-len": "off", - "max-lines": "off", - "max-nested-callbacks": "error", - "max-params": "off", - "max-statements": "off", - "max-statements-per-line": "off", - "multiline-ternary": "off", - "new-cap": "error", - "new-parens": "error", - "newline-after-var": "off", - "newline-before-return": "off", - "newline-per-chained-call": "error", - "no-alert": "off", - "no-array-constructor": "error", - "no-await-in-loop": "error", - "no-bitwise": "off", - "no-caller": "error", - "no-catch-shadow": "error", - "no-compare-neg-zero": "error", - "no-confusing-arrow": "error", - "no-continue": "off", - "no-div-regex": "error", - "no-duplicate-imports": "off", - "no-else-return": "off", - "no-empty-function": "off", - "no-eq-null": "error", - "no-eval": "error", - "no-extend-native": "error", - "no-extra-bind": "error", - "no-extra-label": "error", - "no-extra-parens": "off", - "no-floating-decimal": "error", - "no-implicit-coercion": [ - "error", - { - "boolean": false, - "number": false, - "string": false - } + }, + "extends": [ + "eslint:recommended", + "prettier" ], - "no-implicit-globals": "error", - "no-implied-eval": "error", - "no-inline-comments": "off", - "no-inner-declarations": [ - "error", - "functions" - ], - "no-invalid-this": "error", - "no-iterator": "error", - "no-label-var": "error", - "no-labels": "error", - "no-lone-blocks": "error", - "no-lonely-if": "off", - "no-loop-func": "error", - "no-magic-numbers": "off", - "no-mixed-operators": "off", - "no-mixed-requires": "error", - "no-multi-assign": "off", - "no-multi-spaces": "off", - "no-multi-str": "error", - "no-multiple-empty-lines": "error", - "no-native-reassign": "error", - "no-negated-condition": "off", - "no-negated-in-lhs": "error", - "no-nested-ternary": "off", - "no-new": "error", - "no-new-func": "error", - "no-new-object": "error", - "no-new-require": "error", - "no-new-wrappers": "error", - "no-octal-escape": "error", - "no-param-reassign": "off", - "no-path-concat": "error", - "no-plusplus": "off", - "no-process-exit": "error", - "no-proto": "error", - "no-prototype-builtins": "off", - "no-restricted-globals": "error", - "no-restricted-imports": "error", - "no-restricted-modules": "error", - "no-restricted-properties": "error", - "no-restricted-syntax": "error", - "no-return-assign": "off", - "no-return-await": "error", - "no-script-url": "error", - "no-self-compare": "error", - "no-sequences": "error", - "no-shadow": "off", - "no-shadow-restricted-names": "error", - "no-spaced-func": "error", - "no-sync": "error", - "no-tabs": "error", - "no-template-curly-in-string": "off", - "no-ternary": "off", - "no-throw-literal": "error", - "no-trailing-spaces": "off", - "no-undef-init": "error", - "no-undefined": "off", - "no-underscore-dangle": "off", - "no-unmodified-loop-condition": "off", - "no-unneeded-ternary": "off", - "no-unused-expressions": "error", - "no-use-before-define": ["error", { "functions": false, "classes": false }], - "no-useless-call": "off", - "no-useless-computed-key": "error", - "no-useless-concat": "error", - "no-useless-constructor": "error", - "no-useless-escape": "off", - "no-useless-rename": "error", - "no-useless-return": "off", - "no-var": "off", - "no-void": "error", - "no-warning-comments": "off", - "no-whitespace-before-property": "error", - "no-with": "error", - "nonblock-statement-body-position": "error", - "object-curly-newline": "off", - "object-curly-spacing": "off", - "object-property-newline": "off", - "object-shorthand": "off", - "one-var": "off", - "one-var-declaration-per-line": "off", - "operator-assignment": "off", - "operator-linebreak": "off", - "padded-blocks": "off", - "prefer-arrow-callback": "off", - "prefer-const": "off", - "prefer-destructuring": [ - "error", - { - "array": false, - "object": false - } - ], - "prefer-numeric-literals": "error", - "prefer-promise-reject-errors": "error", - "prefer-reflect": "off", - "prefer-rest-params": "off", - "prefer-spread": "off", - "prefer-template": "off", - "quote-props": "off", - "quotes": "off", - "radix": "error", - "require-await": "error", - "require-jsdoc": "off", - "rest-spread-spacing": [ - "error", - "never" - ], - "semi": "error", - "semi-spacing": [ - "error", - { - "after": true, - "before": false - } - ], - "sort-imports": "off", - "sort-keys": "off", - "sort-vars": "off", - "space-before-blocks": "off", - "space-before-function-paren": "off", - "space-in-parens": [ - "error", - "never" - ], - "space-infix-ops": "off", - "space-unary-ops": "error", - "spaced-comment": "off", - "strict": "error", - "symbol-description": "error", - "template-curly-spacing": [ - "error", - "never" - ], - "template-tag-spacing": "error", - "unicode-bom": [ - "error", - "never" - ], - "valid-jsdoc": "off", - "vars-on-top": "off", - "wrap-iife": "error", - "wrap-regex": "off", - "yield-star-spacing": "error", - "yoda": [ - "error", - "never" - ] - } + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parserOptions": { + "ecmaVersion": 11, + "sourceType": "module" + }, + "rules": { + "accessor-pairs": "error", + "array-bracket-newline": "error", + "array-bracket-spacing": [ + "error", + "never" + ], + "array-callback-return": "error", + "array-element-newline": "off", + "arrow-body-style": "off", + "arrow-parens": [ + "error", + "as-needed" + ], + "arrow-spacing": [ + "error", + { + "after": true, + "before": true + } + ], + "block-scoped-var": "error", + "block-spacing": "error", + "brace-style": [ + "error", + "1tbs" + ], + "callback-return": "off", + "camelcase": "off", + "capitalized-comments": "off", + "class-methods-use-this": "off", + "comma-dangle": "off", + "comma-spacing": [ + "error", + { + "after": true, + "before": false + } + ], + "comma-style": [ + "error", + "last" + ], + "complexity": "error", + "computed-property-spacing": [ + "error", + "never" + ], + "consistent-return": "off", + "consistent-this": "off", + "curly": "error", + "default-case": "off", + "default-case-last": "error", + "default-param-last": "error", + "dot-location": "error", + "dot-notation": [ + "error", + { + "allowKeywords": true + } + ], + "eol-last": "error", + "eqeqeq": "error", + "func-call-spacing": "error", + "func-name-matching": "error", + "func-names": "off", + "func-style": "off", + "function-call-argument-newline": [ + "error", + "consistent" + ], + "function-paren-newline": "off", + "generator-star-spacing": "error", + "global-require": "error", + "grouped-accessor-pairs": [ + "error", + "anyOrder" + ], + "guard-for-in": "error", + "handle-callback-err": "error", + "id-blacklist": "error", + "id-length": "off", + "id-match": "error", + "implicit-arrow-linebreak": "off", + "indent": "off", + "indent-legacy": "off", + "init-declarations": "off", + "jsx-quotes": "error", + "key-spacing": "error", + "keyword-spacing": [ + "error", + { + "after": true, + "before": true + } + ], + "line-comment-position": "off", + "linebreak-style": [ + "error", + "unix" + ], + "lines-around-comment": "off", + "lines-around-directive": "error", + "lines-between-class-members": "off", + "max-classes-per-file": "off", + "max-depth": "off", + "max-len": "off", + "max-lines": "off", + "max-lines-per-function": "off", + "max-nested-callbacks": "error", + "max-params": "off", + "max-statements": "off", + "max-statements-per-line": "error", + "multiline-comment-style": "off", + "new-cap": "error", + "new-parens": "error", + "newline-after-var": "off", + "newline-before-return": "off", + "newline-per-chained-call": "error", + "no-alert": "off", + "no-array-constructor": "error", + "no-await-in-loop": "error", + "no-bitwise": "off", + "no-buffer-constructor": "error", + "no-caller": "error", + "no-catch-shadow": "error", + "no-confusing-arrow": "error", + "no-console": "error", + "no-constructor-return": "error", + "no-continue": "off", + "no-div-regex": "error", + "no-duplicate-imports": "off", + "no-else-return": "off", + "no-empty-function": "off", + "no-eq-null": "error", + "no-eval": "error", + "no-extend-native": "error", + "no-extra-bind": "error", + "no-extra-boolean-cast": [ + "error", + { + "enforceForLogicalOperands": false + } + ], + "no-extra-label": "error", + "no-extra-parens": "off", + "no-floating-decimal": "error", + "no-implicit-coercion": [ + "error", + { + "boolean": false, + "number": false, + "string": false + } + ], + "no-implicit-globals": "error", + "no-implied-eval": "error", + "no-inline-comments": "off", + "no-inner-declarations": [ + "error", + "functions" + ], + "no-invalid-this": [ + "error", + { + "capIsConstructor": true + } + ], + "no-iterator": "error", + "no-label-var": "error", + "no-labels": "error", + "no-lone-blocks": "error", + "no-lonely-if": "off", + "no-loop-func": "error", + "no-loss-of-precision": "error", + "no-magic-numbers": "off", + "no-mixed-operators": "off", + "no-mixed-requires": "error", + "no-multi-assign": "off", + "no-multi-spaces": "error", + "no-multi-str": "error", + "no-multiple-empty-lines": "error", + "no-native-reassign": "error", + "no-negated-condition": "off", + "no-negated-in-lhs": "error", + "no-nested-ternary": "off", + "no-new": "error", + "no-new-func": "error", + "no-new-object": "error", + "no-new-require": "error", + "no-new-wrappers": "error", + "no-octal-escape": "error", + "no-param-reassign": "off", + "no-path-concat": "error", + "no-plusplus": "off", + "no-process-env": "error", + "no-process-exit": "error", + "no-proto": "error", + "no-redeclare": [ + "error", + { + "builtinGlobals": false + } + ], + "no-restricted-exports": "error", + "no-restricted-globals": "error", + "no-restricted-imports": "error", + "no-restricted-modules": "error", + "no-restricted-properties": "error", + "no-restricted-syntax": "error", + "no-return-assign": "off", + "no-return-await": "error", + "no-script-url": "error", + "no-self-compare": "error", + "no-sequences": "error", + "no-shadow": "off", + "no-spaced-func": "error", + "no-sync": "error", + "no-tabs": "error", + "no-template-curly-in-string": "off", + "no-ternary": "off", + "no-throw-literal": "error", + "no-trailing-spaces": "error", + "no-undef-init": "error", + "no-undefined": "off", + "no-underscore-dangle": "off", + "no-unmodified-loop-condition": "off", + "no-unneeded-ternary": "off", + "no-unused-expressions": "error", + "no-use-before-define": "error", + "no-useless-backreference": "error", + "no-useless-call": "off", + "no-useless-computed-key": "error", + "no-useless-concat": "off", + "no-useless-constructor": "error", + "no-useless-rename": "error", + "no-useless-return": "off", + "no-var": "off", + "no-void": "error", + "no-warning-comments": "off", + "no-whitespace-before-property": "error", + "nonblock-statement-body-position": "error", + "object-curly-newline": "error", + "object-curly-spacing": [ + "error", + "always" + ], + "object-shorthand": "off", + "one-var": "off", + "one-var-declaration-per-line": [ + "error", + "initializations" + ], + "operator-assignment": "off", + "operator-linebreak": "error", + "padded-blocks": "off", + "padding-line-between-statements": "error", + "prefer-arrow-callback": "off", + "prefer-const": "off", + "prefer-destructuring": "off", + "prefer-exponentiation-operator": "error", + "prefer-named-capture-group": "off", + "prefer-numeric-literals": "error", + "prefer-object-spread": "error", + "prefer-promise-reject-errors": "error", + "prefer-reflect": "off", + "prefer-regex-literals": "error", + "prefer-rest-params": "off", + "prefer-spread": "off", + "prefer-template": "off", + "quote-props": "off", + "quotes": "off", + "radix": "error", + "require-atomic-updates": "error", + "require-await": "error", + "require-jsdoc": "off", + "require-unicode-regexp": "off", + "rest-spread-spacing": [ + "error", + "never" + ], + "semi": "off", + "semi-spacing": [ + "error", + { + "after": true, + "before": false + } + ], + "semi-style": [ + "error", + "first" + ], + "sort-keys": "off", + "sort-vars": "off", + "space-before-blocks": "error", + "space-before-function-paren": "off", + "space-in-parens": [ + "error", + "never" + ], + "space-infix-ops": "error", + "space-unary-ops": "error", + "spaced-comment": "off", + "strict": "error", + "switch-colon-spacing": [ + "error", + { + "after": true, + "before": false + } + ], + "symbol-description": "error", + "template-curly-spacing": [ + "error", + "never" + ], + "template-tag-spacing": "error", + "unicode-bom": [ + "error", + "never" + ], + "valid-jsdoc": "off", + "vars-on-top": "off", + "wrap-iife": "error", + "wrap-regex": "off", + "yield-star-spacing": "error", + "yoda": [ + "error", + "never" + ] + } }; diff --git a/package.json b/package.json index ee7b40a37..7501ee840 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,8 @@ "scripts": { "start": "rollup -c --watch", "test:ci": "yarn build:docs && yarn build && testem ci -f testem-ci.js", - "test": "yarn format && yarn build && testem ci -f testem.js", + "test": "yarn lint && yarn build && testem ci -f testem.js", + "lint": "prettier src --check && eslint src", "format": "prettier src --write", "build": "rollup -c", "build:docs": "jsdoc -c ./.jsdoc", @@ -51,6 +52,8 @@ "@rollup/plugin-commonjs": "^11.1.0", "@rollup/plugin-node-resolve": "^7.1.3", "conventional-changelog-cli": "^2.0.34", + "eslint": "^7.1.0", + "eslint-config-prettier": "^6.11.0", "jquery": "^3.5.1", "jsdoc": "^3.6.4", "prettier": "^2.0.5", diff --git a/src/js/models/_section.js b/src/js/models/_section.js index 439be88f8..54342aad5 100644 --- a/src/js/models/_section.js +++ b/src/js/models/_section.js @@ -37,6 +37,7 @@ export default class Section extends LinkedItem { return 0 } + // eslint-disable-next-line getter-return get isBlank() { unimplementedMethod('isBlank', this) } diff --git a/src/js/parsers/dom.js b/src/js/parsers/dom.js index d20318c37..a37a56401 100644 --- a/src/js/parsers/dom.js +++ b/src/js/parsers/dom.js @@ -8,7 +8,7 @@ import { ZWNJ } from 'mobiledoc-kit/renderers/editor-dom' import SectionParser from 'mobiledoc-kit/parsers/section' import Markup from 'mobiledoc-kit/models/markup' -const GOOGLE_DOCS_CONTAINER_ID_REGEX = /^docs\-internal\-guid/ +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') diff --git a/src/js/utils/cursor/position.js b/src/js/utils/cursor/position.js index bff47ac76..57fa07435 100644 --- a/src/js/utils/cursor/position.js +++ b/src/js/utils/cursor/position.js @@ -10,6 +10,7 @@ const { FORWARD, BACKWARD } = DIRECTION // generated via http://xregexp.com/ to cover chars that \w misses // (new XRegExp('\\p{Alphabetic}|[0-9]|_|:')).toString() +// eslint-disable-next-line no-misleading-character-class const WORD_CHAR_REGEX = /[A-Za-zªµºÀ-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮͅͰ-ʹͶͷͺ-ͽͿΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁҊ-ԯԱ-Ֆՙա-ևְ-ׇֽֿׁׂׅׄא-תװ-ײؐ-ؚؠ-ٗٙ-ٟٮ-ۓە-ۜۡ-ۭۨ-ۯۺ-ۼۿܐ-ܿݍ-ޱߊ-ߪߴߵߺࠀ-ࠗࠚ-ࠬࡀ-ࡘࢠ-ࢴࣣ-ࣰࣩ-ऻऽ-ौॎ-ॐॕ-ॣॱ-ঃঅ-ঌএঐও-নপ-রলশ-হঽ-ৄেৈোৌৎৗড়ঢ়য়-ৣৰৱਁ-ਃਅ-ਊਏਐਓ-ਨਪ-ਰਲਲ਼ਵਸ਼ਸਹਾ-ੂੇੈੋੌੑਖ਼-ੜਫ਼ੰ-ੵઁ-ઃઅ-ઍએ-ઑઓ-નપ-રલળવ-હઽ-ૅે-ૉોૌૐૠ-ૣૹଁ-ଃଅ-ଌଏଐଓ-ନପ-ରଲଳଵ-ହଽ-ୄେୈୋୌୖୗଡ଼ଢ଼ୟ-ୣୱஂஃஅ-ஊஎ-ஐஒ-கஙசஜஞடணதந-பம-ஹா-ூெ-ைொ-ௌௐௗఀ-ఃఅ-ఌఎ-ఐఒ-నప-హఽ-ౄె-ైొ-ౌౕౖౘ-ౚౠ-ౣಁ-ಃಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹಽ-ೄೆ-ೈೊ-ೌೕೖೞೠ-ೣೱೲഁ-ഃഅ-ഌഎ-ഐഒ-ഺഽ-ൄെ-ൈൊ-ൌൎൗൟ-ൣൺ-ൿංඃඅ-ඖක-නඳ-රලව-ෆා-ුූෘ-ෟෲෳก-ฺเ-ๆํກຂຄງຈຊຍດ-ທນ-ຟມ-ຣລວສຫອ-ູົ-ຽເ-ໄໆໍໜ-ໟༀཀ-ཇཉ-ཬཱ-ཱྀྈ-ྗྙ-ྼက-ံးျ-ဿၐ-ၢၥ-ၨၮ-ႆႎႜႝႠ-ჅჇჍა-ჺჼ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚ፟ᎀ-ᎏᎠ-Ᏽᏸ-ᏽᐁ-ᙬᙯ-ᙿᚁ-ᚚᚠ-ᛪᛮ-ᛸᜀ-ᜌᜎ-ᜓᜠ-ᜳᝀ-ᝓᝠ-ᝬᝮ-ᝰᝲᝳក-ឳា-ៈៗៜᠠ-ᡷᢀ-ᢪᢰ-ᣵᤀ-ᤞᤠ-ᤫᤰ-ᤸᥐ-ᥭᥰ-ᥴᦀ-ᦫᦰ-ᧉᨀ-ᨛᨠ-ᩞᩡ-ᩴᪧᬀ-ᬳᬵ-ᭃᭅ-ᭋᮀ-ᮩᮬ-ᮯᮺ-ᯥᯧ-ᯱᰀ-ᰵᱍ-ᱏᱚ-ᱽᳩ-ᳬᳮ-ᳳᳵᳶᴀ-ᶿᷧ-ᷴḀ-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼⁱⁿₐ-ₜℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎⅠ-ↈⒶ-ⓩⰀ-Ⱞⰰ-ⱞⱠ-ⳤⳫ-ⳮⳲⳳⴀ-ⴥⴧⴭⴰ-ⵧⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞⷠ-ⷿⸯ々-〇〡-〩〱-〵〸-〼ぁ-ゖゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎㆠ-ㆺㇰ-ㇿ㐀-䶵一-鿕ꀀ-ꒌꓐ-ꓽꔀ-ꘌꘐ-ꘟꘪꘫꙀ-ꙮꙴ-ꙻꙿ-ꛯꜗ-ꜟꜢ-ꞈꞋ-ꞭꞰ-ꞷꟷ-ꠁꠃ-ꠅꠇ-ꠊꠌ-ꠧꡀ-ꡳꢀ-ꣃꣲ-ꣷꣻꣽꤊ-ꤪꤰ-ꥒꥠ-ꥼꦀ-ꦲꦴ-ꦿꧏꧠ-ꧤꧦ-ꧯꧺ-ꧾꨀ-ꨶꩀ-ꩍꩠ-ꩶꩺꩾ-ꪾꫀꫂꫛ-ꫝꫠ-ꫯꫲ-ꫵꬁ-ꬆꬉ-ꬎꬑ-ꬖꬠ-ꬦꬨ-ꬮꬰ-ꭚꭜ-ꭥꭰ-ꯪ가-힣ힰ-ퟆퟋ-ퟻ豈-舘並-龎ff-stﬓ-ﬗיִ-ﬨשׁ-זּטּ-לּמּנּסּףּפּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻﹰ-ﹴﹶ-ﻼA-Za-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ]|[0-9]|_|:/ function findParentSectionFromNode(renderTree, node) { @@ -436,6 +437,7 @@ BlankPosition = class BlankPosition extends Position { toRange() { return Range.blankRange() } + // eslint-disable-next-line getter-return get leafSectionIndex() { assert('must implement get leafSectionIndex', false) } diff --git a/src/js/utils/key.js b/src/js/utils/key.js index cd8b80e31..a5fcaf3fe 100644 --- a/src/js/utils/key.js +++ b/src/js/utils/key.js @@ -225,6 +225,8 @@ const Key = class Key { return this.isForwardDelete() ? DIRECTION.FORWARD : DIRECTION.BACKWARD case this.isHorizontalArrow(): return this.isRightArrow() ? DIRECTION.FORWARD : DIRECTION.BACKWARD + default: + return DIRECTION.FORWARD } } diff --git a/src/js/utils/merge.js b/src/js/utils/merge.js index 1c3ef103c..a3a8d661c 100644 --- a/src/js/utils/merge.js +++ b/src/js/utils/merge.js @@ -1,3 +1,4 @@ +/* eslint-disable no-prototype-builtins */ function mergeWithOptions(original, updates, options) { options = options || {} for (var prop in updates) { diff --git a/src/js/utils/parse-utils.js b/src/js/utils/parse-utils.js index a2ccd9d32..dde21b1df 100644 --- a/src/js/utils/parse-utils.js +++ b/src/js/utils/parse-utils.js @@ -7,7 +7,7 @@ export const MIME_TEXT_PLAIN = 'text/plain' export const MIME_TEXT_HTML = 'text/html' export const NONSTANDARD_IE_TEXT_TYPE = 'Text' -const MOBILEDOC_REGEX = new RegExp(/data\-mobiledoc='(.*?)'>/) +const MOBILEDOC_REGEX = new RegExp(/data-mobiledoc='(.*?)'>/) /** * @return {Post} diff --git a/src/js/views/tooltip.js b/src/js/views/tooltip.js index 944a90bef..a1d77c509 100644 --- a/src/js/views/tooltip.js +++ b/src/js/views/tooltip.js @@ -44,16 +44,16 @@ export default class Tooltip extends View { }, HIDE_DELAY) } - this.addEventListener(tooltipElement, 'mouseenter', e => { + this.addEventListener(tooltipElement, 'mouseenter', () => { clearTimeout(hideTimeout) }) - this.addEventListener(tooltipElement, 'mouseleave', e => { + this.addEventListener(tooltipElement, 'mouseleave', () => { scheduleHide() }) - this.addEventListener(rootElement, 'mouseover', e => { - let target = getEventTargetMatchingTag(options.showForTag, e.target, rootElement) + this.addEventListener(rootElement, 'mouseover', event => { + let target = getEventTargetMatchingTag(options.showForTag, event.target, rootElement) if (target && target.isContentEditable) { clearTimeout(hideTimeout) @@ -63,7 +63,7 @@ export default class Tooltip extends View { } }) - this.addEventListener(rootElement, 'mouseout', e => { + this.addEventListener(rootElement, 'mouseout', () => { clearTimeout(showTimeout) if (this.elementObserver) { this.elementObserver.cancel() diff --git a/yarn.lock b/yarn.lock index b573366d8..a5d1c2821 100644 --- a/yarn.lock +++ b/yarn.lock @@ -89,6 +89,11 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@types/color-name@^1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" + integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== + "@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" @@ -158,6 +163,16 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" +acorn-jsx@^5.2.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe" + integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ== + +acorn@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.2.0.tgz#17ea7e40d7c8640ff54a694c889c26f31704effe" + integrity sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ== + add-stream@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/add-stream/-/add-stream-1.0.0.tgz#6a7990437ca736d5e1288db92bd3266d5f5cb2aa" @@ -180,6 +195,16 @@ agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" +ajv@^6.10.0, ajv@^6.10.2: + version "6.12.2" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.2.tgz#c629c5eced17baf314437918d2da88c99d5958cd" + integrity sha512-k+V+hzjm5q/Mr8ef/1Y9goCmlsK4I6Sm74teeyGvFk1XrOsbsKLjEdrvny42CZ+a8sXbk8KWpY/bDwS+FLL2UQ== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + ajv@^6.5.5: version "6.11.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9" @@ -190,6 +215,13 @@ ajv@^6.5.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ansi-escapes@^4.2.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -200,13 +232,31 @@ ansi-regex@^3.0.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg= -ansi-styles@^3.2.1: +ansi-regex@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" + integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== + +ansi-regex@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" + integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== + +ansi-styles@^3.2.0, 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" +ansi-styles@^4.1.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" + integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== + dependencies: + "@types/color-name" "^1.1.1" + color-convert "^2.0.1" + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -303,6 +353,11 @@ 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= +astral-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" + integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== + async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" @@ -462,6 +517,11 @@ callsite@1.0.0: resolved "https://registry.yarnpkg.com/callsite/-/callsite-1.0.0.tgz#280398e5d664bd74038b6f0905153e6e8af1bc20" integrity sha1-KAOY5dZkvXQDi28JBRU+borxvCA= +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + camelcase-keys@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" @@ -529,6 +589,27 @@ chalk@^2.0.0: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.0.0.tgz#6e98081ed2d17faab615eb52ac66ec1fe6209e72" + integrity sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + charm@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/charm/-/charm-1.0.2.tgz#8add367153a6d9a581331052c4090991da995e35" @@ -536,6 +617,18 @@ charm@^1.0.0: dependencies: inherits "^2.0.1" +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-width@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" + integrity sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw== + 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" @@ -548,11 +641,23 @@ color-convert@^1.9.0: dependencies: color-name "1.1.3" +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + 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= +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + colorette@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.1.0.tgz#1f943e5a357fac10b4e0f5aaef3b14cdc1af6ec7" @@ -866,6 +971,15 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^7.0.2: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -906,6 +1020,13 @@ debug@^3.0.0, debug@^3.1.0: dependencies: ms "^2.1.1" +debug@^4.0.1, debug@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -913,13 +1034,6 @@ debug@~3.1.0: dependencies: ms "2.0.0" -debug@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - decamelize-keys@^1.0.0, decamelize-keys@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz#d171a87933252807eb3cb61dc1c1445d078df2d9" @@ -933,6 +1047,11 @@ decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +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= + delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -960,6 +1079,13 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + dot-prop@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-3.0.0.tgz#1b708af094a49c9a0e7dbcad790aba539dac1177" @@ -980,6 +1106,16 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= +emoji-regex@^7.0.1: + version "7.0.3" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" + integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -1071,11 +1207,113 @@ escape-string-regexp@^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-config-prettier@^6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1" + integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA== + dependencies: + get-stdin "^6.0.0" + +eslint-scope@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" + integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== + dependencies: + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-utils@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.0.0.tgz#7be1cc70f27a72a76cd14aa698bcabed6890e1cd" + integrity sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA== + dependencies: + eslint-visitor-keys "^1.1.0" + +eslint-visitor-keys@^1.1.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@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.1.0.tgz#d9a1df25e5b7859b0a3d86bb05f0940ab676a851" + integrity sha512-DfS3b8iHMK5z/YLSme8K5cge168I8j8o1uiVmFCgnnjxZQbCGyraF8bMl7Ju4yfBmCuxD7shOF7eqGkcuIHfsA== + dependencies: + "@babel/code-frame" "^7.0.0" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.0.1" + doctrine "^3.0.0" + eslint-scope "^5.0.0" + eslint-utils "^2.0.0" + eslint-visitor-keys "^1.1.0" + espree "^7.0.0" + esquery "^1.2.0" + esutils "^2.0.2" + file-entry-cache "^5.0.1" + functional-red-black-tree "^1.0.1" + glob-parent "^5.0.0" + globals "^12.1.0" + ignore "^4.0.6" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + inquirer "^7.0.0" + is-glob "^4.0.0" + js-yaml "^3.13.1" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash "^4.17.14" + minimatch "^3.0.4" + natural-compare "^1.4.0" + optionator "^0.9.1" + progress "^2.0.0" + regexpp "^3.1.0" + semver "^7.2.1" + strip-ansi "^6.0.0" + strip-json-comments "^3.1.0" + table "^5.2.3" + text-table "^0.2.0" + v8-compile-cache "^2.0.3" + +espree@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.0.0.tgz#8a7a60f218e69f120a842dc24c5a88aa7748a74e" + integrity sha512-/r2XEx5Mw4pgKdyb7GNLQNsu++asx/dltf/CI8RFi9oGHxmQFgvLbc5Op4U6i8Oaj+kdslhJtVlEZeAqH5qOTw== + dependencies: + acorn "^7.1.1" + acorn-jsx "^5.2.0" + eslint-visitor-keys "^1.1.0" + 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== +esquery@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" + integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== + dependencies: + estraverse "^5.1.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.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== + +estraverse@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.1.0.tgz#374309d39fd935ae500e7b92e8a6b4c720e59642" + integrity sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw== + estree-walker@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-0.6.1.tgz#53049143f40c6eb918b23671d1fe3219f3a1b362" @@ -1086,6 +1324,11 @@ 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.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" @@ -1155,6 +1398,15 @@ extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -1187,6 +1439,11 @@ fast-json-stable-stringify@^2.0.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= + fastq@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2" @@ -1194,6 +1451,20 @@ fastq@^1.6.0: dependencies: reusify "^1.0.0" +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + +file-entry-cache@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" + integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== + dependencies: + flat-cache "^2.0.1" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -1248,6 +1519,20 @@ fireworm@^0.7.0: lodash.flatten "^3.0.2" minimatch "^3.0.2" +flat-cache@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" + integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== + dependencies: + flatted "^2.0.0" + rimraf "2.6.3" + write "1.0.3" + +flatted@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" + integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== + follow-redirects@^1.0.0: version "1.10.0" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.10.0.tgz#01f5263aee921c6a54fb91667f08f4155ce169eb" @@ -1303,6 +1588,11 @@ fsevents@~2.1.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== +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" @@ -1333,6 +1623,11 @@ get-stdin@^4.0.1: resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= +get-stdin@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" + integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g== + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -1381,6 +1676,13 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" +glob-parent@^5.0.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" + integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== + dependencies: + is-glob "^4.0.1" + glob-parent@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" @@ -1400,6 +1702,13 @@ glob@^7.0.0, 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" +globals@^12.1.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-12.4.0.tgz#a18813576a41b00a24a97e7f815918c2e19925f8" + integrity sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg== + dependencies: + type-fest "^0.8.1" + globby@10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/globby/-/globby-10.0.1.tgz#4782c34cb75dd683351335c5829cc3420e606b22" @@ -1471,6 +1780,11 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + has-unicode@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -1529,7 +1843,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.24, 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== @@ -1541,11 +1855,29 @@ ieee754@^1.1.4: resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== +ignore@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" + integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg== + ignore@^5.1.1: version "5.1.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz#84b7b3dbe64552b6ef0eca99f6743dbec6d97adf" integrity sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A== +import-fresh@^3.0.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.1.tgz#633ff618506e793af5ac91bf48b72677e15cbe66" + integrity sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= + indent-string@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" @@ -1591,6 +1923,25 @@ ini@^1.3.2: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" integrity sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== +inquirer@^7.0.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.1.0.tgz#1298a01859883e17c7264b82870ae1034f92dd29" + integrity sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== + dependencies: + ansi-escapes "^4.2.1" + chalk "^3.0.0" + cli-cursor "^3.1.0" + cli-width "^2.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.15" + mute-stream "0.0.8" + run-async "^2.4.0" + rxjs "^6.5.3" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + interpret@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" @@ -1630,7 +1981,12 @@ 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@^4.0.1: +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, 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== @@ -1745,6 +2101,14 @@ js-tokens@^4.0.0: resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== +js-yaml@^3.13.1: + version "3.14.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" + integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + 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" @@ -1800,6 +2164,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-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" @@ -1846,6 +2215,14 @@ lazystream@^1.0.0: dependencies: readable-stream "^2.0.5" +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" @@ -2173,6 +2550,11 @@ mime@>=2.0.3: resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5" integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA== +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + min-indent@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.0.tgz#cfc45c37e9ec0d8f0a0ec3dd4ef7f7c3abe39256" @@ -2272,6 +2654,16 @@ mustache@^3.0.0: resolved "https://registry.yarnpkg.com/mustache/-/mustache-3.2.1.tgz#89e78a9d207d78f2799b1e95764a25bf71a28322" integrity sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA== +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + +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" @@ -2374,11 +2766,30 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" +onetime@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5" + integrity sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q== + dependencies: + mimic-fn "^2.1.0" + opener@1: version "1.5.1" resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.1.tgz#6d2f0e77f1a0af0032aca716c2c1fbb8e7e8abed" integrity sha512-goYSy5c2UXE4Ra1xixabeVh1guIX/ZV/YokJksb6q2lubWu6UbvPQ20p542/sFIll1nl8JnCyK9oBaOcCWXwvA== +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -2427,6 +2838,13 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + parse-github-repo-url@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/parse-github-repo-url/-/parse-github-repo-url-1.4.1.tgz#9e7d8bb252a6cb6ba42595060b7bf6df3dbc1f50" @@ -2503,6 +2921,11 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" @@ -2571,6 +2994,11 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + prettier@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4" @@ -2586,6 +3014,11 @@ process-nextick-args@~2.0.0: 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== + proxy-addr@~2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34" @@ -2783,6 +3216,11 @@ redent@^3.0.0: indent-string "^4.0.0" strip-indent "^3.0.0" +regexpp@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" + integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== + repeating@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" @@ -2828,6 +3266,11 @@ requizzle@^0.2.3: dependencies: lodash "^4.17.14" +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + resolve@1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.9.0.tgz#a14c6fdfa8f92a7df1d996cb7105fa744658ea06" @@ -2856,11 +3299,26 @@ resolve@^1.14.2: dependencies: path-parse "^1.0.6" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + 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.6.3: + version "2.6.3" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" + integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== + dependencies: + glob "^7.1.3" + rimraf@^2.4.4, rimraf@^2.5.4: version "2.7.1" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" @@ -2909,11 +3367,23 @@ rollup@^2.10.3: optionalDependencies: fsevents "~2.1.2" +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + 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== +rxjs@^6.5.3: + version "6.5.5" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.5.5.tgz#c5c884e3094c8cfee31bf27eb87e54ccfc87f9ec" + integrity sha512-WfQI+1gohdf0Dai/Bbmk5L5ItH5tYqm3ki2c5GdWhKjalzjg93N3avFjVStyZZz+A2Em+ZxKH5bNghw9UeylGQ== + dependencies: + tslib "^1.9.0" + safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -2961,6 +3431,11 @@ semver@^6.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^7.2.1: + version "7.3.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" + integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== + send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" @@ -3007,11 +3482,23 @@ shebang-command@^1.2.0: dependencies: shebang-regex "^1.0.0" +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + shebang-regex@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + shelljs@^0.8.3: version "0.8.4" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" @@ -3031,11 +3518,25 @@ signal-exit@^3.0.0: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= +signal-exit@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" + integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== + 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@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" + integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== + dependencies: + ansi-styles "^3.2.0" + astral-regex "^1.0.0" + is-fullwidth-code-point "^2.0.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" @@ -3188,6 +3689,24 @@ string-width@^1.0.1: is-fullwidth-code-point "^2.0.0" strip-ansi "^4.0.0" +string-width@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" + integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== + dependencies: + emoji-regex "^7.0.1" + is-fullwidth-code-point "^2.0.0" + strip-ansi "^5.1.0" + +string-width@^4.1.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.0" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -3216,6 +3735,20 @@ strip-ansi@^4.0.0: dependencies: ansi-regex "^3.0.0" +strip-ansi@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" + integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== + dependencies: + ansi-regex "^4.1.0" + +strip-ansi@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" + integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== + dependencies: + ansi-regex "^5.0.0" + strip-bom@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" @@ -3269,6 +3802,23 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +supports-color@^7.1.0: + version "7.1.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1" + integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g== + dependencies: + has-flag "^4.0.0" + +table@^5.2.3: + version "5.4.6" + resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" + integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== + dependencies: + ajv "^6.10.2" + lodash "^4.17.14" + slice-ansi "^2.1.0" + string-width "^3.0.0" + taffydb@2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/taffydb/-/taffydb-2.6.2.tgz#7cbcb64b5a141b6a2efc2c5d2c67b4e150b2a268" @@ -3347,6 +3897,11 @@ 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= + through2@^2.0.0, through2@^2.0.2: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" @@ -3362,12 +3917,12 @@ through2@^3.0.0: dependencies: readable-stream "2 || 3" -through@2, "through@>=2.2.7 <3": +through@2, "through@>=2.2.7 <3", through@^2.3.6: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= -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== @@ -3419,6 +3974,11 @@ 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= +tslib@^1.9.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" + integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" @@ -3431,6 +3991,18 @@ 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.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + type-fest@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" @@ -3509,6 +4081,11 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +v8-compile-cache@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" + integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== + validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -3556,6 +4133,13 @@ which@^1.2.9, which@^1.3.0: dependencies: isexe "^2.0.0" +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + wide-align@^1.1.0: version "1.1.3" resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.3.tgz#ae074e6bdc0c14a431e804e624549c633b000457" @@ -3563,6 +4147,11 @@ wide-align@^1.1.0: dependencies: string-width "^1.0.2 || 2" +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@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" @@ -3573,6 +4162,13 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= +write@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" + integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== + dependencies: + mkdirp "^0.5.1" + ws@^7.1.2: version "7.2.1" resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.1.tgz#03ed52423cd744084b2cf42ed197c8b65a936b8e" From bfcd8cf33eeb935fcebc6145903fe107c0522a28 Mon Sep 17 00:00:00 2001 From: Zahra Jabini Date: Fri, 29 May 2020 09:02:20 -0400 Subject: [PATCH 3/3] =?UTF-8?q?Update=20ReadME=20=F0=9F=92=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2ff18ff1f..962eb1d70 100644 --- a/README.md +++ b/README.md @@ -448,7 +448,7 @@ editor.render(element); Fork the repo, write a test, make a change, open a PR. -### Tests +### Tests, Linting, Formatting Install dependencies via yarn: @@ -467,6 +467,14 @@ Or run headless tests via testem: Tests in CI are run at Travis via Saucelabs (see the `test:ci` yarn script). +Run linter + +* `yarn lint` + +Run formatting + +* `yarn format` + ### Demo To run the demo site locally: