1
0
mirror of https://github.com/ianstormtaylor/slate.git synced 2025-09-01 19:22:35 +02:00

add linting

This commit is contained in:
Ian Storm Taylor
2016-07-06 20:19:19 -07:00
parent b61f416e02
commit 226b6592dc
23 changed files with 321 additions and 178 deletions

128
.eslintrc Normal file
View File

@@ -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"]
}
}

View File

@@ -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

View File

@@ -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 {
<div
contentEditable suppressContentEditableWarning
style={style}
onBeforeInput={e => 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}
</div>
@@ -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 (
<Void
@@ -432,7 +432,7 @@ class Content extends React.Component {
* @return {Element} element
*/
renderText(node) {
renderText = (node) => {
const { editor, renderMark, state } = this.props
return (
<Text

View File

@@ -15,12 +15,12 @@ class Editor extends React.Component {
*/
static propTypes = {
onChange: React.PropTypes.func.isRequired,
plugins: React.PropTypes.array,
renderDecorations: React.PropTypes.func,
renderMark: React.PropTypes.func,
renderNode: React.PropTypes.func,
state: React.PropTypes.object.isRequired,
onChange: React.PropTypes.func.isRequired
};
static defaultProps = {
@@ -46,7 +46,7 @@ class Editor extends React.Component {
* @param {Object} props
*/
componentWillReceiveProps(props) {
componentWillReceiveProps = (props) => {
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 (
<Content
editor={this}
state={this.state.state}
onChange={state => 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

View File

@@ -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 || <br/>}
{text || <br />}
</span>
)
}

View File

@@ -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)
})

View File

@@ -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 (
<Leaf
ref={el => 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
}
}
/**

View File

@@ -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()

View File

@@ -26,7 +26,7 @@ const DEFAULTS = {
* Document.
*/
class Document extends Record(DEFAULTS) {
class Document extends new Record(DEFAULTS) {
/**
* Create a new `Document` with `properties`.

View File

@@ -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)

View File

@@ -15,7 +15,7 @@ const DEFAULTS = {
* Mark.
*/
class Mark extends Record(DEFAULTS) {
class Mark extends new Record(DEFAULTS) {
/**
* Create a new `Mark` with `properties`.

View File

@@ -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]
}

View File

@@ -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
)
}

View File

@@ -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)
}

View File

@@ -19,7 +19,7 @@ const DEFAULTS = {
* Text.
*/
class Text extends Record(DEFAULTS) {
class Text extends new Record(DEFAULTS) {
/**
* Create a new `Text` with `properties`.

View File

@@ -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

View File

@@ -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

View File

@@ -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 => <div>{props.children}</div>
function DEFAULT_BLOCK(props) {
return <div>{props.children}</div>
}
/**
* Default inline renderer.
*
* @type {Component}
* @param {Object} props
* @return {Element} element
*/
const DEFAULT_INLINE = props => <span>{props.children}</span>
function DEFAULT_INLINE(props) {
return <span>{props.children}</span>
}
/**
* Default mark renderer.

View File

@@ -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)
}
/**

View File

@@ -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))

View File

@@ -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')

View File

@@ -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.')
}
/**

View File

@@ -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",