UNPKG

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 system. It is developed to power our online editing platform [Substance](http://substance.io).

176 lines (155 loc) 4.87 kB
import { documentHelpers } from '../model' import { isString } from '../util' export default class BasicEditorApi { constructor (archive, editorSession) { this.archive = archive this.editorSession = editorSession } extendWith (apiExtension) { apiExtension._register(this) } getEditorSession () { return this.editorSession } getDocument () { return this.editorSession.getDocument() } getRoot () { return this.getDocument().root } deleteNode (nodeId) { this.editorSession.transaction(tx => { const node = tx.get(nodeId) if (node) { if (node.isInlineNode()) { // Note: inline nodes are bound to a character within the text // in contrast to regular annotations, the text underneath an inline node // is owned by the inline node. // Deleting the node's character implicitly deletes the inline node tx.setSelection(node.getSelection()) tx.deleteSelection() } else { documentHelpers.deepDeleteNode(tx, nodeId) } } }) } removeAndDeleteNode (nodeId) { const node = this.editorSession.getDocument().get(nodeId, true) const parent = node.getParent() if (parent) { const { property: propertyName, pos } = node.getXpath() const property = parent.schema.getProperty(propertyName) this.editorSession.transaction(tx => { // remove the item from its parent if (property.isArray()) { documentHelpers.removeAt(tx, [parent.id, propertyName], pos) } else { tx.set([parent.id, propertyName], null) } documentHelpers.deepDeleteNode(tx, nodeId) tx.setSelection(null) }) } } removeItem (collectionPath, itemId) { this.editorSession.transaction(tx => { documentHelpers.removeFromCollection(tx, collectionPath, itemId) tx.setSelection(null) }) } updateNode (id, nodeData) { this.editorSession.transaction(tx => { const node = tx.get(id) node.assign(nodeData) }) } insertAnnotation (type, nodeData) { this.editorSession.transaction(tx => { tx.annotate(Object.assign({ type }, nodeData)) }) } moveNode (nodeId, direction) { const doc = this.getDocument() const node = doc.get(nodeId) const parent = node.getParent() if (!parent) throw new Error('Node does not have parent') const propertyName = node.getXpath().property this.moveItem([parent.id, propertyName], nodeId, direction) } moveItem (collectionPath, itemId, direction) { const doc = this.getDocument() const collection = doc.get(collectionPath) if (!collection) throw new Error('Collection does not exist') const pos = collection.indexOf(itemId) const diff = direction === 'up' ? -1 : +1 const insertPos = Math.min(collection.length - 1, Math.max(0, pos + diff)) if (insertPos !== pos) { this.editorSession.transaction(tx => { documentHelpers.removeAt(tx, collectionPath, pos) documentHelpers.insertAt(tx, collectionPath, insertPos, itemId) }) } } addNode (collectionPath, nodeData) { this.editorSession.transaction(tx => { const node = tx.create(nodeData) documentHelpers.append(tx, collectionPath, node.id) this._selectItem(tx, node) }) } insertNode (collectionPath, pos, nodeData, options = {}) { let newNodeId this.editorSession.transaction(tx => { const node = tx.create(nodeData) documentHelpers.insertAt(tx, collectionPath, pos, node.id) if (options.select !== false) { this._selectItem(tx, node) } newNodeId = node.id }) return this.editorSession.getDocument().get(newNodeId) } insertInlineNode (type, nodeData) { this.editorSession.transaction(tx => { tx.insertInlineNode(Object.assign({ type }, nodeData)) }) } selectItem (item) { if (isString(item)) { item = this.getDocument().get(item) } this._selectItem(this.editorSession, item) } selectInlineNode (inlineNode) { if (isString(inlineNode)) { inlineNode = this.getDocument().get(inlineNode) } this._selectInlineNode(this.editorSession, inlineNode) } renameAsset (assetId, newFilename) { const archive = this.archive const asset = archive.getAssetById(assetId) if (asset.filename !== newFilename) { this.archive.renameAsset(assetId, newFilename) } } _selectItem (tx, node) { tx.setSelection({ type: 'custom', customType: 'node', nodeId: node.id, data: { nodeType: node.type } }) } _selectInlineNode (tx, inlineNode) { tx.setSelection({ type: 'property', path: inlineNode.start.path, startOffset: inlineNode.start.offset, endOffset: inlineNode.end.offset }) } }