mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-09-01 19:22:35 +02:00
add linting
This commit is contained in:
128
.eslintrc
Normal file
128
.eslintrc
Normal 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"]
|
||||
}
|
||||
}
|
10
Makefile
10
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
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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>
|
||||
)
|
||||
}
|
||||
|
@@ -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)
|
||||
})
|
||||
|
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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()
|
||||
|
@@ -26,7 +26,7 @@ const DEFAULTS = {
|
||||
* Document.
|
||||
*/
|
||||
|
||||
class Document extends Record(DEFAULTS) {
|
||||
class Document extends new Record(DEFAULTS) {
|
||||
|
||||
/**
|
||||
* Create a new `Document` with `properties`.
|
||||
|
@@ -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)
|
||||
|
@@ -15,7 +15,7 @@ const DEFAULTS = {
|
||||
* Mark.
|
||||
*/
|
||||
|
||||
class Mark extends Record(DEFAULTS) {
|
||||
class Mark extends new Record(DEFAULTS) {
|
||||
|
||||
/**
|
||||
* Create a new `Mark` with `properties`.
|
||||
|
@@ -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]
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -19,7 +19,7 @@ const DEFAULTS = {
|
||||
* Text.
|
||||
*/
|
||||
|
||||
class Text extends Record(DEFAULTS) {
|
||||
class Text extends new Record(DEFAULTS) {
|
||||
|
||||
/**
|
||||
* Create a new `Text` with `properties`.
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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.
|
||||
|
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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))
|
||||
|
@@ -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')
|
||||
|
@@ -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.')
|
||||
}
|
||||
|
||||
/**
|
||||
|
13
package.json
13
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",
|
||||
|
Reference in New Issue
Block a user