2016-06-15 12:07:12 -07:00
|
|
|
|
2016-06-18 18:21:21 -07:00
|
|
|
import Document from './document'
|
2016-06-17 18:52:23 -07:00
|
|
|
import Selection from './selection'
|
2016-06-18 18:21:21 -07:00
|
|
|
import Transform from './transform'
|
|
|
|
import { Record, Stack } from 'immutable'
|
2016-06-15 12:07:12 -07:00
|
|
|
|
|
|
|
/**
|
2016-06-18 18:21:21 -07:00
|
|
|
* History.
|
2016-06-15 12:07:12 -07:00
|
|
|
*/
|
|
|
|
|
2016-06-18 18:21:21 -07:00
|
|
|
const History = new Record({
|
|
|
|
undos: new Stack(),
|
|
|
|
redos: new Stack()
|
2016-06-15 12:07:12 -07:00
|
|
|
})
|
|
|
|
|
2016-06-17 00:09:54 -07:00
|
|
|
/**
|
2016-06-18 18:21:21 -07:00
|
|
|
* Default properties.
|
2016-06-17 00:09:54 -07:00
|
|
|
*/
|
|
|
|
|
2016-06-20 12:57:31 -07:00
|
|
|
const DEFAULTS = {
|
2016-06-18 18:21:21 -07:00
|
|
|
document: new Document(),
|
|
|
|
selection: new Selection(),
|
2016-06-18 23:05:38 -07:00
|
|
|
history: new History(),
|
2016-06-24 16:02:59 -07:00
|
|
|
isNative: true,
|
|
|
|
copiedFragment: null
|
2016-06-18 18:21:21 -07:00
|
|
|
}
|
2016-06-17 00:09:54 -07:00
|
|
|
|
2016-06-15 12:07:12 -07:00
|
|
|
/**
|
|
|
|
* State.
|
|
|
|
*/
|
|
|
|
|
2016-06-20 12:57:31 -07:00
|
|
|
class State extends Record(DEFAULTS) {
|
2016-06-15 12:07:12 -07:00
|
|
|
|
|
|
|
/**
|
2016-06-17 18:20:26 -07:00
|
|
|
* Create a new `State` with `properties`.
|
2016-06-15 12:07:12 -07:00
|
|
|
*
|
2016-06-20 12:57:31 -07:00
|
|
|
* @param {Object} properties
|
2016-06-15 12:07:12 -07:00
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
2016-06-17 18:20:26 -07:00
|
|
|
static create(properties = {}) {
|
2016-06-23 10:43:36 -07:00
|
|
|
if (properties instanceof State) return properties
|
|
|
|
properties.document = Document.create(properties.document)
|
|
|
|
properties.selection = Selection.create(properties.selection)
|
2016-06-17 18:20:26 -07:00
|
|
|
return new State(properties)
|
2016-06-15 12:07:12 -07:00
|
|
|
}
|
|
|
|
|
2016-06-20 19:16:36 -07:00
|
|
|
/**
|
|
|
|
* Is the current selection collapsed?
|
|
|
|
*
|
|
|
|
* @return {Boolean} isCollapsed
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get isCollapsed() {
|
2016-06-20 19:16:36 -07:00
|
|
|
return this.selection.isCollapsed
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is the current selection expanded?
|
|
|
|
*
|
|
|
|
* @return {Boolean} isExpanded
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get isExpanded() {
|
2016-06-20 19:16:36 -07:00
|
|
|
return this.selection.isExpanded
|
|
|
|
}
|
|
|
|
|
2016-06-22 18:59:19 -07:00
|
|
|
/**
|
|
|
|
* Is the current selection backward?
|
|
|
|
*
|
|
|
|
* @return {Boolean} isBackward
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get isBackward() {
|
2016-06-22 18:59:19 -07:00
|
|
|
return this.selection.isBackward
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Is the current selection forward?
|
|
|
|
*
|
|
|
|
* @return {Boolean} isForward
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get isForward() {
|
2016-06-22 18:59:19 -07:00
|
|
|
return this.selection.isForward
|
|
|
|
}
|
|
|
|
|
2016-06-21 10:43:04 -07:00
|
|
|
/**
|
|
|
|
* Get the current start key.
|
|
|
|
*
|
|
|
|
* @return {String} startKey
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get startKey() {
|
2016-06-21 10:43:04 -07:00
|
|
|
return this.selection.startKey
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current end key.
|
|
|
|
*
|
|
|
|
* @return {String} endKey
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get endKey() {
|
2016-06-21 10:43:04 -07:00
|
|
|
return this.selection.endKey
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current start offset.
|
|
|
|
*
|
|
|
|
* @return {String} startOffset
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get startOffset() {
|
2016-06-21 10:43:04 -07:00
|
|
|
return this.selection.startOffset
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current end offset.
|
|
|
|
*
|
|
|
|
* @return {String} endOffset
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get endOffset() {
|
2016-06-21 10:43:04 -07:00
|
|
|
return this.selection.endOffset
|
|
|
|
}
|
|
|
|
|
2016-06-22 18:59:19 -07:00
|
|
|
/**
|
|
|
|
* Get the current anchor key.
|
|
|
|
*
|
|
|
|
* @return {String} anchorKey
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get anchorKey() {
|
2016-06-22 18:59:19 -07:00
|
|
|
return this.selection.anchorKey
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current focus key.
|
|
|
|
*
|
|
|
|
* @return {String} focusKey
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get focusKey() {
|
2016-06-22 18:59:19 -07:00
|
|
|
return this.selection.focusKey
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current anchor offset.
|
|
|
|
*
|
|
|
|
* @return {String} anchorOffset
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get anchorOffset() {
|
2016-06-22 18:59:19 -07:00
|
|
|
return this.selection.anchorOffset
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current focus offset.
|
|
|
|
*
|
|
|
|
* @return {String} focusOffset
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get focusOffset() {
|
2016-06-22 18:59:19 -07:00
|
|
|
return this.selection.focusOffset
|
|
|
|
}
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
/**
|
|
|
|
* Get the current start text node.
|
|
|
|
*
|
|
|
|
* @return {Text} text
|
|
|
|
*/
|
|
|
|
|
|
|
|
get startText() {
|
|
|
|
return this.document.getDescendant(this.selection.startKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current end node.
|
|
|
|
*
|
|
|
|
* @return {Text} text
|
|
|
|
*/
|
|
|
|
|
|
|
|
get endText() {
|
|
|
|
return this.document.getDescendant(this.selection.endKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current anchor node.
|
|
|
|
*
|
|
|
|
* @return {Text} text
|
|
|
|
*/
|
|
|
|
|
|
|
|
get anchorText() {
|
|
|
|
return this.document.getDescendant(this.selection.anchorKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current focus node.
|
|
|
|
*
|
|
|
|
* @return {Text} text
|
|
|
|
*/
|
|
|
|
|
|
|
|
get focusText() {
|
|
|
|
return this.document.getDescendant(this.selection.focusKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current start text node's closest block parent.
|
|
|
|
*
|
|
|
|
* @return {Block} block
|
|
|
|
*/
|
|
|
|
|
|
|
|
get startBlock() {
|
|
|
|
return this.document.getClosestBlock(this.selection.startKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current end text node's closest block parent.
|
|
|
|
*
|
|
|
|
* @return {Block} block
|
|
|
|
*/
|
|
|
|
|
|
|
|
get endBlock() {
|
|
|
|
return this.document.getClosestBlock(this.selection.endKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current anchor text node's closest block parent.
|
|
|
|
*
|
|
|
|
* @return {Block} block
|
|
|
|
*/
|
|
|
|
|
|
|
|
get anchorBlock() {
|
|
|
|
return this.document.getClosestBlock(this.selection.anchorKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the current focus text node's closest block parent.
|
|
|
|
*
|
|
|
|
* @return {Block} block
|
|
|
|
*/
|
|
|
|
|
|
|
|
get focusBlock() {
|
|
|
|
return this.document.getClosestBlock(this.selection.focusKey)
|
|
|
|
}
|
|
|
|
|
2016-06-20 12:57:31 -07:00
|
|
|
/**
|
|
|
|
* Get the characters in the current selection.
|
|
|
|
*
|
|
|
|
* @return {List} characters
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get characters() {
|
2016-06-20 19:16:36 -07:00
|
|
|
return this.document.getCharactersAtRange(this.selection)
|
2016-06-20 12:57:31 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the marks of the current selection.
|
|
|
|
*
|
|
|
|
* @return {Set} marks
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get marks() {
|
2016-06-20 19:16:36 -07:00
|
|
|
return this.document.getMarksAtRange(this.selection)
|
2016-06-20 12:57:31 -07:00
|
|
|
}
|
|
|
|
|
2016-06-20 17:38:56 -07:00
|
|
|
/**
|
2016-06-21 16:44:11 -07:00
|
|
|
* Get the block nodes in the current selection.
|
2016-06-20 17:38:56 -07:00
|
|
|
*
|
2016-06-24 16:02:59 -07:00
|
|
|
* @return {List} nodes
|
2016-06-20 17:38:56 -07:00
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get blocks() {
|
2016-06-22 18:42:49 -07:00
|
|
|
return this.document.getBlocksAtRange(this.selection)
|
|
|
|
}
|
|
|
|
|
2016-06-24 16:02:59 -07:00
|
|
|
/**
|
|
|
|
* Get the fragment of the current selection.
|
|
|
|
*
|
|
|
|
* @return {List} nodes
|
|
|
|
*/
|
|
|
|
|
|
|
|
get fragment() {
|
|
|
|
return this.document.getFragmentAtRange(this.selection)
|
|
|
|
}
|
|
|
|
|
2016-06-22 18:42:49 -07:00
|
|
|
/**
|
|
|
|
* Get the inline nodes in the current selection.
|
|
|
|
*
|
2016-06-24 16:02:59 -07:00
|
|
|
* @return {List} nodes
|
2016-06-22 18:42:49 -07:00
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get inlines() {
|
2016-06-22 18:42:49 -07:00
|
|
|
return this.document.getInlinesAtRange(this.selection)
|
2016-06-20 17:38:56 -07:00
|
|
|
}
|
|
|
|
|
2016-06-20 12:57:31 -07:00
|
|
|
/**
|
|
|
|
* Get the text nodes in the current selection.
|
|
|
|
*
|
2016-06-24 16:02:59 -07:00
|
|
|
* @return {List} nodes
|
2016-06-20 12:57:31 -07:00
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
get texts() {
|
2016-06-22 18:59:19 -07:00
|
|
|
return this.document.getTextsAtRange(this.selection)
|
2016-06-20 12:57:31 -07:00
|
|
|
}
|
|
|
|
|
2016-06-16 12:12:50 -07:00
|
|
|
/**
|
2016-06-18 18:21:21 -07:00
|
|
|
* Return a new `Transform` with the current state as a starting point.
|
2016-06-16 12:12:50 -07:00
|
|
|
*
|
2016-06-18 18:21:21 -07:00
|
|
|
* @return {Transform} transform
|
2016-06-16 12:12:50 -07:00
|
|
|
*/
|
|
|
|
|
2016-06-18 18:21:21 -07:00
|
|
|
transform() {
|
2016-06-20 12:57:31 -07:00
|
|
|
const state = this
|
|
|
|
return new Transform({ state })
|
2016-06-16 12:12:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-06-20 12:57:31 -07:00
|
|
|
* Delete at the current selection.
|
2016-06-17 13:34:29 -07:00
|
|
|
*
|
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
|
|
|
delete() {
|
|
|
|
let state = this
|
2016-06-18 18:21:21 -07:00
|
|
|
let { document, selection } = state
|
2016-06-17 13:34:29 -07:00
|
|
|
|
|
|
|
// When collapsed, there's nothing to do.
|
2016-06-18 18:21:21 -07:00
|
|
|
if (selection.isCollapsed) return state
|
2016-06-17 13:34:29 -07:00
|
|
|
|
|
|
|
// Otherwise, delete and update the selection.
|
2016-06-18 18:21:21 -07:00
|
|
|
document = document.deleteAtRange(selection)
|
|
|
|
selection = selection.moveToStart()
|
|
|
|
state = state.merge({ document, selection })
|
2016-06-17 13:34:29 -07:00
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete backward `n` characters at the current selection.
|
|
|
|
*
|
|
|
|
* @param {Number} n (optional)
|
|
|
|
* @return {State} state
|
|
|
|
*/
|
2016-06-17 00:09:54 -07:00
|
|
|
|
2016-06-17 13:34:29 -07:00
|
|
|
deleteBackward(n = 1) {
|
|
|
|
let state = this
|
2016-06-18 18:21:21 -07:00
|
|
|
let { document, selection } = state
|
|
|
|
let after = selection
|
2016-06-17 13:34:29 -07:00
|
|
|
|
|
|
|
// Determine what the selection should be after deleting.
|
2016-06-18 18:21:21 -07:00
|
|
|
const { startKey } = selection
|
2016-06-23 12:34:47 -07:00
|
|
|
const startNode = document.getDescendant(startKey)
|
2016-06-17 13:34:29 -07:00
|
|
|
|
2016-06-21 17:08:15 -07:00
|
|
|
if (selection.isExpanded) {
|
|
|
|
after = selection.moveToStart()
|
2016-06-20 13:21:24 -07:00
|
|
|
}
|
|
|
|
|
2016-06-21 17:08:15 -07:00
|
|
|
else if (selection.isAtStartOf(document)) {
|
|
|
|
after = selection
|
2016-06-17 18:52:23 -07:00
|
|
|
}
|
|
|
|
|
2016-06-18 18:21:21 -07:00
|
|
|
else if (selection.isAtStartOf(startNode)) {
|
2016-06-22 18:42:49 -07:00
|
|
|
const parent = document.getParent(startNode)
|
2016-06-23 15:39:44 -07:00
|
|
|
const previous = document.getPreviousSibling(parent).nodes.first()
|
2016-06-18 18:21:21 -07:00
|
|
|
after = selection.moveToEndOf(previous)
|
2016-06-17 13:34:29 -07:00
|
|
|
}
|
2016-06-17 00:34:27 -07:00
|
|
|
|
2016-06-20 17:38:56 -07:00
|
|
|
else {
|
2016-06-18 18:21:21 -07:00
|
|
|
after = selection.moveBackward(n)
|
2016-06-17 00:09:54 -07:00
|
|
|
}
|
|
|
|
|
2016-06-17 13:34:29 -07:00
|
|
|
// Delete backward and then update the selection.
|
2016-06-18 18:21:21 -07:00
|
|
|
document = document.deleteBackwardAtRange(selection)
|
|
|
|
selection = after
|
|
|
|
state = state.merge({ document, selection })
|
2016-06-17 13:34:29 -07:00
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Delete forward `n` characters at the current selection.
|
|
|
|
*
|
|
|
|
* @param {Number} n (optional)
|
|
|
|
* @return {State} state
|
|
|
|
*/
|
2016-06-15 12:07:12 -07:00
|
|
|
|
2016-06-17 13:34:29 -07:00
|
|
|
deleteForward(n = 1) {
|
|
|
|
let state = this
|
2016-06-18 18:21:21 -07:00
|
|
|
let { document, selection } = state
|
|
|
|
let after = selection
|
2016-06-17 18:52:23 -07:00
|
|
|
|
|
|
|
// Determine what the selection should be after deleting.
|
2016-06-18 18:21:21 -07:00
|
|
|
if (selection.isExpanded) {
|
|
|
|
after = selection.moveToStart()
|
2016-06-17 18:52:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Delete forward and then update the selection.
|
2016-06-18 18:21:21 -07:00
|
|
|
document = document.deleteForwardAtRange(selection)
|
|
|
|
selection = after
|
|
|
|
state = state.merge({ document, selection })
|
2016-06-17 12:00:15 -07:00
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
2016-06-24 17:22:08 -07:00
|
|
|
/**
|
|
|
|
* Insert a `fragment` at the current selection.
|
|
|
|
*
|
|
|
|
* @param {List} fragment
|
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
|
|
|
insertFragment(fragment) {
|
|
|
|
let state = this
|
|
|
|
let { document, selection } = state
|
|
|
|
let after = selection
|
|
|
|
|
|
|
|
//
|
|
|
|
}
|
|
|
|
|
2016-06-17 12:00:15 -07:00
|
|
|
/**
|
2016-06-20 12:57:31 -07:00
|
|
|
* Insert a `text` string at the current selection.
|
2016-06-17 12:00:15 -07:00
|
|
|
*
|
2016-06-18 19:18:47 -07:00
|
|
|
* @param {String} text
|
2016-06-17 13:34:29 -07:00
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
2016-06-18 19:18:47 -07:00
|
|
|
insertText(text) {
|
2016-06-17 13:34:29 -07:00
|
|
|
let state = this
|
2016-06-18 18:21:21 -07:00
|
|
|
let { document, selection } = state
|
2016-06-24 14:07:11 -07:00
|
|
|
let after = selection
|
|
|
|
|
|
|
|
// Determine what the selection should be after inserting.
|
|
|
|
if (selection.isExpanded) {
|
|
|
|
after = selection.moveToStart()
|
|
|
|
}
|
2016-06-17 13:34:29 -07:00
|
|
|
|
2016-06-18 19:18:47 -07:00
|
|
|
// Insert the text and update the selection.
|
|
|
|
document = document.insertTextAtRange(selection, text)
|
2016-06-24 14:07:11 -07:00
|
|
|
selection = after
|
2016-06-18 19:18:47 -07:00
|
|
|
selection = selection.moveForward(text.length)
|
2016-06-18 18:21:21 -07:00
|
|
|
state = state.merge({ document, selection })
|
2016-06-17 13:34:29 -07:00
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-06-20 12:57:31 -07:00
|
|
|
* Add a `mark` to the characters in the current selection.
|
|
|
|
*
|
|
|
|
* @param {Mark} mark
|
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
|
|
|
mark(mark) {
|
|
|
|
let state = this
|
|
|
|
let { document, selection } = state
|
|
|
|
document = document.markAtRange(selection, mark)
|
|
|
|
state = state.merge({ document })
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
2016-06-20 17:38:56 -07:00
|
|
|
/**
|
2016-06-22 18:42:49 -07:00
|
|
|
* Set the block nodes in the current selection to `type`.
|
|
|
|
*
|
|
|
|
* @param {String} type
|
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
|
|
|
setBlock(type, data) {
|
|
|
|
let state = this
|
|
|
|
let { document, selection } = state
|
|
|
|
document = document.setBlockAtRange(selection, type, data)
|
|
|
|
state = state.merge({ document })
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the inline nodes in the current selection to `type`.
|
2016-06-20 17:38:56 -07:00
|
|
|
*
|
|
|
|
* @param {String} type
|
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
2016-06-22 18:42:49 -07:00
|
|
|
setInline(type, data) {
|
2016-06-20 17:38:56 -07:00
|
|
|
let state = this
|
|
|
|
let { document, selection } = state
|
2016-06-22 18:42:49 -07:00
|
|
|
document = document.setInlineAtRange(selection, type, data)
|
2016-06-20 17:38:56 -07:00
|
|
|
state = state.merge({ document })
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
2016-06-20 12:57:31 -07:00
|
|
|
/**
|
2016-06-23 15:39:44 -07:00
|
|
|
* Split the block node at the current selection.
|
2016-06-16 16:43:02 -07:00
|
|
|
*
|
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
2016-06-23 15:39:44 -07:00
|
|
|
splitBlock() {
|
2016-06-17 00:09:54 -07:00
|
|
|
let state = this
|
2016-06-18 18:21:21 -07:00
|
|
|
let { document, selection } = state
|
2016-06-17 00:34:27 -07:00
|
|
|
|
2016-06-18 18:21:21 -07:00
|
|
|
// Split the document.
|
2016-06-23 15:39:44 -07:00
|
|
|
document = document.splitBlockAtRange(selection)
|
2016-06-17 13:34:29 -07:00
|
|
|
|
2016-06-18 18:21:21 -07:00
|
|
|
// Determine what the selection should be after splitting.
|
|
|
|
const { startKey } = selection
|
2016-06-23 12:34:47 -07:00
|
|
|
const startNode = document.getDescendant(startKey)
|
2016-06-23 15:39:44 -07:00
|
|
|
const nextNode = document.getNextText(startNode)
|
|
|
|
selection = selection.moveToStartOf(nextNode)
|
|
|
|
|
|
|
|
state = state.merge({ document, selection })
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Split the inline nodes at the current selection.
|
|
|
|
*
|
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
|
|
|
splitInline() {
|
|
|
|
let state = this
|
|
|
|
let { document, selection } = state
|
|
|
|
|
|
|
|
// Split the document.
|
|
|
|
document = document.splitInlineAtRange(selection)
|
|
|
|
|
|
|
|
// Determine what the selection should be after splitting.
|
|
|
|
const { startKey } = selection
|
|
|
|
const inlineParent = document.getClosestInline(startKey)
|
|
|
|
|
|
|
|
if (inlineParent) {
|
|
|
|
const startNode = document.getDescendant(startKey)
|
|
|
|
const nextNode = document.getNextText(startNode)
|
|
|
|
selection = selection.moveToStartOf(nextNode)
|
|
|
|
}
|
2016-06-17 16:10:44 -07:00
|
|
|
|
2016-06-18 18:21:21 -07:00
|
|
|
state = state.merge({ document, selection })
|
2016-06-17 16:10:44 -07:00
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
2016-06-20 12:57:31 -07:00
|
|
|
/**
|
|
|
|
* Remove a `mark` to the characters in the current selection.
|
|
|
|
*
|
|
|
|
* @param {Mark} mark
|
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
|
|
|
unmark(mark) {
|
|
|
|
let state = this
|
|
|
|
let { document, selection } = state
|
|
|
|
document = document.unmarkAtRange(selection, mark)
|
|
|
|
state = state.merge({ document })
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
2016-06-21 18:00:18 -07:00
|
|
|
/**
|
|
|
|
* Wrap the block nodes in the current selection in new nodes of `type`.
|
|
|
|
*
|
|
|
|
* @param {String} type
|
2016-06-23 23:39:08 -07:00
|
|
|
* @param {Data} data (optional)
|
2016-06-21 18:00:18 -07:00
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
2016-06-23 23:39:08 -07:00
|
|
|
wrapBlock(type, data) {
|
2016-06-21 18:00:18 -07:00
|
|
|
let state = this
|
|
|
|
let { document, selection } = state
|
2016-06-23 23:39:08 -07:00
|
|
|
document = document.wrapBlockAtRange(selection, type, data)
|
2016-06-21 18:00:18 -07:00
|
|
|
state = state.merge({ document })
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
2016-06-21 19:02:39 -07:00
|
|
|
/**
|
2016-06-23 23:39:08 -07:00
|
|
|
* Unwrap the current selection from a block parent of `type`.
|
2016-06-21 19:02:39 -07:00
|
|
|
*
|
2016-06-23 23:39:08 -07:00
|
|
|
* @param {String} type (optional)
|
|
|
|
* @param {Data} data (optional)
|
2016-06-21 19:02:39 -07:00
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
2016-06-23 23:39:08 -07:00
|
|
|
unwrapBlock(type, data) {
|
2016-06-22 18:42:49 -07:00
|
|
|
let state = this
|
|
|
|
let { document, selection } = state
|
2016-06-23 23:39:08 -07:00
|
|
|
document = document.unwrapBlockAtRange(selection, type, data)
|
2016-06-22 18:42:49 -07:00
|
|
|
state = state.merge({ document, selection })
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Wrap the current selection in new inline nodes of `type`.
|
|
|
|
*
|
|
|
|
* @param {String} type
|
2016-06-23 23:39:08 -07:00
|
|
|
* @param {Data} data (optional)
|
2016-06-22 18:42:49 -07:00
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
|
|
|
wrapInline(type, data) {
|
|
|
|
let state = this
|
|
|
|
let { document, selection } = state
|
|
|
|
document = document.wrapInlineAtRange(selection, type, data)
|
2016-06-23 23:39:08 -07:00
|
|
|
|
|
|
|
// 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)
|
|
|
|
selection = selection.moveToRangeOf(start, end)
|
|
|
|
selection = selection.normalize(document)
|
|
|
|
}
|
|
|
|
|
|
|
|
state = state.merge({ document, selection })
|
2016-06-22 18:42:49 -07:00
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-06-23 23:39:08 -07:00
|
|
|
* Unwrap the current selection from an inline parent of `type`.
|
2016-06-22 18:42:49 -07:00
|
|
|
*
|
2016-06-23 23:39:08 -07:00
|
|
|
* @param {String} type (optional)
|
|
|
|
* @param {Data} data (optional)
|
2016-06-22 18:42:49 -07:00
|
|
|
* @return {State} state
|
|
|
|
*/
|
|
|
|
|
2016-06-23 23:39:08 -07:00
|
|
|
unwrapInline(type, data) {
|
2016-06-21 19:02:39 -07:00
|
|
|
let state = this
|
|
|
|
let { document, selection } = state
|
2016-06-23 23:39:08 -07:00
|
|
|
document = document.unwrapInlineAtRange(selection, type, data)
|
2016-06-21 19:02:39 -07:00
|
|
|
state = state.merge({ document, selection })
|
|
|
|
return state
|
|
|
|
}
|
|
|
|
|
2016-06-15 12:07:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Export.
|
|
|
|
*/
|
|
|
|
|
|
|
|
export default State
|