diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 000000000..3bc2f9ed4 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,128 @@ +{ + "env": { + "browser": true, + "node": true + }, + "parser": "babel-eslint", + "parserOptions": { + "ecmaFeatures": { + "jsx": true + } + }, + "plugins": [ + "import", + "react" + ], + "rules": { + "block-spacing": "error", + "comma-dangle": ["error", "only-multiline"], + "comma-spacing": ["error", { "before": false, "after": true }], + "comma-style": ["error", "last"], + "computed-property-spacing": ["error", "never"], + "constructor-super": "error", + "curly": ["error", "multi-line"], + "dot-location": ["error", "property"], + "dot-notation": ["error", { "allowKeywords": true }], + "eol-last": "error", + "func-style": ["error", "declaration"], + "import/default": "error", + "import/export": "error", + "import/named": "error", + "import/namespace": "error", + "import/newline-after-import": "error", + "import/no-deprecated": "error", + "import/no-extraneous-dependencies": "error", + "import/no-mutable-exports": "error", + "import/no-named-as-default": "error", + "import/no-named-as-default-member": "error", + "import/no-unresolved": "error", + "key-spacing": ["error", { "beforeColon": false, "afterColon": true }], + "lines-around-comment": ["error", { "beforeBlockComment": true, "afterBlockComment": true }], + "new-parens": "error", + "no-array-constructor": "error", + "no-class-assign": "error", + "no-console": "warn", + "no-const-assign": "error", + "no-debugger": "warn", + "no-dupe-args": "error", + "no-dupe-class-members": "error", + "no-dupe-keys": "error", + "no-duplicate-case": "error", + "no-empty": "error", + "no-empty-character-class": "error", + "no-empty-pattern": "error", + "no-ex-assign": "error", + "no-extend-native": "error", + "no-func-assign": "error", + "no-invalid-regexp": "error", + "no-mixed-spaces-and-tabs": ["error", false], + "no-multi-spaces": "error", + "no-multiple-empty-lines": ["error", { "max": 2, "maxEOF": 1 }], + "no-native-reassign": "error", + "no-negated-in-lhs": "error", + "no-new-object": "error", + "no-new-symbol": "error", + "no-redeclare": "error", + "no-regex-spaces": "error", + "no-sequences": "error", + "no-shadow": "error", + "no-shadow-restricted-names": "error", + "no-spaced-func": "error", + "no-throw-literal": "error", + "no-trailing-spaces": "error", + "no-undef": "error", + "no-unreachable": "error", + "no-unsafe-finally": "error", + "no-unused-expressions": "error", + "no-useless-call": "error", + "no-useless-computed-key": "error", + "no-useless-constructor": "error", + "no-useless-rename": "error", + "no-var": "error", + "no-void": "error", + "no-whitespace-before-property": "error", + "no-with": "error", + "object-property-newline": ["error", { "allowMultiplePropertiesPerLine": true }], + "object-shorthand": ["error", "always"], + "padded-blocks": ["error", "never"], + "prefer-arrow-callback": "error", + "prefer-rest-params": "error", + "prefer-spread": "error", + "prefer-template": "error", + "radix": "error", + "react/jsx-boolean-value": ["error", "never"], + "react/jsx-closing-bracket-location": "error", + "react/jsx-curly-spacing": ["error", "never"], + "react/jsx-equals-spacing": "error", + "react/jsx-first-prop-new-line": ["error", "multiline"], + "react/jsx-key": "error", + "react/jsx-no-bind": "error", + "react/jsx-no-duplicate-props": "error", + "react/jsx-no-undef": "error", + "react/jsx-space-before-closing": ["error", "always"], + "react/no-deprecated": "error", + "react/no-did-mount-set-state": "error", + "react/no-did-update-set-state": "error", + "react/no-string-refs": "error", + "react/no-unknown-property": "error", + "react/react-in-jsx-scope": "error", + "react/self-closing-comp": "error", + "react/sort-prop-types": "error", + "react/wrap-multilines": "error", + "rest-spread-spacing": ["error", "never"], + "semi": ["error", "never"], + "space-before-blocks": "error", + "space-before-function-paren": ["error", { "anonymous": "always", "named": "never" }], + "space-in-parens": "error", + "space-infix-ops": "error", + "space-unary-ops": ["error", { "words": true, "nonwords": false }], + "spaced-comment": ["error", "always", { "exceptions": ["-"]}], + "template-curly-spacing": "error", + "unicode-bom": ["error", "never"], + "use-isnan": "error", + "valid-jsdoc": ["error", { "prefer": { "return": "returns" }, "requireReturn": false }], + "valid-typeof": "error", + "yield-star-spacing": ["error", "after"], + "yoda": ["error", "never"] + } +} diff --git a/Makefile b/Makefile index 192b85e0d..fbd01525c 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ bin = ./node_modules/.bin babel = $(bin)/babel browserify = $(bin)/browserify exorcist = $(bin)/exorcist -standard = $(bin)/standard +eslint = $(bin)/eslint mocha = $(bin)/mocha mocha-phantomjs = $(bin)/mocha-phantomjs node = node @@ -20,6 +20,9 @@ ifeq ($(DEBUG),true) node += debug endif +# Run all of the checks. +check: lint test + # Remove the generated files. clean: @ rm -rf ./dist @@ -46,7 +49,7 @@ install: # Lint the sources files with Standard JS. lint: - @ $(standard) ./lib + @ $(eslint) "lib/**/*.js" # Build the test source. test/browser/support/build.js: $(shell find ./lib) ./test/browser.js @@ -62,8 +65,6 @@ test: test-browser test-server test-browser: ./test/support/build.js @ $(mocha-phantomjs) \ --reporter spec \ - --timeout 5000 \ - --fgrep "$(GREP)" \ ./test/support/browser.html # Run the server-side tests. @@ -72,7 +73,6 @@ test-server: --compilers js:babel-core/register \ --require source-map-support/register \ --reporter spec \ - --timeout 5000 \ --fgrep "$(GREP)" \ ./test/server.js diff --git a/lib/components/content.js b/lib/components/content.js index cc0b09091..4a524d91d 100644 --- a/lib/components/content.js +++ b/lib/components/content.js @@ -48,7 +48,7 @@ class Content extends React.Component { * @return {Boolean} shouldUpdate */ - shouldComponentUpdate(props, state) { + shouldComponentUpdate = (props, state) => { if (props.state.isNative) return false return ( props.state.selection != this.props.state.selection || @@ -63,21 +63,21 @@ class Content extends React.Component { * @param {Object} props */ - componentWillMount() { + componentWillMount = () => { this.tmp.isRendering = true } - componentWillUpdate(props, state) { + componentWillUpdate = (props, state) => { this.tmp.isRendering = true } - componentDidMount() { + componentDidMount = () => { setTimeout(() => { this.tmp.isRendering = false }) } - componentDidUpdate(props, state) { + componentDidUpdate = (props, state) => { setTimeout(() => { this.tmp.isRendering = false }) @@ -89,7 +89,7 @@ class Content extends React.Component { * @param {Event} e */ - onBeforeInput(e) { + onBeforeInput = (e) => { this.props.onBeforeInput(e) } @@ -99,7 +99,7 @@ class Content extends React.Component { * @param {Event} e */ - onBlur(e) { + onBlur = (e) => { if (this.tmp.isCopying) return let { state } = this.props @@ -117,7 +117,7 @@ class Content extends React.Component { * @param {State} state */ - onChange(state) { + onChange = (state) => { this.props.onChange(state) } @@ -127,7 +127,7 @@ class Content extends React.Component { * @param {Event} e */ - onCopy(e) { + onCopy = (e) => { this.onCutCopy(e) } @@ -137,7 +137,7 @@ class Content extends React.Component { * @param {Event} e */ - onCut(e) { + onCut = (e) => { this.onCutCopy(e) // Once the cut has successfully executed, delete the current selection. @@ -154,7 +154,7 @@ class Content extends React.Component { * @param {Event} e */ - onCutCopy(e) { + onCutCopy = (e) => { const native = window.getSelection() if (!native.rangeCount) return @@ -207,7 +207,7 @@ class Content extends React.Component { * @param {Event} e */ - onKeyDown(e) { + onKeyDown = (e) => { const key = keycode(e.which) if ( @@ -231,7 +231,7 @@ class Content extends React.Component { * @param {Event} e */ - onPaste(e) { + onPaste = (e) => { e.preventDefault() const data = e.clipboardData const { types } = data @@ -287,7 +287,7 @@ class Content extends React.Component { * @param {Event} e */ - onSelect(e) { + onSelect = (e) => { if (this.tmp.isRendering) return if (this.tmp.isCopying) return @@ -326,7 +326,7 @@ class Content extends React.Component { * @return {Element} element */ - render() { + render = () => { const { state } = this.props const { document } = state const children = document.nodes @@ -343,13 +343,13 @@ class Content extends React.Component {
this.onBeforeInput(e)} - onBlur={e => this.onBlur(e)} - onCopy={e => this.onCopy(e)} - onCut={e => this.onCut(e)} - onKeyDown={e => this.onKeyDown(e)} - onPaste={e => this.onPaste(e)} - onSelect={e => this.onSelect(e)} + onBeforeInput={this.onBeforeInput} + onBlur={this.onBlur} + onCopy={this.onCopy} + onCut={this.onCut} + onKeyDown={this.onKeyDown} + onPaste={this.onPaste} + onSelect={this.onSelect} > {children}
@@ -363,7 +363,7 @@ class Content extends React.Component { * @return {Element} element */ - renderNode(node) { + renderNode = (node) => { switch (node.kind) { case 'block': case 'inline': @@ -380,7 +380,7 @@ class Content extends React.Component { * @return {Element} element */ - renderElement(node) { + renderElement = (node) => { const { editor, renderNode, state } = this.props const Component = renderNode(node) const children = node.nodes @@ -411,7 +411,7 @@ class Content extends React.Component { * @return {Element} element */ - renderVoid(element, node) { + renderVoid = (element, node) => { const { editor, state } = this.props return ( { const { editor, renderMark, state } = this.props return ( { this.setState({ plugins: this.resolvePlugins(props) }) this.setState({ state: this.resolveState(props.state) }) } @@ -57,7 +57,7 @@ class Editor extends React.Component { * @return {State} state */ - getState() { + getState = () => { return this.state.state } @@ -67,7 +67,7 @@ class Editor extends React.Component { * @param {State} state */ - onChange(state) { + onChange = (state) => { if (state == this.state.state) return for (const plugin of this.state.plugins) { @@ -88,7 +88,7 @@ class Editor extends React.Component { * @param {Mixed} ...args */ - onEvent(name, ...args) { + onEvent = (name, ...args) => { for (const plugin of this.state.plugins) { if (!plugin[name]) continue const newState = plugin[name](...args, this.state.state, this) @@ -98,23 +98,53 @@ class Editor extends React.Component { } } + /** + * On before input. + * + * @param {Mixed} ...args + */ + + onBeforeInput = (...args) => { + this.onEvent('onBeforeInput', ...args) + } + + /** + * On key down. + * + * @param {Mixed} ...args + */ + + onKeyDown = (...args) => { + this.onEvent('onKeyDown', ...args) + } + + /** + * On paste. + * + * @param {Mixed} ...args + */ + + onPaste = (...args) => { + this.onEvent('onPaste', ...args) + } + /** * Render the editor. * * @return {Element} element */ - render() { + render = () => { return ( this.onChange(state)} - renderMark={mark => this.renderMark(mark)} - renderNode={node => this.renderNode(node)} - onPaste={(e, paste) => this.onEvent('onPaste', e, paste)} - onBeforeInput={e => this.onEvent('onBeforeInput', e)} - onKeyDown={e => this.onEvent('onKeyDown', e)} + onChange={this.onChange} + renderMark={this.renderMark} + renderNode={this.renderNode} + onPaste={this.onPaste} + onBeforeInput={this.onBeforeInput} + onKeyDown={this.onKeyDown} /> ) } @@ -126,7 +156,7 @@ class Editor extends React.Component { * @return {Element} element */ - renderNode(node) { + renderNode = (node) => { for (const plugin of this.state.plugins) { if (!plugin.renderNode) continue const component = plugin.renderNode(node, this.state.state, this) @@ -142,7 +172,7 @@ class Editor extends React.Component { * @return {Object} style */ - renderMark(mark) { + renderMark = (mark) => { for (const plugin of this.state.plugins) { if (!plugin.renderMark) continue const style = plugin.renderMark(mark, this.state.state, this) @@ -165,7 +195,7 @@ class Editor extends React.Component { * @return {Array} plugins */ - resolvePlugins(props) { + resolvePlugins = (props) => { const { onChange, plugins, ...editorPlugin } = props return [ editorPlugin, @@ -184,7 +214,7 @@ class Editor extends React.Component { * @return {State} state */ - resolveState(state) { + resolveState = (state) => { const { plugins } = this.state let { document } = state diff --git a/lib/components/leaf.js b/lib/components/leaf.js index 0b465a7e9..a44aaa3be 100644 --- a/lib/components/leaf.js +++ b/lib/components/leaf.js @@ -14,11 +14,11 @@ class Leaf extends React.Component { */ static propTypes = { + end: React.PropTypes.number.isRequired, marks: React.PropTypes.object.isRequired, node: React.PropTypes.object.isRequired, - start: React.PropTypes.number.isRequired, - end: React.PropTypes.number.isRequired, renderMark: React.PropTypes.func.isRequired, + start: React.PropTypes.number.isRequired, state: React.PropTypes.object.isRequired, text: React.PropTypes.string.isRequired }; @@ -122,9 +122,9 @@ class Leaf extends React.Component { end }) - const style = marks.reduce((style, mark) => { + const style = marks.reduce((memo, mark) => { return { - ...style, + ...memo, ...renderMark(mark), } }, {}) @@ -134,7 +134,7 @@ class Leaf extends React.Component { data-offset-key={offsetKey} style={style} > - {text ||
} + {text ||
} ) } diff --git a/lib/components/text.js b/lib/components/text.js index c84f50013..5b66f997d 100644 --- a/lib/components/text.js +++ b/lib/components/text.js @@ -62,10 +62,10 @@ class Text extends React.Component { const { characters, decorations } = node const ranges = groupByMarks(decorations || characters) - return ranges.map((range, i, ranges) => { - const previous = ranges.slice(0, i) + return ranges.map((range, i, original) => { + const previous = original.slice(0, i) const offset = previous.size - ? previous.map(range => range.text).join('').length + ? previous.map(r => r.text).join('').length : 0 return this.renderLeaf(range, offset) }) diff --git a/lib/components/void.js b/lib/components/void.js index 07be30a78..5def92d80 100644 --- a/lib/components/void.js +++ b/lib/components/void.js @@ -19,7 +19,7 @@ class Void extends React.Component { state: React.PropTypes.object.isRequired }; - render() { + render = () => { const { children, node } = this.props const Tag = node.kind == 'block' ? 'div' : 'span' const style = { @@ -34,7 +34,7 @@ class Void extends React.Component { ) } - renderSpacer() { + renderSpacer = () => { const style = { position: 'absolute', top: '0px', @@ -49,7 +49,7 @@ class Void extends React.Component { ) } - renderLeaf() { + renderLeaf = () => { const { node, state } = this.props const child = node.getTextNodes().first() const text = '' @@ -64,7 +64,8 @@ class Void extends React.Component { return ( this.leaf = el} + ref={this.renderLeafRefs} + renderMark={this.renderLeafMark} key={offsetKey} state={state} node={child} @@ -72,11 +73,18 @@ class Void extends React.Component { end={end} text={text} marks={marks} - renderMark={mark => {}} /> ) } + renderLeafMark = (mark) => { + return {} + } + + renderLeafRefs = (el) => { + this.leaf = el + } + } /** diff --git a/lib/models/block.js b/lib/models/block.js index a518c5750..89426814f 100644 --- a/lib/models/block.js +++ b/lib/models/block.js @@ -33,7 +33,7 @@ const DEFAULTS = { * Block. */ -class Block extends Record(DEFAULTS) { +class Block extends new Record(DEFAULTS) { /** * Create a new `Block` with `properties`. @@ -50,7 +50,7 @@ class Block extends Record(DEFAULTS) { properties.key = uid(4) properties.data = Data.create(properties.data) - properties.isVoid = !! properties.isVoid + properties.isVoid = !!properties.isVoid properties.nodes = Block.createList(properties.nodes) return new Block(properties).normalize() diff --git a/lib/models/document.js b/lib/models/document.js index 600105f01..d34deb6f6 100644 --- a/lib/models/document.js +++ b/lib/models/document.js @@ -26,7 +26,7 @@ const DEFAULTS = { * Document. */ -class Document extends Record(DEFAULTS) { +class Document extends new Record(DEFAULTS) { /** * Create a new `Document` with `properties`. diff --git a/lib/models/inline.js b/lib/models/inline.js index fee08ebbc..500f463ae 100644 --- a/lib/models/inline.js +++ b/lib/models/inline.js @@ -33,7 +33,7 @@ const DEFAULTS = { * Inline. */ -class Inline extends Record(DEFAULTS) { +class Inline extends new Record(DEFAULTS) { /** * Create a new `Inline` with `properties`. @@ -50,7 +50,7 @@ class Inline extends Record(DEFAULTS) { properties.key = uid(4) properties.data = Data.create(properties.data) - properties.isVoid = !! properties.isVoid + properties.isVoid = !!properties.isVoid properties.nodes = Inline.createList(properties.nodes) let inline = new Inline(properties) diff --git a/lib/models/mark.js b/lib/models/mark.js index 3a39ad081..60cc7d9c5 100644 --- a/lib/models/mark.js +++ b/lib/models/mark.js @@ -15,7 +15,7 @@ const DEFAULTS = { * Mark. */ -class Mark extends Record(DEFAULTS) { +class Mark extends new Record(DEFAULTS) { /** * Create a new `Mark` with `properties`. diff --git a/lib/models/node.js b/lib/models/node.js index 88c5fd952..26d13380c 100644 --- a/lib/models/node.js +++ b/lib/models/node.js @@ -301,8 +301,8 @@ const Node = { getDeepestBlocks() { const texts = this.getTextNodes() - const set = texts.reduce((set, text) => { - return set.add(this.getClosestBlock(text)) + const set = texts.reduce((memo, text) => { + return memo.add(this.getClosestBlock(text)) }, new OrderedSet()) return set.toList() @@ -500,7 +500,7 @@ const Node = { // Otherwise, get a set of the marks for each character in the range. return this .getCharactersAtRange(range) - .reduce((marks, char) => marks.union(char.marks), marks) + .reduce((memo, char) => memo.union(char.marks), marks) }, /** @@ -573,8 +573,8 @@ const Node = { // Calculate the offset of the nodes before the highest child. const child = this.getHighestChild(key) const offset = this.nodes - .takeUntil(node => node == child) - .reduce((offset, child) => offset + child.length, 0) + .takeUntil(n => n == child) + .reduce((memo, n) => memo + n.length, 0) // Recurse if need be. return this.hasChild(key) @@ -744,7 +744,7 @@ const Node = { hasChild(key) { key = normalizeKey(key) - return !! this.nodes.find(node => node.key == key) + return !!this.nodes.find(node => node.key == key) }, /** @@ -756,7 +756,7 @@ const Node = { hasDescendant(key) { key = normalizeKey(key) - return !! this.nodes.find((node) => { + return !!this.nodes.find((node) => { return node.kind == 'text' ? node.key == key : node.key == key || node.hasDescendant(key) @@ -829,9 +829,9 @@ const Node = { */ mapDescendants(iterator) { - const nodes = this.nodes.map((node, i, nodes) => { + const nodes = this.nodes.map((node, i, original) => { if (node.kind != 'text') node = node.mapDescendants(iterator) - return iterator(node, i, nodes) + return iterator(node, i, original) }) return this.merge({ nodes }) @@ -850,7 +850,6 @@ const Node = { const keys = [] node = node.mapDescendants((desc) => { - // That there are no duplicate keys. if (keys.includes(desc.key)) desc = desc.set('key', uid()) keys.push(desc.key) @@ -1026,7 +1025,7 @@ function isInRange(index, text, range) { * Transforms. */ -for (var key in Transforms) { +for (const key in Transforms) { Node[key] = Transforms[key] } diff --git a/lib/models/selection.js b/lib/models/selection.js index 178153236..7dd078d9b 100644 --- a/lib/models/selection.js +++ b/lib/models/selection.js @@ -18,7 +18,7 @@ const DEFAULTS = { * Selection. */ -class Selection extends Record(DEFAULTS) { +class Selection extends new Record(DEFAULTS) { /** * Create a new `Selection` with `properties`. @@ -50,8 +50,8 @@ class Selection extends Record(DEFAULTS) { get isCollapsed() { return ( - this.anchorKey === this.focusKey && - this.anchorOffset === this.focusOffset + this.anchorKey == this.focusKey && + this.anchorOffset == this.focusOffset ) } diff --git a/lib/models/state.js b/lib/models/state.js index 1b403fb3d..eeda0b8ec 100644 --- a/lib/models/state.js +++ b/lib/models/state.js @@ -1,4 +1,5 @@ + import Document from './document' import Selection from './selection' import Transform from './transform' @@ -29,7 +30,7 @@ const DEFAULTS = { * State. */ -class State extends Record(DEFAULTS) { +class State extends new Record(DEFAULTS) { /** * Create a new `State` with `properties`. @@ -465,38 +466,8 @@ class State extends Record(DEFAULTS) { document = document.insertFragmentAtRange(selection, fragment) // Determine what the selection should be after inserting. - // if (texts.size == 1) { - // after = selection - // .moveToStart() - // .moveForward(fragment.length) - // } - - // else if (!nextText) { - // const text = document.getTextNodes().last() - // after = selection - // .moveToStartOf(text) - // .moveForward(lastText.length) - // } - - // else if (nextBlock != startBlock || lastInline || startInline) { - // const text = document.getPreviousText(nextText) - // after = selection - // .moveToStartOf(text) - // .moveForward(lastText.length) - // } - - // else { - // const text = nextNextText - // ? document.getPreviousText(nextNextText) - // : document.getPreviousText(document.getTextNodes().last()) - - // after = selection - // .moveToStartOf(text) - // .moveForward(lastText.length) - // } - const keys = docTexts.map(text => text.key) - const text = document.getTextNodes().findLast(text => !keys.includes(text.key)) + const text = document.getTextNodes().findLast(n => !keys.includes(n.key)) after = text ? selection.moveToStartOf(text).moveForward(lastText.length) @@ -508,9 +479,6 @@ class State extends Record(DEFAULTS) { return state } - - - /** * Insert a `text` string at the current selection. * @@ -868,13 +836,9 @@ class State extends Record(DEFAULTS) { document = document.wrapInlineAtRange(selection, type, data) // Determine what the selection should be after wrapping. - if (selection.isCollapsed) { - selection = selection - } - - else { - const startText = document.getNextText(selection.startKey) - const endText = document.getPreviousText(selection.endKey) + if (selection.isExpanded) { + const start = document.getNextText(selection.startKey) + const end = document.getPreviousText(selection.endKey) selection = selection.moveToRangeOf(start, end) selection = selection.normalize(document) } diff --git a/lib/models/text.js b/lib/models/text.js index 4e5f0862c..82e03405b 100644 --- a/lib/models/text.js +++ b/lib/models/text.js @@ -19,7 +19,7 @@ const DEFAULTS = { * Text. */ -class Text extends Record(DEFAULTS) { +class Text extends new Record(DEFAULTS) { /** * Create a new `Text` with `properties`. diff --git a/lib/models/transform.js b/lib/models/transform.js index f0de04491..a1c0e164e 100644 --- a/lib/models/transform.js +++ b/lib/models/transform.js @@ -7,7 +7,7 @@ import { List, Record } from 'immutable' * Snapshot, with a state-like shape. */ -const Snapshot = Record({ +const Snapshot = new Record({ document: null, selection: null, steps: new List() @@ -17,7 +17,7 @@ const Snapshot = Record({ * Step. */ -const Step = Record({ +const Step = new Record({ type: null, args: null }) @@ -119,7 +119,7 @@ const DEFAULT_PROPERTIES = { * Transform. */ -class Transform extends Record(DEFAULT_PROPERTIES) { +class Transform extends new Record(DEFAULT_PROPERTIES) { /** * Create a history-ready snapshot of the current state. @@ -183,12 +183,12 @@ class Transform extends Record(DEFAULT_PROPERTIES) { } // Apply each of the steps in the transform, arriving at a new state. - state = steps.reduce((state, step) => this.applyStep(state, step), state) + state = steps.reduce((memo, step) => this.applyStep(memo, step), state) // Apply the "isNative" flag, which is used to allow for natively-handled // content changes to skip rerendering the editor for performance. state = state.merge({ - isNative: !! options.isNative + isNative: !!options.isNative }) return state diff --git a/lib/models/transforms.js b/lib/models/transforms.js index 3438a1cd0..b1e2cc595 100644 --- a/lib/models/transforms.js +++ b/lib/models/transforms.js @@ -190,7 +190,7 @@ const Transforms = { if (!fragment.length) return node // Make sure each node in the fragment has a unique key. - fragment = fragment.mapDescendants(node => node.set('key', uid())) + fragment = fragment.mapDescendants(child => child.set('key', uid())) // Split the inlines if need be. if (!node.isInlineSplitAtRange(range)) { @@ -414,8 +414,8 @@ const Transforms = { const { startKey } = range const firstText = node.getDescendant(startKey) const secondText = node.getNextText(startKey) - const firstChild = node.getFurthestInline(firstText) || firstText - const secondChild = node.getFurthestInline(secondText) || secondText + let firstChild = node.getFurthestInline(firstText) || firstText + let secondChild = node.getFurthestInline(secondText) || secondText let parent = node.getClosestBlock(firstChild) let firstChildren = parent.nodes.takeUntil(n => n == firstChild).push(firstChild) let secondChildren = parent.nodes.skipUntil(n => n == secondChild) @@ -423,8 +423,8 @@ const Transforms = { // While the parent is a block, split the block nodes. while (parent && d < depth) { - const firstChild = parent.merge({ nodes: firstChildren }) - const secondChild = Block.create({ + firstChild = parent.merge({ nodes: firstChildren }) + secondChild = Block.create({ nodes: secondChildren, type: parent.type, data: parent.data @@ -615,7 +615,7 @@ const Transforms = { // Find the closest wrapping blocks of each text node. const texts = node.getBlocksAtRange(range) - const wrappers = texts.reduce((wrappers, text) => { + const wrappers = texts.reduce((memo, text) => { const match = node.getClosest(text, (parent) => { if (parent.kind != 'block') return false if (type && parent.type != type) return false @@ -623,8 +623,8 @@ const Transforms = { return true }) - if (match) wrappers = wrappers.add(match) - return wrappers + if (match) memo = memo.add(match) + return memo }, new Set()) // Replace each of the wrappers with their child nodes. @@ -669,7 +669,7 @@ const Transforms = { // Find the closest matching inline wrappers of each text node. const texts = this.getTextNodes() - const wrappers = texts.reduce((wrappers, text) => { + const wrappers = texts.reduce((memo, text) => { const match = node.getClosest(text, (parent) => { if (parent.kind != 'inline') return false if (type && parent.type != type) return false @@ -677,8 +677,8 @@ const Transforms = { return true }) - if (match) wrappers = wrappers.add(match) - return wrappers + if (match) memo = memo.add(match) + return memo }, new Set()) // Replace each of the wrappers with their child nodes. @@ -718,19 +718,19 @@ const Transforms = { const da = node.getDepth(a) const db = node.getDepth(b) if (da == db) return 0 - if (da > db) return -1 - if (da < db) return 1 + else if (da > db) return -1 + else return 1 }) // Get the lowest common siblings, relative to the highest block. const highest = sorted.first() const depth = node.getDepth(highest) - const siblings = blocks.reduce((siblings, block) => { + const siblings = blocks.reduce((memo, block) => { const sibling = node.getDepth(block) == depth ? block : node.getClosest(block, (p) => node.getDepth(p) == depth) - siblings = siblings.push(sibling) - return siblings + memo = memo.push(sibling) + return memo }, Block.createList()) // Wrap the siblings in a new block. @@ -745,9 +745,9 @@ const Transforms = { const last = siblings.last() const parent = node.getParent(highest) const nodes = parent.nodes - .takeUntil(node => node == first) + .takeUntil(n => n == first) .push(wrapper) - .concat(parent.nodes.skipUntil(node => node == last).rest()) + .concat(parent.nodes.skipUntil(n => n == last).rest()) // Update the parent. node = parent == node diff --git a/lib/plugins/core.js b/lib/plugins/core.js index 252679a3a..af7687985 100644 --- a/lib/plugins/core.js +++ b/lib/plugins/core.js @@ -6,17 +6,25 @@ import { IS_WINDOWS, IS_MAC } from '../utils/environment' /** * Default block renderer. + * + * @param {Object} props + * @return {Element} element */ -const DEFAULT_BLOCK = props =>
{props.children}
+function DEFAULT_BLOCK(props) { + return
{props.children}
+} /** * Default inline renderer. * - * @type {Component} + * @param {Object} props + * @return {Element} element */ -const DEFAULT_INLINE = props => {props.children} +function DEFAULT_INLINE(props) { + return {props.children} +} /** * Default mark renderer. diff --git a/lib/serializers/html.js b/lib/serializers/html.js index 67e279c85..b2ab0cb7d 100644 --- a/lib/serializers/html.js +++ b/lib/serializers/html.js @@ -78,16 +78,16 @@ class Html { let nodes = this.deserializeElements(children) // HACK: ensure for now that all top-level inline are wrapping into a block. - nodes = nodes.reduce((nodes, node, i, original) => { + nodes = nodes.reduce((memo, node, i, original) => { if (node.kind == 'block') { - nodes.push(node) - return nodes + memo.push(node) + return memo } if (i > 0 && original[i - 1].kind != 'block') { - const block = nodes[nodes.length - 1] + const block = memo[memo.length - 1] block.nodes.push(node) - return nodes + return memo } const block = { @@ -96,8 +96,8 @@ class Html { nodes: [node] } - nodes.push(block) - return nodes + memo.push(block) + return memo }, []) const state = Raw.deserialize({ nodes }) @@ -151,9 +151,7 @@ class Html { : ret } - return node - ? node - : next(element.children) + return node || next(element.children) } /** diff --git a/lib/serializers/raw.js b/lib/serializers/raw.js index 688ff3e86..4e4bd822f 100644 --- a/lib/serializers/raw.js +++ b/lib/serializers/raw.js @@ -7,7 +7,6 @@ import Mark from '../models/mark' import State from '../models/state' import Text from '../models/text' import groupByMarks from '../utils/group-by-marks' -import { Map } from 'immutable' /** * Serialize a `state`. @@ -31,7 +30,7 @@ function serializeNode(node) { switch (node.kind) { case 'document': { return { - nodes: node.nodes.toArray().map(node => serializeNode(node)) + nodes: node.nodes.toArray().map(child => serializeNode(child)) } } case 'text': { @@ -45,11 +44,14 @@ function serializeNode(node) { const obj = {} obj.kind = node.kind obj.type = node.type - obj.nodes = node.nodes.toArray().map(node => serializeNode(node)) + obj.nodes = node.nodes.toArray().map(child => serializeNode(child)) if (node.isVoid) obj.isVoid = node.isVoid if (node.data.size) obj.data = node.data.toJSON() return obj } + default: { + throw new Error(`Unknown node kind "${node.kind}".`) + } } } @@ -130,6 +132,9 @@ function deserializeNode(object) { characters: deserializeRanges(object.ranges) }) } + default: { + throw new Error(`Unknown node kind "${object.kind}".`) + } } } @@ -145,7 +150,7 @@ function deserializeRanges(array) { const marks = object.marks || [] const chars = object.text .split('') - .map(char => { + .map((char) => { return Character.create({ text: char, marks: Mark.createSet(marks.map(deserializeMark)) diff --git a/lib/utils/environment.js b/lib/utils/environment.js index abc994f34..3c594a932 100644 --- a/lib/utils/environment.js +++ b/lib/utils/environment.js @@ -6,13 +6,13 @@ import Parser from 'ua-parser-js' * Export. */ -export const IS_ANDROID = process.browser && browser.name === 'android' -export const IS_CHROME = process.browser && browser.name === 'chrome' -export const IS_EDGE = process.browser && browser.name === 'edge' -export const IS_FIREFOX = process.browser && browser.name === 'firefox' -export const IS_IE = process.browser && browser.name === 'ie' -export const IS_IOS = process.browser && browser.name === 'ios' -export const IS_MAC = process.browser && new Parser().getOS().name === 'Mac OS' -export const IS_SAFARI = process.browser && browser.name === 'safari' -export const IS_UBUNTU = process.browser && new Parser().getOS().name === 'Ubuntu' +export const IS_ANDROID = process.browser && browser.name == 'android' +export const IS_CHROME = process.browser && browser.name == 'chrome' +export const IS_EDGE = process.browser && browser.name == 'edge' +export const IS_FIREFOX = process.browser && browser.name == 'firefox' +export const IS_IE = process.browser && browser.name == 'ie' +export const IS_IOS = process.browser && browser.name == 'ios' +export const IS_MAC = process.browser && new Parser().getOS().name == 'Mac OS' +export const IS_SAFARI = process.browser && browser.name == 'safari' +export const IS_UBUNTU = process.browser && new Parser().getOS().name == 'Ubuntu' export const IS_WINDOWS = process.browser && new Parser().getOS().name.includes('Windows') diff --git a/lib/utils/offset-key.js b/lib/utils/offset-key.js index 908a976d6..b2516731e 100644 --- a/lib/utils/offset-key.js +++ b/lib/utils/offset-key.js @@ -37,7 +37,7 @@ function findKey(node) { } // Shouldn't get here... else we have an edge case to handle. - console.error('No offset key found for node:', node) + throw new Error('No offset key found for node.') } /** diff --git a/package.json b/package.json index 3372f0bdf..89a42df6b 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,13 @@ "version": "0.1.0", "license": "MIT", "repository": "git://github.com/ianstormtaylor/slate.git", - "main": "./lib/index.js", + "main": "./dist/index.js", "scripts": { + "prepublish": "make dist", "test": "make test" }, "dependencies": { + "cheerio": "^0.20.0", "detect-browser": "^1.3.3", "immutable": "^3.8.1", "keycode": "^2.1.2", @@ -22,14 +24,17 @@ "devDependencies": { "babel-cli": "^6.10.1", "babel-core": "^6.9.1", + "babel-eslint": "^6.1.0", "babel-polyfill": "^6.9.1", "babel-preset-es2015": "^6.9.0", "babel-preset-react": "^6.5.0", "babel-preset-stage-0": "^6.5.0", "babelify": "^7.3.0", "browserify": "^13.0.1", - "cheerio": "^0.20.0", "component-type": "^1.2.1", + "eslint": "^3.0.1", + "eslint-plugin-import": "^1.10.2", + "eslint-plugin-react": "^5.2.2", "exorcist": "^0.4.0", "mocha": "^2.5.3", "mocha-phantomjs": "^4.0.2", @@ -41,11 +46,9 @@ "read-metadata": "^1.0.0", "selection-position": "^1.0.0", "source-map-support": "^0.4.0", - "standard": "^7.1.2", "to-camel-case": "^1.0.0", "to-title-case": "^1.0.0", - "watchify": "^3.7.0", - "xml2js": "^0.4.16" + "watchify": "^3.7.0" }, "keywords": [ "canvas",