diff --git a/Makefile b/Makefile index 26f6d91b8..4d1fbf0f3 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,7 @@ watchify = $(bin)/watchify # Flags. DEBUG ?= +GREP ?= # Config. ifeq ($(DEBUG),true) @@ -93,6 +94,7 @@ test-browser: ./test/support/build.js @ $(mocha-phantomjs) \ --reporter spec \ --timeout 5000 \ + --fgrep "$(GREP)" \ ./test/support/browser.html # Run the server-side tests. @@ -102,6 +104,7 @@ test-server: --require source-map-support/register \ --reporter spec \ --timeout 5000 \ + --fgrep "$(GREP)" \ ./test/server.js # Watch the auto-markdown example. diff --git a/examples/auto-markdown/index.js b/examples/auto-markdown/index.js index 562118d44..54dc8f98b 100644 --- a/examples/auto-markdown/index.js +++ b/examples/auto-markdown/index.js @@ -23,6 +23,29 @@ class App extends React.Component { state: Raw.deserialize(state) }; + /** + * Get the block type for a series of auto-markdown shortcut `chars`. + * + * @param {String} chars + * @return {String} block + */ + + getType(chars) { + switch (chars) { + case '*': + case '-': + case '+': return 'list-item' + case '>': return 'block-quote' + case '#': return 'heading-one' + case '##': return 'heading-two' + case '###': return 'heading-three' + case '####': return 'heading-four' + case '#####': return 'heading-five' + case '######': return 'heading-six' + default: return null + } + } + /** * * Render the example. @@ -120,57 +143,27 @@ class App extends React.Component { */ onSpace(e, state) { - if (state.isCurrentlyExpanded) return + if (state.isExpanded) return let { selection } = state - const { currentTextNodes, document } = state - const { startOffset } = selection - const node = currentTextNodes.first() - const { text } = node - const chars = text.slice(0, startOffset).replace(/\s*/g, '') - let transform = state.transform() - - switch (chars) { - case '#': - transform = transform.setType('heading-one') - break - case '##': - transform = transform.setType('heading-two') - break - case '###': - transform = transform.setType('heading-three') - break - case '####': - transform = transform.setType('heading-four') - break - case '#####': - transform = transform.setType('heading-five') - break - case '######': - transform = transform.setType('heading-six') - break - case '>': - transform = transform.setType('block-quote') - break - case '*': - case '-': - case '+': - if (node.type == 'list-item') return - transform = transform - .setType('list-item') - .wrapBlock('bulleted-list') - break - default: - return - } + const { startText, startBlock, startOffset } = state + const chars = startBlock.text.slice(0, startOffset).replace(/\s*/g, '') + const type = this.getType(chars) + if (!type) return + if (type == 'list-item' && startBlock.type == 'list-item') return e.preventDefault() + let transform = state + .transform() + .setBlock(type) + + if (type == 'list-item') transform = transform.wrapBlock('bulleted-list') + state = transform - .deleteAtRange(selection.extendBackwardToStartOf(node)) + .extendToStartOf(startBlock) + .delete() .apply() - selection = selection.moveToStartOf(node) - state = state.merge({ selection }) return state } @@ -184,18 +177,18 @@ class App extends React.Component { */ onBackspace(e, state) { - if (state.isCurrentlyExpanded) return - if (state.currentStartOffset != 0) return - const node = state.currentBlockNodes.first() - if (!node) debugger - if (node.type == 'paragraph') return + if (state.isExpanded) return + if (state.startOffset != 0) return + const { startBlock } = state + + if (startBlock.type == 'paragraph') return e.preventDefault() let transform = state .transform() - .setType('paragraph') + .setBlock('paragraph') - if (node.type == 'list-item') transform = transform.unwrapBlock('bulleted-list') + if (startBlock.type == 'list-item') transform = transform.unwrapBlock('bulleted-list') state = transform.apply() return state @@ -211,20 +204,19 @@ class App extends React.Component { */ onEnter(e, state) { - if (state.isCurrentlyExpanded) return - const node = state.currentBlockNodes.first() - if (!node) debugger - if (state.currentStartOffset == 0 && node.length == 0) return this.onBackspace(e, state) - if (state.currentEndOffset != node.length) return + if (state.isExpanded) return + const { startBlock, startOffset, endOffset } = state + if (startOffset == 0 && startBlock.length == 0) return this.onBackspace(e, state) + if (endOffset != startBlock.length) return if ( - node.type != 'heading-one' && - node.type != 'heading-two' && - node.type != 'heading-three' && - node.type != 'heading-four' && - node.type != 'heading-five' && - node.type != 'heading-six' && - node.type != 'block-quote' + startBlock.type != 'heading-one' && + startBlock.type != 'heading-two' && + startBlock.type != 'heading-three' && + startBlock.type != 'heading-four' && + startBlock.type != 'heading-five' && + startBlock.type != 'heading-six' && + startBlock.type != 'block-quote' ) { return } @@ -232,8 +224,8 @@ class App extends React.Component { e.preventDefault() return state .transform() - .split() - .setType('paragraph') + .splitBlock() + .setBlock('paragraph') .apply() } diff --git a/lib/models/node.js b/lib/models/node.js index 7df5ac09a..15afb406c 100644 --- a/lib/models/node.js +++ b/lib/models/node.js @@ -153,7 +153,8 @@ const Node = { if (range.isAtStartOf(startNode)) { const previous = node.getPreviousText(startNode) - range = range.extendBackwardToEndOf(previous) + range = range.extendToEndOf(previous) + range = range.normalize(node) node = node.deleteAtRange(range) return node } @@ -190,7 +191,8 @@ const Node = { if (range.isAtEndOf(startNode)) { const next = node.getNextText(startNode) - range = range.extendForwardToStartOf(next) + range = range.extendToStartOf(next) + range = range.normalize(node) node = node.deleteAtRange(range) return node } @@ -491,27 +493,19 @@ const Node = { key = normalizeKey(key) this.assertHasDescendant(key) - const match = this.getDescendant(key) - // Find the shallow matching child. - const child = this.nodes.find((node) => { - if (node == match) return true - return node.kind == 'text' - ? false - : node.hasDescendant(match) - }) + const isChild = this.hasChild(key) + const child = isChild + ? this.getChild(key) + : this.nodes.find(node => node.hasDescendant && node.hasDescendant(key)) - // Get all of the nodes that come before the matching child. - const befores = this.nodes.takeUntil(node => node.key == child.key) + // Calculate the offset of the nodes before the child. + const offset = this.nodes + .takeUntil(node => node == child) + .reduce((offset, child) => offset + child.length, 0) - // Calculate the offset of the nodes before the matching child. - const offset = befores.reduce((offset, child) => { - return offset + child.length - }, 0) - - // If the child's parent is this node, return the offset of all of the nodes - // before it, otherwise recurse. - return this.nodes.find(node => node.key == match.key) + // Recurse if need be. + return isChild ? offset : offset + child.getOffset(key) }, diff --git a/lib/models/selection.js b/lib/models/selection.js index ae8243515..82a68901d 100644 --- a/lib/models/selection.js +++ b/lib/models/selection.js @@ -10,7 +10,7 @@ const DEFAULTS = { anchorOffset: 0, focusKey: null, focusOffset: 0, - isBackward: false, + isBackward: null, isFocused: false } @@ -52,7 +52,7 @@ class Selection extends Record(DEFAULTS) { */ get isExpanded() { - return ! this.isCollapsed + return !this.isCollapsed } /** @@ -72,7 +72,7 @@ class Selection extends Record(DEFAULTS) { */ get isForward() { - return ! this.isBackward + return this.isBackward == null ? null : !this.isBackward } /** @@ -141,10 +141,10 @@ class Selection extends Record(DEFAULTS) { normalize(node) { let selection = this - let { anchorKey, anchorOffset, focusKey, focusOffset } = selection + let { anchorKey, anchorOffset, focusKey, focusOffset, isBackward } = selection // If the selection isn't formed yet, abort. - if (anchorKey == null || focusKey == null) return selection + if (this.isUnset) return this // Asset that the anchor and focus nodes exist in the node tree. node.assertHasDescendant(anchorKey) @@ -154,28 +154,37 @@ class Selection extends Record(DEFAULTS) { // If the anchor node isn't a text node, match it to one. if (anchorNode.kind != 'text') { - anchorNode = node.getTextAtOffset(anchorOffset) - let parent = node.getParent(anchorNode) - let offset = parent.getOffset(anchorNode) + let anchorText = anchorNode.getTextAtOffset(anchorOffset) + let offset = anchorNode.getOffset(anchorText) anchorOffset = anchorOffset - offset - anchorKey = anchorNode.key + anchorNode = anchorText } // If the focus node isn't a text node, match it to one. if (focusNode.kind != 'text') { - focusNode = node.getTextAtOffset(focusOffset) - let parent = node.getParent(focusNode) - let offset = parent.getOffset(focusNode) + let focusText = focusNode.getTextAtOffset(focusOffset) + let offset = focusNode.getOffset(focusText) focusOffset = focusOffset - offset - focusKey = focusNode.key + focusNode = focusText + } + + // If `isBackward` is not set, derive it. + if (isBackward == null) { + let texts = node.getTextNodes() + let anchorIndex = texts.indexOf(anchorNode) + let focusIndex = texts.indexOf(focusNode) + isBackward = anchorIndex == focusIndex + ? anchorOffset > focusOffset + : anchorIndex > focusIndex } // Merge in any updated properties. return selection.merge({ - anchorKey, + anchorKey: anchorNode.key, anchorOffset, - focusKey, - focusOffset + focusKey: focusNode.key, + focusOffset, + isBackward }) } @@ -303,10 +312,6 @@ class Selection extends Record(DEFAULTS) { */ moveForward(n = 1) { - if (!this.isCollapsed) { - throw new Error('The selection must be collapsed to move forward.') - } - return this.merge({ anchorOffset: this.anchorOffset + n, focusOffset: this.focusOffset + n @@ -321,10 +326,6 @@ class Selection extends Record(DEFAULTS) { */ moveBackward(n = 1) { - if (!this.isCollapsed) { - throw new Error('The selection must be collapsed to move backward.') - } - return this.merge({ anchorOffset: this.anchorOffset - n, focusOffset: this.focusOffset - n @@ -339,13 +340,9 @@ class Selection extends Record(DEFAULTS) { */ extendForward(n = 1) { - if (!this.isCollapsed) { - throw new Error('The selection must be collapsed before extending.') - } - return this.merge({ focusOffset: this.focusOffset + n, - isBackward: false + isBackward: null }) } @@ -357,89 +354,39 @@ class Selection extends Record(DEFAULTS) { */ extendBackward(n = 1) { - if (!this.isCollapsed) { - throw new Error('The selection must be collapsed before extending.') - } - return this.merge({ focusOffset: this.focusOffset - n, - isBackward: true + isBackward: null }) } /** - * Extend the focus forward to the start of a `node`. + * Extend the focus point to the start of a `node`. * * @param {Node} node * @return {Selection} selection */ - extendForwardToStartOf(node) { - if (!this.isCollapsed) { - throw new Error('The selection must be collapsed before extending.') - } - + extendToStartOf(node) { return this.merge({ focusKey: node.key, focusOffset: 0, - isBackward: false + isBackward: null }) } /** - * Extend the focus backward to the start of a `node`. + * Extend the focus point to the end of a `node`. * * @param {Node} node * @return {Selection} selection */ - extendBackwardToStartOf(node) { - if (!this.isCollapsed) { - throw new Error('The selection must be collapsed before extending.') - } - - return this.merge({ - focusKey: node.key, - focusOffset: 0, - isBackward: true - }) - } - - /** - * Extend the focus forward to the end of a `node`. - * - * @param {Node} node - * @return {Selection} selection - */ - - extendForwardToEndOf(node) { - if (!this.isCollapsed) { - throw new Error('The selection must be collapsed before extending.') - } - + extendToEndOf(node) { return this.merge({ focusKey: node.key, focusOffset: node.length, - isBackward: false - }) - } - - /** - * Extend the focus backward to the end of a `node`. - * - * @param {Node} node - * @return {Selection} selection - */ - - extendBackwardToEndOf(node) { - if (!this.isCollapsed) { - throw new Error('The selection must be collapsed before extending.') - } - - return this.merge({ - focusKey: node.key, - focusOffset: node.length, - isBackward: true + isBackward: null }) } diff --git a/lib/models/state.js b/lib/models/state.js index 7ddcb8514..7f4de0a33 100644 --- a/lib/models/state.js +++ b/lib/models/state.js @@ -24,27 +24,6 @@ const DEFAULTS = { isNative: true } -/** - * Node-like methods that should be mixed into the `State` prototype. - */ - -const NODE_LIKE_METHODS = [ - 'deleteAtRange', - 'deleteBackwardAtRange', - 'deleteForwardAtRange', - 'insertTextAtRange', - 'markAtRange', - 'setBlockAtRange', - 'setInlineAtRange', - 'splitBlockAtRange', - 'splitInlineAtRange', - 'unmarkAtRange', - 'unwrapBlockAtRange', - 'unwrapInlineAtRange', - 'wrapBlockAtRange', - 'wrapInlineAtRange' -] - /** * State. */ @@ -71,7 +50,7 @@ class State extends Record(DEFAULTS) { * @return {Boolean} isCollapsed */ - get isCurrentlyCollapsed() { + get isCollapsed() { return this.selection.isCollapsed } @@ -81,7 +60,7 @@ class State extends Record(DEFAULTS) { * @return {Boolean} isExpanded */ - get isCurrentlyExpanded() { + get isExpanded() { return this.selection.isExpanded } @@ -91,7 +70,7 @@ class State extends Record(DEFAULTS) { * @return {Boolean} isBackward */ - get isCurrentlyBackward() { + get isBackward() { return this.selection.isBackward } @@ -101,7 +80,7 @@ class State extends Record(DEFAULTS) { * @return {Boolean} isForward */ - get isCurrentlyForward() { + get isForward() { return this.selection.isForward } @@ -111,7 +90,7 @@ class State extends Record(DEFAULTS) { * @return {String} startKey */ - get currentStartKey() { + get startKey() { return this.selection.startKey } @@ -121,7 +100,7 @@ class State extends Record(DEFAULTS) { * @return {String} endKey */ - get currentEndKey() { + get endKey() { return this.selection.endKey } @@ -131,7 +110,7 @@ class State extends Record(DEFAULTS) { * @return {String} startOffset */ - get currentStartOffset() { + get startOffset() { return this.selection.startOffset } @@ -141,7 +120,7 @@ class State extends Record(DEFAULTS) { * @return {String} endOffset */ - get currentEndOffset() { + get endOffset() { return this.selection.endOffset } @@ -151,7 +130,7 @@ class State extends Record(DEFAULTS) { * @return {String} anchorKey */ - get currentAnchorKey() { + get anchorKey() { return this.selection.anchorKey } @@ -161,7 +140,7 @@ class State extends Record(DEFAULTS) { * @return {String} focusKey */ - get currentFocusKey() { + get focusKey() { return this.selection.focusKey } @@ -171,7 +150,7 @@ class State extends Record(DEFAULTS) { * @return {String} anchorOffset */ - get currentAnchorOffset() { + get anchorOffset() { return this.selection.anchorOffset } @@ -181,17 +160,97 @@ class State extends Record(DEFAULTS) { * @return {String} focusOffset */ - get currentFocusOffset() { + get focusOffset() { return this.selection.focusOffset } + /** + * Get the current start text node. + * + * @return {Text} text + */ + + get startText() { + return this.document.getDescendant(this.selection.startKey) + } + + /** + * Get the current end node. + * + * @return {Text} text + */ + + get endText() { + return this.document.getDescendant(this.selection.endKey) + } + + /** + * Get the current anchor node. + * + * @return {Text} text + */ + + get anchorText() { + return this.document.getDescendant(this.selection.anchorKey) + } + + /** + * Get the current focus node. + * + * @return {Text} text + */ + + get focusText() { + return this.document.getDescendant(this.selection.focusKey) + } + + /** + * Get the current start text node's closest block parent. + * + * @return {Block} block + */ + + get startBlock() { + return this.document.getClosestBlock(this.selection.startKey) + } + + /** + * Get the current end text node's closest block parent. + * + * @return {Block} block + */ + + get endBlock() { + return this.document.getClosestBlock(this.selection.endKey) + } + + /** + * Get the current anchor text node's closest block parent. + * + * @return {Block} block + */ + + get anchorBlock() { + return this.document.getClosestBlock(this.selection.anchorKey) + } + + /** + * Get the current focus text node's closest block parent. + * + * @return {Block} block + */ + + get focusBlock() { + return this.document.getClosestBlock(this.selection.focusKey) + } + /** * Get the characters in the current selection. * * @return {List} characters */ - get currentCharacters() { + get characters() { return this.document.getCharactersAtRange(this.selection) } @@ -201,7 +260,7 @@ class State extends Record(DEFAULTS) { * @return {Set} marks */ - get currentMarks() { + get marks() { return this.document.getMarksAtRange(this.selection) } @@ -211,7 +270,7 @@ class State extends Record(DEFAULTS) { * @return {OrderedMap} nodes */ - get currentBlocks() { + get blocks() { return this.document.getBlocksAtRange(this.selection) } @@ -221,7 +280,7 @@ class State extends Record(DEFAULTS) { * @return {OrderedMap} nodes */ - get currentInlines() { + get inlines() { return this.document.getInlinesAtRange(this.selection) } @@ -231,7 +290,7 @@ class State extends Record(DEFAULTS) { * @return {OrderedMap} nodes */ - get currentTexts() { + get texts() { return this.document.getTextsAtRange(this.selection) } @@ -292,7 +351,7 @@ class State extends Record(DEFAULTS) { else if (selection.isAtStartOf(startNode)) { const parent = document.getParent(startNode) - const previous = document.getPrevious(parent).nodes.first() + const previous = document.getPreviousSibling(parent).nodes.first() after = selection.moveToEndOf(previous) } @@ -395,26 +454,50 @@ class State extends Record(DEFAULTS) { } /** - * Split the node at the current selection. + * Split the block node at the current selection. * * @return {State} state */ - split() { + splitBlock() { let state = this let { document, selection } = state - let after // Split the document. - document = document.splitAtRange(selection) + document = document.splitBlockAtRange(selection) // Determine what the selection should be after splitting. const { startKey } = selection const startNode = document.getDescendant(startKey) - const parent = document.getParent(startNode) - const next = document.getNext(parent) - const text = next.nodes.first() - selection = selection.moveToStartOf(text) + const nextNode = document.getNextText(startNode) + selection = selection.moveToStartOf(nextNode) + + state = state.merge({ document, selection }) + return state + } + + /** + * Split the inline nodes at the current selection. + * + * @return {State} state + */ + + splitInline() { + let state = this + let { document, selection } = state + + // Split the document. + document = document.splitInlineAtRange(selection) + + // Determine what the selection should be after splitting. + const { startKey } = selection + const inlineParent = document.getClosestInline(startKey) + + if (inlineParent) { + const startNode = document.getDescendant(startKey) + const nextNode = document.getNextText(startNode) + selection = selection.moveToStartOf(nextNode) + } state = state.merge({ document, selection }) return state @@ -460,7 +543,6 @@ class State extends Record(DEFAULTS) { unwrapBlock(type) { let state = this let { document, selection } = state - selection = selection.normalize(document) document = document.unwrapBlockAtRange(selection, type) state = state.merge({ document, selection }) return state @@ -492,7 +574,6 @@ class State extends Record(DEFAULTS) { unwrapInline(type) { let state = this let { document, selection } = state - selection = selection.normalize(document) document = document.unwrapInlineAtRange(selection, type) state = state.merge({ document, selection }) return state @@ -500,18 +581,6 @@ class State extends Record(DEFAULTS) { } -/** - * Mix in node-like methods. - */ - -NODE_LIKE_METHODS.forEach((method) => { - State.prototype[method] = function (...args) { - let { document } = this - document = document[method](...args) - return this.merge({ document }) - } -}) - /** * Export. */ diff --git a/lib/models/transform.js b/lib/models/transform.js index 29e4f2b1d..347ac0d85 100644 --- a/lib/models/transform.js +++ b/lib/models/transform.js @@ -22,6 +22,77 @@ const Step = Record({ args: null }) +/** + * Document transforms. + */ + +const DOCUMENT_TRANSFORMS = [ + 'deleteAtRange', + 'deleteBackwardAtRange', + 'deleteForwardAtRange', + 'insertTextAtRange', + 'markAtRange', + 'setBlockAtRange', + 'setInlineAtRange', + 'splitBlockAtRange', + 'splitInlineAtRange', + 'unmarkAtRange', + 'unwrapBlockAtRange', + 'unwrapInlineAtRange', + 'wrapBlockAtRange', + 'wrapInlineAtRange' +] + +/** + * Selection transforms. + */ + +const SELECTION_TRANSFORMS = [ + 'moveToAnchor', + 'moveToFocus', + 'moveToStart', + 'moveToEnd', + 'moveToStartOf', + 'moveToEndOf', + 'moveToRangeOf', + 'moveForward', + 'moveBackward', + 'extendForward', + 'extendBackward', + 'extendToStartOf', + 'extendToEndOf' +] + +/** + * State transforms, that act on both the document and selection. + */ + +const STATE_TRANSFORMS = [ + 'delete', + 'deleteBackward', + 'deleteForward', + 'insertText', + 'mark', + 'setBlock', + 'setInline', + 'splitBlock', + 'splitInline', + 'unmark', + 'unwrapBlock', + 'unwrapInline', + 'wrapBlock', + 'wrapInline' +] + +/** + * All transforms. + */ + +const TRANSFORMS = [] + .concat(DOCUMENT_TRANSFORMS) + .concat(SELECTION_TRANSFORMS) + .concat(STATE_TRANSFORMS) + /** * Defaults. */ @@ -31,41 +102,6 @@ const DEFAULT_PROPERTIES = { steps: new List() } -/** - * Transform types. - */ - -const TRANSFORM_TYPES = [ - 'delete', - 'deleteAtRange', - 'deleteBackward', - 'deleteBackwardAtRange', - 'deleteForward', - 'deleteForwardAtRange', - 'insertText', - 'insertTextAtRange', - 'mark', - 'markAtRange', - 'setBlock', - 'setBlockAtRange', - 'setInline', - 'setInlineAtRange', - 'splitBlock', - 'splitBlockAtRange', - 'splitInline', - 'splitInlineAtRange', - 'unmark', - 'unmarkAtRange', - 'unwrapBlock', - 'unwrapBlockAtRange', - 'unwrapInline', - 'unwrapInlineAtRange', - 'wrapBlock', - 'wrapBlockAtRange', - 'wrapInline', - 'wrapInlineAtRange' -] - /** * Transform. */ @@ -134,10 +170,7 @@ class Transform extends Record(DEFAULT_PROPERTIES) { } // Apply each of the steps in the transform, arriving at a new state. - state = steps.reduce((state, step) => { - const { type, args } = step - return state[type](...args) - }, state) + state = steps.reduce((state, step) => this.applyStep(state, step), state) // Apply the "isNative" flag, which is used to allow for natively-handled // content changes to skip rerendering the editor for performance. @@ -148,6 +181,41 @@ class Transform extends Record(DEFAULT_PROPERTIES) { return state } + /** + * Apply a single `step` to a `state`, differentiating between types. + * + * @param {State} state + * @param {Step} step + * @return {State} state + */ + + applyStep(state, step) { + const { type, args } = step + + if (DOCUMENT_TRANSFORMS.includes(type)) { + let { document, selection } = state + let [ range, ...rest ] = args + range = range.normalize(document) + document = document[type](range, ...rest) + selection = selection.normalize(document) + state = state.merge({ document, selection }) + return state + } + + else if (SELECTION_TRANSFORMS.includes(type)) { + let { document, selection } = state + selection = selection[type](...args) + selection = selection.normalize(document) + state = state.merge({ selection }) + return state + } + + else if (STATE_TRANSFORMS.includes(type)) { + state = state[type](...args) + return state + } + } + /** * Undo to the previous state in the history. * @@ -211,10 +279,10 @@ class Transform extends Record(DEFAULT_PROPERTIES) { } /** - * Add a step-creating method for each transform type. + * Add a step-creating method for each of the transforms. */ -TRANSFORM_TYPES.forEach((type) => { +TRANSFORMS.forEach((type) => { Transform.prototype[type] = function (...args) { let transform = this let { steps } = transform diff --git a/lib/plugins/core.js b/lib/plugins/core.js index b9cea3ad3..70a59deee 100644 --- a/lib/plugins/core.js +++ b/lib/plugins/core.js @@ -1,7 +1,7 @@ import React from 'react' import keycode from 'keycode' -import environment from '../utils/environment' +import { IS_WINDOWS, IS_MAC } from '../utils/environment' /** * Export. @@ -20,14 +20,13 @@ export default { onKeyDown(e, state, editor) { const key = keycode(e.which) - const { IS_WINDOWS, IS_MAC } = environment() switch (key) { case 'enter': { e.preventDefault() return state .transform() - .split() + .splitBlock() .apply() } diff --git a/lib/utils/environment.js b/lib/utils/environment.js index c80182ff3..2dcb4175b 100644 --- a/lib/utils/environment.js +++ b/lib/utils/environment.js @@ -4,27 +4,25 @@ import Parser from 'ua-parser-js' /** * Read the environment. - * - * @return {Object} environment */ -function environment() { - return { - IS_ANDROID: browser.name === 'android', - IS_CHROME: browser.name === 'chrome', - IS_EDGE: browser.name === 'edge', - IS_FIREFOX: browser.name === 'firefox', - IS_IE: browser.name === 'ie', - IS_IOS: browser.name === 'ios', - IS_MAC: new Parser().getOS().name === 'Mac OS', - IS_UBUNTU: new Parser().getOS().name === 'Ubuntu', - IS_SAFARI: browser.name === 'safari', - IS_WINDOWS: new Parser().getOS().name.includes('Windows') - } -} +const ENVIRONMENT = process.browser + ? { + IS_ANDROID: browser.name === 'android', + IS_CHROME: browser.name === 'chrome', + IS_EDGE: browser.name === 'edge', + IS_FIREFOX: browser.name === 'firefox', + IS_IE: browser.name === 'ie', + IS_IOS: browser.name === 'ios', + IS_MAC: new Parser().getOS().name === 'Mac OS', + IS_UBUNTU: new Parser().getOS().name === 'Ubuntu', + IS_SAFARI: browser.name === 'safari', + IS_WINDOWS: new Parser().getOS().name.includes('Windows') + } + : {} /** * Export. */ -export default environment +export default ENVIRONMENT