substance
Version:
Substance is a JavaScript library for web-based content editing. It provides building blocks for realizing custom text editors and web-based publishing systems.
132 lines (121 loc) • 4.02 kB
JavaScript
import { cloneDeep, forEach } from '../util'
import Document from './Document'
import documentHelpers from './documentHelpers'
import { isFirst, isLast } from './selectionHelpers'
/**
Creates a new document instance containing only the selected content
@param {Object} args object with `selection`
@return {Object} with a `doc` property that has a fresh doc with the copied content
*/
export default function copySelection(doc, selection) {
if (!selection) throw new Error("'selection' is mandatory.")
let copy = null
if (!selection.isNull() && !selection.isCollapsed()) {
// return a simplified version if only a piece of text is selected
if (selection.isPropertySelection()) {
copy = _copyPropertySelection(doc, selection)
}
else if (selection.isContainerSelection()) {
copy = _copyContainerSelection(doc, selection)
}
else if (selection.isNodeSelection()) {
copy = _copyNodeSelection(doc, selection)
}
else {
console.error('Copy is not yet supported for selection type.')
}
}
return copy
}
function _copyPropertySelection(doc, selection) {
let path = selection.start.path
let offset = selection.start.offset
let endOffset = selection.end.offset
let text = doc.get(path)
let snippet = doc.createSnippet()
let containerNode = snippet.getContainer()
snippet.create({
type: doc.schema.getDefaultTextType(),
id: Document.TEXT_SNIPPET_ID,
content: text.substring(offset, endOffset)
})
containerNode.show(Document.TEXT_SNIPPET_ID)
let annotations = doc.getIndex('annotations').get(path, offset, endOffset)
forEach(annotations, function(anno) {
let data = cloneDeep(anno.toJSON())
let path = [Document.TEXT_SNIPPET_ID, 'content']
data.start = {
path: path,
offset: Math.max(offset, anno.start.offset)-offset
}
data.end = {
path: path,
offset: Math.min(endOffset, anno.end.offset)-offset
}
snippet.create(data)
})
return snippet
}
function _copyContainerSelection(tx, sel) {
let snippet = tx.createSnippet()
let container = snippet.getContainer()
let nodeIds = sel.getNodeIds()
let L = nodeIds.length
if (L === 0) return snippet
let start = sel.start
let end = sel.end
let skippedFirst = false
let skippedLast = false
// First copy the whole covered nodes
let created = {}
for(let i = 0; i<L; i++) {
let id = nodeIds[i]
let node = tx.get(id)
// skip NIL selections, such as cursor at the end of first node or cursor at the start of last node.
if (i===0 && isLast(tx, start)) {
skippedFirst = true
continue
}
if (i===L-1 && isFirst(tx, end)) {
skippedLast = true
continue
}
if (!created[id]) {
documentHelpers.copyNode(node).forEach((nodeData) => {
let copy = snippet.create(nodeData)
created[copy.id] = true
})
container.show(id)
}
}
if (!skippedFirst) {
// ATTENTION: we need the root node here, e.g. the list, not the list items
let startNode = snippet.get(start.getNodeId()).getContainerRoot()
if (startNode.isText()) {
documentHelpers.deleteTextRange(snippet, null, start)
} else if (startNode.isList()) {
documentHelpers.deleteListRange(snippet, startNode, null, start)
}
}
if (!skippedLast) {
// ATTENTION: we need the root node here, e.g. the list, not the list items
let endNode = snippet.get(end.getNodeId()).getContainerRoot()
if (endNode.isText()) {
documentHelpers.deleteTextRange(snippet, end, null)
} else if (endNode.isList()) {
documentHelpers.deleteListRange(snippet, endNode, end, null)
}
}
return snippet
}
function _copyNodeSelection(doc, selection) {
let snippet = doc.createSnippet()
let containerNode = snippet.getContainer()
let nodeId = selection.getNodeId()
let node = doc.get(nodeId)
documentHelpers.copyNode(node).forEach((nodeData) => {
snippet.create(nodeData)
})
containerNode.show(node.id)
return snippet
}