mirror of
https://github.com/ianstormtaylor/slate.git
synced 2025-08-11 09:43:58 +02:00
cleanup
This commit is contained in:
45
Makefile
45
Makefile
@@ -24,28 +24,51 @@ clean:
|
|||||||
|
|
||||||
# Build the source.
|
# Build the source.
|
||||||
dist: $(shell find ./lib)
|
dist: $(shell find ./lib)
|
||||||
@ $(babel) --out-dir ./dist ./lib
|
@ $(babel) \
|
||||||
|
--out-dir \
|
||||||
|
./dist \
|
||||||
|
./lib
|
||||||
@ touch ./dist
|
@ touch ./dist
|
||||||
|
|
||||||
# Build the auto-markdown example.
|
# Build the auto-markdown example.
|
||||||
example-auto-markdown:
|
example-auto-markdown:
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./examples/auto-markdown/build.js ./examples/auto-markdown/index.js
|
@ $(browserify) \
|
||||||
|
--debug \
|
||||||
|
--transform babelify \
|
||||||
|
--outfile ./examples/auto-markdown/build.js \
|
||||||
|
./examples/auto-markdown/index.js
|
||||||
|
|
||||||
# Build the links example.
|
# Build the links example.
|
||||||
example-links:
|
example-links:
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./examples/links/build.js ./examples/links/index.js
|
@ $(browserify) \
|
||||||
|
--debug \
|
||||||
|
--transform babelify \
|
||||||
|
--outfile ./examples/links/build.js \
|
||||||
|
./examples/links/index.js
|
||||||
|
|
||||||
# Build the plain-text example.
|
# Build the plain-text example.
|
||||||
example-plain-text:
|
example-plain-text:
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./examples/plain-text/build.js ./examples/plain-text/index.js
|
@ $(browserify) \
|
||||||
|
--debug \
|
||||||
|
--transform babelify \
|
||||||
|
--outfile ./examples/plain-text/build.js \
|
||||||
|
./examples/plain-text/index.js
|
||||||
|
|
||||||
# Build the rich-text example.
|
# Build the rich-text example.
|
||||||
example-rich-text:
|
example-rich-text:
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./examples/rich-text/build.js ./examples/rich-text/index.js
|
@ $(browserify) \
|
||||||
|
--debug \
|
||||||
|
--transform babelify \
|
||||||
|
--outfile ./examples/rich-text/build.js \
|
||||||
|
./examples/rich-text/index.js
|
||||||
|
|
||||||
# Build the table example.
|
# Build the table example.
|
||||||
example-table:
|
example-table:
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./examples/table/build.js ./examples/table/index.js
|
@ $(browserify) \
|
||||||
|
--debug \
|
||||||
|
--transform babelify \
|
||||||
|
--outfile ./examples/table/build.js \
|
||||||
|
./examples/table/index.js
|
||||||
|
|
||||||
# Install the dependencies.
|
# Install the dependencies.
|
||||||
install:
|
install:
|
||||||
@@ -57,14 +80,20 @@ lint:
|
|||||||
|
|
||||||
# Build the test source.
|
# Build the test source.
|
||||||
test/browser/support/build.js: $(shell find ./lib) ./test/browser.js
|
test/browser/support/build.js: $(shell find ./lib) ./test/browser.js
|
||||||
@ $(browserify) --debug --transform babelify --outfile ./test/support/build.js ./test/browser.js
|
@ $(browserify) \
|
||||||
|
--debug \
|
||||||
|
--transform babelify \
|
||||||
|
--outfile ./test/support/build.js ./test/browser.js
|
||||||
|
|
||||||
# Run the tests.
|
# Run the tests.
|
||||||
test: test-browser test-server
|
test: test-browser test-server
|
||||||
|
|
||||||
# Run the browser-side tests.
|
# Run the browser-side tests.
|
||||||
test-browser: ./test/support/build.js
|
test-browser: ./test/support/build.js
|
||||||
@ $(mocha-phantomjs) --reporter spec --timeout 5000 ./test/support/browser.html
|
@ $(mocha-phantomjs) \
|
||||||
|
--reporter spec \
|
||||||
|
--timeout 5000 \
|
||||||
|
./test/support/browser.html
|
||||||
|
|
||||||
# Run the server-side tests.
|
# Run the server-side tests.
|
||||||
test-server:
|
test-server:
|
||||||
|
@@ -83,7 +83,7 @@ class Content extends React.Component {
|
|||||||
const { anchorNode, anchorOffset, focusNode, focusOffset } = native
|
const { anchorNode, anchorOffset, focusNode, focusOffset } = native
|
||||||
const anchor = OffsetKey.findPoint(anchorNode, anchorOffset)
|
const anchor = OffsetKey.findPoint(anchorNode, anchorOffset)
|
||||||
const focus = OffsetKey.findPoint(focusNode, focusOffset)
|
const focus = OffsetKey.findPoint(focusNode, focusOffset)
|
||||||
const edges = document.filterDeep((node) => {
|
const edges = document.filterDescendants((node) => {
|
||||||
return node.key == anchor.key || node.key == focus.key
|
return node.key == anchor.key || node.key == focus.key
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -22,8 +22,24 @@ const Node = {
|
|||||||
* @param {String or Node} key
|
* @param {String or Node} key
|
||||||
*/
|
*/
|
||||||
|
|
||||||
assertHasDeep(key) {
|
assertHasChild(key) {
|
||||||
if (!this.hasDeep(key)) throw new Error('Could not find that child node.')
|
key = normalizeKey(key)
|
||||||
|
if (!this.hasChild(key)) {
|
||||||
|
throw new Error(`Could not find a child node with key "${key}".`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that the node has a descendant by `key`.
|
||||||
|
*
|
||||||
|
* @param {String or Node} key
|
||||||
|
*/
|
||||||
|
|
||||||
|
assertHasDescendant(key) {
|
||||||
|
key = normalizeKey(key)
|
||||||
|
if (!this.hasDescendant(key)) {
|
||||||
|
throw new Error(`Could not find a descendant node with key "${key}".`)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -42,10 +58,10 @@ const Node = {
|
|||||||
|
|
||||||
// Make sure the children exist.
|
// Make sure the children exist.
|
||||||
const { startKey, startOffset, endKey, endOffset } = range
|
const { startKey, startOffset, endKey, endOffset } = range
|
||||||
node.assertHasDeep(startKey)
|
node.assertHasDescendant(startKey)
|
||||||
node.assertHasDeep(endKey)
|
node.assertHasDescendant(endKey)
|
||||||
|
|
||||||
let startNode = node.getDeep(startKey)
|
let startNode = node.getDescendant(startKey)
|
||||||
|
|
||||||
// If the start and end nodes are the same, remove the matching characters.
|
// If the start and end nodes are the same, remove the matching characters.
|
||||||
if (startKey == endKey) {
|
if (startKey == endKey) {
|
||||||
@@ -81,11 +97,11 @@ const Node = {
|
|||||||
let endParent = node.getParent(endKey)
|
let endParent = node.getParent(endKey)
|
||||||
|
|
||||||
const startGrandestParent = node.nodes.find((child) => {
|
const startGrandestParent = node.nodes.find((child) => {
|
||||||
return child == startParent || child.hasDeep(startParent)
|
return child == startParent || child.hasDescendant(startParent)
|
||||||
})
|
})
|
||||||
|
|
||||||
const endGrandestParent = node.nodes.find((child) => {
|
const endGrandestParent = node.nodes.find((child) => {
|
||||||
return child == endParent || child.hasDeep(endParent)
|
return child == endParent || child.hasDescendant(endParent)
|
||||||
})
|
})
|
||||||
|
|
||||||
const nodes = node.nodes
|
const nodes = node.nodes
|
||||||
@@ -103,9 +119,9 @@ const Node = {
|
|||||||
// Then remove the end parent.
|
// Then remove the end parent.
|
||||||
let endGrandparent = node.getParent(endParent)
|
let endGrandparent = node.getParent(endParent)
|
||||||
if (endGrandparent == node) {
|
if (endGrandparent == node) {
|
||||||
node = node.removeDeep(endParent)
|
node = node.removeDescendant(endParent)
|
||||||
} else {
|
} else {
|
||||||
endGrandparent = endGrandparent.removeDeep(endParent)
|
endGrandparent = endGrandparent.removeDescendant(endParent)
|
||||||
node = node.updateDeep(endGrandparent)
|
node = node.updateDeep(endGrandparent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +149,7 @@ const Node = {
|
|||||||
|
|
||||||
// When at start of a text node, merge forwards into the next text node.
|
// When at start of a text node, merge forwards into the next text node.
|
||||||
const { startKey } = range
|
const { startKey } = range
|
||||||
const startNode = node.getDeep(startKey)
|
const startNode = node.getDescendant(startKey)
|
||||||
|
|
||||||
if (range.isAtStartOf(startNode)) {
|
if (range.isAtStartOf(startNode)) {
|
||||||
const previous = node.getPreviousText(startNode)
|
const previous = node.getPreviousText(startNode)
|
||||||
@@ -170,7 +186,7 @@ const Node = {
|
|||||||
|
|
||||||
// When at end of a text node, merge forwards into the next text node.
|
// When at end of a text node, merge forwards into the next text node.
|
||||||
const { startKey } = range
|
const { startKey } = range
|
||||||
const startNode = node.getDeep(startKey)
|
const startNode = node.getDescendant(startKey)
|
||||||
|
|
||||||
if (range.isAtEndOf(startNode)) {
|
if (range.isAtEndOf(startNode)) {
|
||||||
const next = node.getNextText(startNode)
|
const next = node.getNextText(startNode)
|
||||||
@@ -188,33 +204,32 @@ const Node = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively find nodes nodes by `iterator`.
|
* Recursively find all ancestor nodes by `iterator`.
|
||||||
*
|
*
|
||||||
* @param {Function} iterator
|
* @param {Function} iterator
|
||||||
* @return {Node} node
|
* @return {Node} node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
findDeep(iterator) {
|
findDescendant(iterator) {
|
||||||
const shallow = this.nodes.find(iterator)
|
return (
|
||||||
if (shallow != null) return shallow
|
this.nodes.find(iterator) ||
|
||||||
|
this.nodes
|
||||||
return this.nodes
|
.map(node => node.kind == 'text' ? null : node.findDescendant(iterator))
|
||||||
.map(node => node.kind == 'text' ? null : node.findDeep(iterator))
|
.find(exists => exists)
|
||||||
.filter(node => node)
|
)
|
||||||
.first()
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively filter nodes nodes with `iterator`.
|
* Recursively filter all ancestor nodes with `iterator`.
|
||||||
*
|
*
|
||||||
* @param {Function} iterator
|
* @param {Function} iterator
|
||||||
* @return {OrderedMap} matches
|
* @return {List} nodes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
filterDeep(iterator) {
|
filterDescendants(iterator) {
|
||||||
return this.nodes.reduce((matches, child, i, nodes) => {
|
return this.nodes.reduce((matches, child, i, nodes) => {
|
||||||
if (iterator(child, i, nodes)) matches = matches.push(child)
|
if (iterator(child, i, nodes)) matches = matches.push(child)
|
||||||
if (child.kind != 'text') matches = matches.concat(child.filterDeep(iterator))
|
if (child.kind != 'text') matches = matches.concat(child.filterDescendants(iterator))
|
||||||
return matches
|
return matches
|
||||||
}, Block.createList())
|
}, Block.createList())
|
||||||
},
|
},
|
||||||
@@ -223,14 +238,14 @@ const Node = {
|
|||||||
* Get the closest block nodes for each text node in a `range`.
|
* Get the closest block nodes for each text node in a `range`.
|
||||||
*
|
*
|
||||||
* @param {Selection} range
|
* @param {Selection} range
|
||||||
* @return {OrderedMap} nodes
|
* @return {List} nodes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getBlocksAtRange(range) {
|
getBlocksAtRange(range) {
|
||||||
range = range.normalize(this)
|
range = range.normalize(this)
|
||||||
const texts = this.getTextsAtRange(range)
|
return this
|
||||||
const blocks = texts.map(text => this.getClosestBlock(text))
|
.getTextsAtRange(range)
|
||||||
return blocks
|
.map(text => this.getClosestBlock(text))
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -242,16 +257,12 @@ const Node = {
|
|||||||
|
|
||||||
getCharactersAtRange(range) {
|
getCharactersAtRange(range) {
|
||||||
range = range.normalize(this)
|
range = range.normalize(this)
|
||||||
const texts = this.getTextsAtRange(range)
|
return this
|
||||||
let list = new List()
|
.getTextsAtRange(range)
|
||||||
|
.reduce((characters, text) => {
|
||||||
texts.forEach((text) => {
|
const chars = text.characters.filter((char, i) => isInRange(i, text, range))
|
||||||
let { characters } = text
|
return characters.concat(chars)
|
||||||
characters = characters.filter((char, i) => isInRange(i, text, range))
|
}, Character.createList())
|
||||||
list = list.concat(characters)
|
|
||||||
})
|
|
||||||
|
|
||||||
return list
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -259,14 +270,11 @@ const Node = {
|
|||||||
*
|
*
|
||||||
* @param {String or Node} key
|
* @param {String or Node} key
|
||||||
* @param {Function} iterator
|
* @param {Function} iterator
|
||||||
* @return {Node or Null} parent
|
* @return {Node or Null} node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getClosest(key, iterator) {
|
getClosest(key, iterator) {
|
||||||
key = normalizeKey(key)
|
let node = this.getDescendant(key)
|
||||||
this.assertHasDeep(key)
|
|
||||||
|
|
||||||
let node = this.getDeep(key)
|
|
||||||
|
|
||||||
while (node = this.getParent(node)) {
|
while (node = this.getParent(node)) {
|
||||||
if (node == this) return null
|
if (node == this) return null
|
||||||
@@ -280,66 +288,46 @@ const Node = {
|
|||||||
* Get the closest block parent of a `node`.
|
* Get the closest block parent of a `node`.
|
||||||
*
|
*
|
||||||
* @param {String or Node} key
|
* @param {String or Node} key
|
||||||
* @return {Node or Null} parent
|
* @return {Node or Null} node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getClosestBlock(key) {
|
getClosestBlock(key) {
|
||||||
key = normalizeKey(key)
|
return this.getClosest(key, parent => parent.kind == 'block')
|
||||||
this.assertHasDeep(key)
|
|
||||||
|
|
||||||
const match = this.getClosest(key, parent => parent.kind == 'block')
|
|
||||||
return match
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the closest inline parent of a `node`.
|
* Get the closest inline parent of a `node`.
|
||||||
*
|
*
|
||||||
* @param {String or Node} key
|
* @param {String or Node} key
|
||||||
* @return {Node or Null} parent
|
* @return {Node or Null} node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getClosestInline(key) {
|
getClosestInline(key) {
|
||||||
key = normalizeKey(key)
|
return this.getClosest(key, parent => parent.kind == 'inline')
|
||||||
this.assertHasDeep(key)
|
|
||||||
|
|
||||||
const match = this.getClosest(key, parent => parent.kind == 'inline')
|
|
||||||
return match
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the furthest inline parent of a node by `key`.
|
|
||||||
*
|
|
||||||
* @param {String or Node} key
|
|
||||||
* @return {Node or Null} parent
|
|
||||||
*/
|
|
||||||
|
|
||||||
getFurthestInline(key) {
|
|
||||||
key = normalizeKey(key)
|
|
||||||
this.assertHasDeep(key)
|
|
||||||
|
|
||||||
let child = this.getDeep(key)
|
|
||||||
let furthest = null
|
|
||||||
let next
|
|
||||||
|
|
||||||
while (next = this.getClosestInline(child)) {
|
|
||||||
furthest = next
|
|
||||||
child = next
|
|
||||||
}
|
|
||||||
|
|
||||||
return furthest
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a child node by `key`.
|
* Get a child node by `key`.
|
||||||
*
|
*
|
||||||
* @param {String} key
|
* @param {String} key
|
||||||
* @return {Node or Null}
|
* @return {Node or Null} node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getDeep(key) {
|
getChild(key) {
|
||||||
key = normalizeKey(key)
|
key = normalizeKey(key)
|
||||||
const match = this.findDeep(node => node.key == key)
|
return this.nodes.find(node => node.key == key)
|
||||||
return match || null
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a descendant node by `key`.
|
||||||
|
*
|
||||||
|
* @param {String} key
|
||||||
|
* @return {Node or Null} node
|
||||||
|
*/
|
||||||
|
|
||||||
|
getDescendant(key) {
|
||||||
|
key = normalizeKey(key)
|
||||||
|
return this.findDescendant(node => node.key == key)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -352,7 +340,7 @@ const Node = {
|
|||||||
|
|
||||||
getDepth(key, startAt = 1) {
|
getDepth(key, startAt = 1) {
|
||||||
key = normalizeKey(key)
|
key = normalizeKey(key)
|
||||||
this.assertHasDeep(key)
|
this.assertHasDescendant(key)
|
||||||
|
|
||||||
const shallow = this.nodes.find(node => node.key == key)
|
const shallow = this.nodes.find(node => node.key == key)
|
||||||
if (shallow) return startAt
|
if (shallow) return startAt
|
||||||
@@ -360,7 +348,7 @@ const Node = {
|
|||||||
const child = this.nodes.find(node => {
|
const child = this.nodes.find(node => {
|
||||||
return node.kind == 'text'
|
return node.kind == 'text'
|
||||||
? null
|
? null
|
||||||
: node.hasDeep(key)
|
: node.hasDescendant(key)
|
||||||
})
|
})
|
||||||
|
|
||||||
return child
|
return child
|
||||||
@@ -369,41 +357,58 @@ const Node = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the first text child node.
|
* Get the furthest block parent of a node by `key`.
|
||||||
*
|
*
|
||||||
* @return {Text or Null} text
|
* @param {String or Node} key
|
||||||
|
* @return {Node or Null} node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getFirstText() {
|
getFurthestBlock(key) {
|
||||||
return this.getTextNodes().first() || null
|
let node = this.getDescendant(key)
|
||||||
|
let furthest = null
|
||||||
|
|
||||||
|
while (node = this.getClosestBlock(node)) {
|
||||||
|
furthest = node
|
||||||
|
}
|
||||||
|
|
||||||
|
return furthest
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the furthest inline parent of a node by `key`.
|
||||||
|
*
|
||||||
|
* @param {String or Node} key
|
||||||
|
* @return {Node or Null} node
|
||||||
|
*/
|
||||||
|
|
||||||
|
getFurthestInline(key) {
|
||||||
|
let node = this.getDescendant(key)
|
||||||
|
let furthest = null
|
||||||
|
|
||||||
|
while (node = this.getClosestInline(node)) {
|
||||||
|
furthest = node
|
||||||
|
}
|
||||||
|
|
||||||
|
return furthest
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the closest inline nodes for each text node in a `range`.
|
* Get the closest inline nodes for each text node in a `range`.
|
||||||
*
|
*
|
||||||
* @param {Selection} range
|
* @param {Selection} range
|
||||||
* @return {OrderedMap} nodes
|
* @return {List} nodes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getInlinesAtRange(range) {
|
getInlinesAtRange(range) {
|
||||||
range = range.normalize(this)
|
range = range.normalize(this)
|
||||||
const node = this
|
|
||||||
const texts = node.getTextsAtRange(range)
|
|
||||||
const inlines = texts
|
|
||||||
.map(text => node.getClosest(text, p => p.kind == 'inline'))
|
|
||||||
.filter(inline => inline)
|
|
||||||
|
|
||||||
return inlines
|
// If the range isn't set, return an empty list.
|
||||||
},
|
if (range.isUnset) return Inline.createList()
|
||||||
|
|
||||||
/**
|
return this
|
||||||
* Get the last text child node.
|
.getTextsAtRange(range)
|
||||||
*
|
.map(text => this.getClosestInline(text))
|
||||||
* @return {Text or Null} text
|
.filter(exists => exists)
|
||||||
*/
|
|
||||||
|
|
||||||
getLastText() {
|
|
||||||
return this.getTextNodes().last() || null
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -415,65 +420,54 @@ const Node = {
|
|||||||
|
|
||||||
getMarksAtRange(range) {
|
getMarksAtRange(range) {
|
||||||
range = range.normalize(this)
|
range = range.normalize(this)
|
||||||
const { startKey, startOffset, endKey } = range
|
const { startKey, startOffset } = range
|
||||||
|
const marks = Mark.createSet()
|
||||||
|
|
||||||
// If the selection isn't set, return nothing.
|
// If the range isn't set, return an empty set.
|
||||||
if (startKey == null || endKey == null) return new Set()
|
if (range.isUnset) return marks
|
||||||
|
|
||||||
// If the range is collapsed, and at the start of the node, check the
|
// If the range is collapsed at the start of the node, check the previous.
|
||||||
// previous text node.
|
|
||||||
if (range.isCollapsed && startOffset == 0) {
|
if (range.isCollapsed && startOffset == 0) {
|
||||||
const previous = this.getPreviousText(startKey)
|
const previous = this.getPreviousText(startKey)
|
||||||
if (!previous) return new Set()
|
if (!previous) return marks
|
||||||
const char = text.characters.get(previous.length - 1)
|
const char = text.characters.get(previous.length - 1)
|
||||||
return char.marks
|
return char.marks
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the range is collapsed, check the character before the start.
|
// If the range is collapsed, check the character before the start.
|
||||||
if (range.isCollapsed) {
|
if (range.isCollapsed) {
|
||||||
const text = this.getDeep(startKey)
|
const text = this.getDescendant(startKey)
|
||||||
const char = text.characters.get(range.startOffset - 1)
|
const char = text.characters.get(range.startOffset - 1)
|
||||||
return char.marks
|
return char.marks
|
||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, get a set of the marks for each character in the range.
|
// Otherwise, get a set of the marks for each character in the range.
|
||||||
const characters = this.getCharactersAtRange(range)
|
this
|
||||||
let set = new Set()
|
.getCharactersAtRange(range)
|
||||||
|
.reduce((marks, char) => {
|
||||||
characters.forEach((char) => {
|
return marks.union(char.marks)
|
||||||
set = set.union(char.marks)
|
}, marks)
|
||||||
})
|
|
||||||
|
|
||||||
return set
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the child node after the one by `key`.
|
* Get the node after a descendant by `key`.
|
||||||
*
|
*
|
||||||
* @param {String or Node} key
|
* @param {String or Node} key
|
||||||
* @return {Node or Null}
|
* @return {Node or Null} node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getNextSibling(key) {
|
getNextSibling(key) {
|
||||||
key = normalizeKey(key)
|
const node = this.getDescendant(key)
|
||||||
this.assertHasDeep(key)
|
if (!node) return null
|
||||||
|
return this
|
||||||
const shallow = this.nodes.find(node => node.key == key)
|
.getParent(node)
|
||||||
if (shallow) {
|
.nodes
|
||||||
return this.nodes
|
.skipUntil(child => child == node)
|
||||||
.skipUntil(node => node.key == key)
|
.get(1)
|
||||||
.rest()
|
|
||||||
.first()
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.nodes
|
|
||||||
.map(node => node.kind == 'text' ? null : node.getNextSibling(key))
|
|
||||||
.filter(node => node)
|
|
||||||
.first()
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the text node after a text node by `key`.
|
* Get the text node after a descendant text node by `key`.
|
||||||
*
|
*
|
||||||
* @param {String or Node} key
|
* @param {String or Node} key
|
||||||
* @return {Node or Null} node
|
* @return {Node or Null} node
|
||||||
@@ -481,16 +475,13 @@ const Node = {
|
|||||||
|
|
||||||
getNextText(key) {
|
getNextText(key) {
|
||||||
key = normalizeKey(key)
|
key = normalizeKey(key)
|
||||||
this.assertHasDeep(key)
|
|
||||||
|
|
||||||
return this.getTextNodes()
|
return this.getTextNodes()
|
||||||
.skipUntil(text => text.key == key)
|
.skipUntil(text => text.key == key)
|
||||||
.take(2)
|
.get(1)
|
||||||
.last()
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the offset for a child text node by `key`.
|
* Get the offset for a descendant text node by `key`.
|
||||||
*
|
*
|
||||||
* @param {String or Node} key
|
* @param {String or Node} key
|
||||||
* @return {Number} offset
|
* @return {Number} offset
|
||||||
@@ -498,16 +489,16 @@ const Node = {
|
|||||||
|
|
||||||
getOffset(key) {
|
getOffset(key) {
|
||||||
key = normalizeKey(key)
|
key = normalizeKey(key)
|
||||||
this.assertHasDeep(key)
|
this.assertHasDescendant(key)
|
||||||
|
|
||||||
const match = this.getDeep(key)
|
const match = this.getDescendant(key)
|
||||||
|
|
||||||
// Find the shallow matching child.
|
// Find the shallow matching child.
|
||||||
const child = this.nodes.find((node) => {
|
const child = this.nodes.find((node) => {
|
||||||
if (node == match) return true
|
if (node == match) return true
|
||||||
return node.kind == 'text'
|
return node.kind == 'text'
|
||||||
? false
|
? false
|
||||||
: node.hasDeep(match)
|
: node.hasDescendant(match)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Get all of the nodes that come before the matching child.
|
// Get all of the nodes that come before the matching child.
|
||||||
@@ -529,17 +520,15 @@ const Node = {
|
|||||||
* Get the parent of a child node by `key`.
|
* Get the parent of a child node by `key`.
|
||||||
*
|
*
|
||||||
* @param {String or Node} key
|
* @param {String or Node} key
|
||||||
* @return {Node or Null}
|
* @return {Node or Null} node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getParent(key) {
|
getParent(key) {
|
||||||
key = normalizeKey(key)
|
key = normalizeKey(key)
|
||||||
// this.assertHasDeep(key)
|
if (this.hasChild(key)) return this
|
||||||
|
|
||||||
const shallow = this.nodes.find(node => node.key == key)
|
|
||||||
if (shallow) return this
|
|
||||||
|
|
||||||
let node = null
|
let node = null
|
||||||
|
|
||||||
this.nodes.forEach((child) => {
|
this.nodes.forEach((child) => {
|
||||||
if (child.kind == 'text') return
|
if (child.kind == 'text') return
|
||||||
const match = child.getParent(key)
|
const match = child.getParent(key)
|
||||||
@@ -550,31 +539,24 @@ const Node = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the child node before the one by `key`.
|
* Get the node before a descendant node by `key`.
|
||||||
*
|
*
|
||||||
* @param {String or Node} key
|
* @param {String or Node} key
|
||||||
* @return {Node or Null}
|
* @return {Node or Null} node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getPreviousSibling(key) {
|
getPreviousSibling(key) {
|
||||||
key = normalizeKey(key)
|
const node = this.getDescendant(key)
|
||||||
this.assertHasDeep(key)
|
if (!node) return null
|
||||||
|
return this
|
||||||
const shallow = this.nodes.find(node => node.key == key)
|
.getParent(node)
|
||||||
if (shallow) {
|
.nodes
|
||||||
return this.nodes
|
.takeUntil(child => child == node)
|
||||||
.takeUntil(node => node.key == key)
|
.last()
|
||||||
.last()
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.nodes
|
|
||||||
.map(node => node.kind == 'text' ? null : node.getPreviousSibling(key))
|
|
||||||
.filter(node => node)
|
|
||||||
.first()
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the text node before a text node by `key`.
|
* Get the text node before a descendant text node by `key`.
|
||||||
*
|
*
|
||||||
* @param {String or Node} key
|
* @param {String or Node} key
|
||||||
* @return {Node or Null} node
|
* @return {Node or Null} node
|
||||||
@@ -582,35 +564,32 @@ const Node = {
|
|||||||
|
|
||||||
getPreviousText(key) {
|
getPreviousText(key) {
|
||||||
key = normalizeKey(key)
|
key = normalizeKey(key)
|
||||||
this.assertHasDeep(key)
|
|
||||||
|
|
||||||
return this.getTextNodes()
|
return this.getTextNodes()
|
||||||
.takeUntil(text => text.key == key)
|
.takeUntil(text => text.key == key)
|
||||||
.last()
|
.last()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the child text node at an `offset`.
|
* Get the descendent text node at an `offset`.
|
||||||
*
|
*
|
||||||
* @param {String} offset
|
* @param {String} offset
|
||||||
* @return {Node or Null}
|
* @return {Node or Null} node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getTextAtOffset(offset) {
|
getTextAtOffset(offset) {
|
||||||
let length = 0
|
let length = 0
|
||||||
let texts = this.getTextNodes()
|
return this
|
||||||
let match = texts.find((node) => {
|
.getTextNodes()
|
||||||
length += node.length
|
.find((text) => {
|
||||||
return length >= offset
|
length += text.length
|
||||||
})
|
return length >= offset
|
||||||
|
})
|
||||||
return match
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively get all of the child text nodes in order of appearance.
|
* Recursively get all of the child text nodes in order of appearance.
|
||||||
*
|
*
|
||||||
* @return {OrderedMap} nodes
|
* @return {List} nodes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getTextNodes() {
|
getTextNodes() {
|
||||||
@@ -625,43 +604,49 @@ const Node = {
|
|||||||
* Get all of the text nodes in a `range`.
|
* Get all of the text nodes in a `range`.
|
||||||
*
|
*
|
||||||
* @param {Selection} range
|
* @param {Selection} range
|
||||||
* @return {OrderedMap} nodes
|
* @return {List} nodes
|
||||||
*/
|
*/
|
||||||
|
|
||||||
getTextsAtRange(range) {
|
getTextsAtRange(range) {
|
||||||
range = range.normalize(this)
|
range = range.normalize(this)
|
||||||
|
|
||||||
|
// If the selection is unset, return an empty list.
|
||||||
|
if (range.isUnset) return Block.createList()
|
||||||
|
|
||||||
const { startKey, endKey } = range
|
const { startKey, endKey } = range
|
||||||
|
|
||||||
// If the selection is unset, return an empty map.
|
|
||||||
if (range.isUnset) return new OrderedMap()
|
|
||||||
|
|
||||||
// Assert that the nodes exist before searching.
|
|
||||||
this.assertHasDeep(startKey)
|
|
||||||
this.assertHasDeep(endKey)
|
|
||||||
|
|
||||||
// Return the text nodes after the start offset and before the end offset.
|
|
||||||
const texts = this.getTextNodes()
|
const texts = this.getTextNodes()
|
||||||
const endNode = this.getDeep(endKey)
|
const startText = this.getDescendant(startKey)
|
||||||
const afterStart = texts.skipUntil(node => node.key == startKey)
|
const endText = this.getDescendant(endKey)
|
||||||
const upToEnd = afterStart.takeUntil(node => node.key == endKey)
|
const start = texts.indexOf(startText)
|
||||||
const matches = upToEnd.push(endNode)
|
const end = texts.indexOf(endText)
|
||||||
return matches
|
return texts.slice(start, end + 1)
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a child node exists by `key`.
|
||||||
|
*
|
||||||
|
* @param {String or Node} key
|
||||||
|
* @return {Boolean} exists
|
||||||
|
*/
|
||||||
|
|
||||||
|
hasChild(key) {
|
||||||
|
key = normalizeKey(key)
|
||||||
|
return !! this.nodes.find(node => node.key == key)
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recursively check if a child node exists by `key`.
|
* Recursively check if a child node exists by `key`.
|
||||||
*
|
*
|
||||||
* @param {String or Node} key
|
* @param {String or Node} key
|
||||||
* @return {Boolean} true
|
* @return {Boolean} exists
|
||||||
*/
|
*/
|
||||||
|
|
||||||
hasDeep(key) {
|
hasDescendant(key) {
|
||||||
key = normalizeKey(key)
|
key = normalizeKey(key)
|
||||||
|
|
||||||
return !! this.nodes.find((node) => {
|
return !! this.nodes.find((node) => {
|
||||||
return node.kind == 'text'
|
return node.kind == 'text'
|
||||||
? node.key == key
|
? node.key == key
|
||||||
: node.key == key || node.hasDeep(key)
|
: node.key == key || node.hasDescendant(key)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -684,7 +669,7 @@ const Node = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let { startKey, startOffset } = range
|
let { startKey, startOffset } = range
|
||||||
let startNode = node.getDeep(startKey)
|
let startNode = node.getDescendant(startKey)
|
||||||
let { characters } = startNode
|
let { characters } = startNode
|
||||||
|
|
||||||
// Create a list of the new characters, with the marks from the previous
|
// Create a list of the new characters, with the marks from the previous
|
||||||
@@ -764,7 +749,7 @@ const Node = {
|
|||||||
let node = this
|
let node = this
|
||||||
|
|
||||||
// See if there are any adjacent text nodes.
|
// See if there are any adjacent text nodes.
|
||||||
let firstAdjacent = node.findDeep((child) => {
|
let firstAdjacent = node.findDescendant((child) => {
|
||||||
if (child.kind != 'text') return
|
if (child.kind != 'text') return
|
||||||
const parent = node.getParent(child)
|
const parent = node.getParent(child)
|
||||||
const next = parent.getNextSibling(child)
|
const next = parent.getNextSibling(child)
|
||||||
@@ -782,7 +767,7 @@ const Node = {
|
|||||||
parent = parent.updateDeep(firstAdjacent)
|
parent = parent.updateDeep(firstAdjacent)
|
||||||
|
|
||||||
// Then remove the second node.
|
// Then remove the second node.
|
||||||
parent = parent.removeDeep(second)
|
parent = parent.removeDescendant(second)
|
||||||
|
|
||||||
// If the parent isn't this node, it needs to be updated.
|
// If the parent isn't this node, it needs to be updated.
|
||||||
if (parent != node) {
|
if (parent != node) {
|
||||||
@@ -795,18 +780,6 @@ const Node = {
|
|||||||
return node.normalize()
|
return node.normalize()
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Push a new `node` onto the map of nodes.
|
|
||||||
*
|
|
||||||
* @param {Node} node
|
|
||||||
* @return {Node} node
|
|
||||||
*/
|
|
||||||
|
|
||||||
pushNode(node) {
|
|
||||||
const nodes = this.nodes.push(node)
|
|
||||||
return this.merge({ nodes })
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a `node` from the children node map.
|
* Remove a `node` from the children node map.
|
||||||
*
|
*
|
||||||
@@ -814,9 +787,9 @@ const Node = {
|
|||||||
* @return {Node} node
|
* @return {Node} node
|
||||||
*/
|
*/
|
||||||
|
|
||||||
removeDeep(key) {
|
removeDescendant(key) {
|
||||||
key = normalizeKey(key)
|
key = normalizeKey(key)
|
||||||
this.assertHasDeep(key)
|
this.assertHasDescendant(key)
|
||||||
const nodes = this.nodes.filterNot(node => node.key == key)
|
const nodes = this.nodes.filterNot(node => node.key == key)
|
||||||
return this.merge({ nodes })
|
return this.merge({ nodes })
|
||||||
},
|
},
|
||||||
@@ -913,14 +886,14 @@ const Node = {
|
|||||||
|
|
||||||
// Find the highest inline elements that were split.
|
// Find the highest inline elements that were split.
|
||||||
const { startKey } = range
|
const { startKey } = range
|
||||||
const firstText = node.getDeep(startKey)
|
const firstText = node.getDescendant(startKey)
|
||||||
const firstChild = node.getFurthestInline(firstText) || firstText
|
const firstChild = node.getFurthestInline(firstText) || firstText
|
||||||
const secondText = node.getNextText(startKey)
|
const secondText = node.getNextText(startKey)
|
||||||
const secondChild = node.getFurthestInline(secondText) || secondText
|
const secondChild = node.getFurthestInline(secondText) || secondText
|
||||||
|
|
||||||
// Remove the second inline child from the first block.
|
// Remove the second inline child from the first block.
|
||||||
let firstBlock = node.getBlocksAtRange(range).first()
|
let firstBlock = node.getBlocksAtRange(range).first()
|
||||||
firstBlock = firstBlock.removeDeep(secondChild)
|
firstBlock = firstBlock.removeDescendant(secondChild)
|
||||||
|
|
||||||
// Create a new block with the second inline child in it.
|
// Create a new block with the second inline child in it.
|
||||||
const secondBlock = Block.create({
|
const secondBlock = Block.create({
|
||||||
@@ -961,7 +934,7 @@ const Node = {
|
|||||||
|
|
||||||
// First split the text nodes.
|
// First split the text nodes.
|
||||||
node = node.splitTextAtRange(range)
|
node = node.splitTextAtRange(range)
|
||||||
let firstChild = node.getDeep(range.startKey)
|
let firstChild = node.getDescendant(range.startKey)
|
||||||
let secondChild = node.getNextText(firstChild)
|
let secondChild = node.getNextText(firstChild)
|
||||||
let parent
|
let parent
|
||||||
|
|
||||||
@@ -1010,7 +983,7 @@ const Node = {
|
|||||||
|
|
||||||
// Split the text node's characters.
|
// Split the text node's characters.
|
||||||
const { startKey, startOffset } = range
|
const { startKey, startOffset } = range
|
||||||
const text = node.getDeep(startKey)
|
const text = node.getDescendant(startKey)
|
||||||
const { characters } = text
|
const { characters } = text
|
||||||
const firstChars = characters.take(startOffset)
|
const firstChars = characters.take(startOffset)
|
||||||
const secondChars = characters.skip(startOffset)
|
const secondChars = characters.skip(startOffset)
|
||||||
@@ -1082,7 +1055,7 @@ const Node = {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
updateDeep(node) {
|
updateDeep(node) {
|
||||||
// this.assertHasDeep(key)
|
// this.assertHasDescendant(key)
|
||||||
|
|
||||||
const shallow = this.nodes.find(child => child.key == node.key)
|
const shallow = this.nodes.find(child => child.key == node.key)
|
||||||
if (shallow) {
|
if (shallow) {
|
||||||
@@ -1235,7 +1208,7 @@ const Node = {
|
|||||||
|
|
||||||
// Determine the new end of the range, and split there.
|
// Determine the new end of the range, and split there.
|
||||||
const { startKey, startOffset, endKey, endOffset } = range
|
const { startKey, startOffset, endKey, endOffset } = range
|
||||||
const firstNode = node.getDeep(startKey)
|
const firstNode = node.getDescendant(startKey)
|
||||||
const nextNode = node.getNextText(startKey)
|
const nextNode = node.getNextText(startKey)
|
||||||
const end = startKey != endKey
|
const end = startKey != endKey
|
||||||
? range.moveToEnd()
|
? range.moveToEnd()
|
||||||
@@ -1249,7 +1222,7 @@ const Node = {
|
|||||||
node = node.splitInlineAtRange(end)
|
node = node.splitInlineAtRange(end)
|
||||||
|
|
||||||
// Calculate the new range to wrap around.
|
// Calculate the new range to wrap around.
|
||||||
const endNode = node.getDeep(end.anchorKey)
|
const endNode = node.getDescendant(end.anchorKey)
|
||||||
range = Selection.create({
|
range = Selection.create({
|
||||||
anchorKey: nextNode.key,
|
anchorKey: nextNode.key,
|
||||||
anchorOffset: 0,
|
anchorOffset: 0,
|
||||||
|
@@ -114,7 +114,7 @@ class Selection extends Record(DEFAULTS) {
|
|||||||
|
|
||||||
isAtStartOf(node) {
|
isAtStartOf(node) {
|
||||||
const { startKey, startOffset } = this
|
const { startKey, startOffset } = this
|
||||||
const first = node.kind == 'text' ? node : node.getFirstText()
|
const first = node.kind == 'text' ? node : node.getTextNodes().first()
|
||||||
return startKey == first.key && startOffset == 0
|
return startKey == first.key && startOffset == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,7 +127,7 @@ class Selection extends Record(DEFAULTS) {
|
|||||||
|
|
||||||
isAtEndOf(node) {
|
isAtEndOf(node) {
|
||||||
const { endKey, endOffset } = this
|
const { endKey, endOffset } = this
|
||||||
const last = node.kind == 'text' ? node : node.getLastText()
|
const last = node.kind == 'text' ? node : node.getTextNodes().last()
|
||||||
return endKey == last.key && endOffset == last.length
|
return endKey == last.key && endOffset == last.length
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,10 +147,10 @@ class Selection extends Record(DEFAULTS) {
|
|||||||
if (anchorKey == null || focusKey == null) return selection
|
if (anchorKey == null || focusKey == null) return selection
|
||||||
|
|
||||||
// Asset that the anchor and focus nodes exist in the node tree.
|
// Asset that the anchor and focus nodes exist in the node tree.
|
||||||
node.assertHasDeep(anchorKey)
|
node.assertHasDescendant(anchorKey)
|
||||||
node.assertHasDeep(focusKey)
|
node.assertHasDescendant(focusKey)
|
||||||
let anchorNode = node.getDeep(anchorKey)
|
let anchorNode = node.getDescendant(anchorKey)
|
||||||
let focusNode = node.getDeep(focusKey)
|
let focusNode = node.getDescendant(focusKey)
|
||||||
|
|
||||||
// If the anchor node isn't a text node, match it to one.
|
// If the anchor node isn't a text node, match it to one.
|
||||||
if (anchorNode.kind != 'text') {
|
if (anchorNode.kind != 'text') {
|
||||||
|
@@ -280,7 +280,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
|
|
||||||
// Determine what the selection should be after deleting.
|
// Determine what the selection should be after deleting.
|
||||||
const { startKey } = selection
|
const { startKey } = selection
|
||||||
const startNode = document.getDeep(startKey)
|
const startNode = document.getDescendant(startKey)
|
||||||
|
|
||||||
if (selection.isExpanded) {
|
if (selection.isExpanded) {
|
||||||
after = selection.moveToStart()
|
after = selection.moveToStart()
|
||||||
@@ -410,7 +410,7 @@ class State extends Record(DEFAULTS) {
|
|||||||
|
|
||||||
// Determine what the selection should be after splitting.
|
// Determine what the selection should be after splitting.
|
||||||
const { startKey } = selection
|
const { startKey } = selection
|
||||||
const startNode = document.getDeep(startKey)
|
const startNode = document.getDescendant(startKey)
|
||||||
const parent = document.getParent(startNode)
|
const parent = document.getParent(startNode)
|
||||||
const next = document.getNext(parent)
|
const next = document.getNext(parent)
|
||||||
const text = next.nodes.first()
|
const text = next.nodes.first()
|
||||||
|
Reference in New Issue
Block a user