UNPKG

12.3 kBSource Map (JSON)View Raw
1{"version":3,"file":"cursor.js","names":["EmbedBlot","Scope","TextBlot","Cursor","blotName","className","tagName","CONTENTS","value","undefined","constructor","scroll","domNode","selection","textNode","document","createTextNode","appendChild","savedLength","detach","parent","removeChild","format","name","target","index","statics","scope","BLOCK_BLOT","offset","length","optimize","formatAt","node","position","data","remove","restore","composing","range","getNativeRange","lastChild","parentNode","insertBefore","prevTextBlot","prev","prevTextLength","nextTextBlot","next","nextText","text","newText","split","join","mergedTextBlot","insertAt","newTextNode","create","remapOffset","start","end","startNode","startOffset","endNode","endOffset","update","mutations","context","some","mutation","type","isolate","unwrap"],"sources":["../../src/blots/cursor.ts"],"sourcesContent":["import { EmbedBlot, Scope } from 'parchment';\nimport type { Parent, ScrollBlot } from 'parchment';\nimport type Selection from '../core/selection.js';\nimport TextBlot from './text.js';\nimport type { EmbedContextRange } from './embed.js';\n\nclass Cursor extends EmbedBlot {\n static blotName = 'cursor';\n static className = 'ql-cursor';\n static tagName = 'span';\n static CONTENTS = '\\uFEFF'; // Zero width no break space\n\n static value() {\n return undefined;\n }\n\n selection: Selection;\n textNode: Text;\n savedLength: number;\n\n constructor(scroll: ScrollBlot, domNode: HTMLElement, selection: Selection) {\n super(scroll, domNode);\n this.selection = selection;\n this.textNode = document.createTextNode(Cursor.CONTENTS);\n this.domNode.appendChild(this.textNode);\n this.savedLength = 0;\n }\n\n detach() {\n // super.detach() will also clear domNode.__blot\n if (this.parent != null) this.parent.removeChild(this);\n }\n\n format(name: string, value: unknown) {\n if (this.savedLength !== 0) {\n super.format(name, value);\n return;\n }\n // TODO: Fix this next time the file is edited.\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n let target: Parent | this = this;\n let index = 0;\n while (target != null && target.statics.scope !== Scope.BLOCK_BLOT) {\n index += target.offset(target.parent);\n target = target.parent;\n }\n if (target != null) {\n this.savedLength = Cursor.CONTENTS.length;\n // @ts-expect-error TODO: allow empty context in Parchment\n target.optimize();\n target.formatAt(index, Cursor.CONTENTS.length, name, value);\n this.savedLength = 0;\n }\n }\n\n index(node: Node, offset: number) {\n if (node === this.textNode) return 0;\n return super.index(node, offset);\n }\n\n length() {\n return this.savedLength;\n }\n\n position(): [Text, number] {\n return [this.textNode, this.textNode.data.length];\n }\n\n remove() {\n super.remove();\n // @ts-expect-error Fix me later\n this.parent = null;\n }\n\n restore(): EmbedContextRange | null {\n if (this.selection.composing || this.parent == null) return null;\n const range = this.selection.getNativeRange();\n // Browser may push down styles/nodes inside the cursor blot.\n // https://dvcs.w3.org/hg/editing/raw-file/tip/editing.html#push-down-values\n while (\n this.domNode.lastChild != null &&\n this.domNode.lastChild !== this.textNode\n ) {\n // @ts-expect-error Fix me later\n this.domNode.parentNode.insertBefore(\n this.domNode.lastChild,\n this.domNode,\n );\n }\n\n const prevTextBlot = this.prev instanceof TextBlot ? this.prev : null;\n const prevTextLength = prevTextBlot ? prevTextBlot.length() : 0;\n const nextTextBlot = this.next instanceof TextBlot ? this.next : null;\n // @ts-expect-error TODO: make TextBlot.text public\n const nextText = nextTextBlot ? nextTextBlot.text : '';\n const { textNode } = this;\n // take text from inside this blot and reset it\n const newText = textNode.data.split(Cursor.CONTENTS).join('');\n textNode.data = Cursor.CONTENTS;\n\n // proactively merge TextBlots around cursor so that optimization\n // doesn't lose the cursor. the reason we are here in cursor.restore\n // could be that the user clicked in prevTextBlot or nextTextBlot, or\n // the user typed something.\n let mergedTextBlot;\n if (prevTextBlot) {\n mergedTextBlot = prevTextBlot;\n if (newText || nextTextBlot) {\n prevTextBlot.insertAt(prevTextBlot.length(), newText + nextText);\n if (nextTextBlot) {\n nextTextBlot.remove();\n }\n }\n } else if (nextTextBlot) {\n mergedTextBlot = nextTextBlot;\n nextTextBlot.insertAt(0, newText);\n } else {\n const newTextNode = document.createTextNode(newText);\n mergedTextBlot = this.scroll.create(newTextNode);\n this.parent.insertBefore(mergedTextBlot, this);\n }\n\n this.remove();\n if (range) {\n // calculate selection to restore\n const remapOffset = (node: Node, offset: number) => {\n if (prevTextBlot && node === prevTextBlot.domNode) {\n return offset;\n }\n if (node === textNode) {\n return prevTextLength + offset - 1;\n }\n if (nextTextBlot && node === nextTextBlot.domNode) {\n return prevTextLength + newText.length + offset;\n }\n return null;\n };\n\n const start = remapOffset(range.start.node, range.start.offset);\n const end = remapOffset(range.end.node, range.end.offset);\n if (start !== null && end !== null) {\n return {\n startNode: mergedTextBlot.domNode,\n startOffset: start,\n endNode: mergedTextBlot.domNode,\n endOffset: end,\n };\n }\n }\n return null;\n }\n\n update(mutations: MutationRecord[], context: Record<string, unknown>) {\n if (\n mutations.some((mutation) => {\n return (\n mutation.type === 'characterData' && mutation.target === this.textNode\n );\n })\n ) {\n const range = this.restore();\n if (range) context.range = range;\n }\n }\n\n // Avoid .ql-cursor being a descendant of `<a/>`.\n // The reason is Safari pushes down `<a/>` on text insertion.\n // That will cause DOM nodes not sync with the model.\n //\n // For example ({I} is the caret), given the markup:\n // <a><span class=\"ql-cursor\">\\uFEFF{I}</span></a>\n // When typing a char \"x\", `<a/>` will be pushed down inside the `<span>` first:\n // <span class=\"ql-cursor\"><a>\\uFEFF{I}</a></span>\n // And then \"x\" will be inserted after `<a/>`:\n // <span class=\"ql-cursor\"><a>\\uFEFF</a>d{I}</span>\n optimize(context?: unknown) {\n // @ts-expect-error Fix me later\n super.optimize(context);\n\n let { parent } = this;\n while (parent) {\n if (parent.domNode.tagName === 'A') {\n this.savedLength = Cursor.CONTENTS.length;\n // @ts-expect-error TODO: make isolate generic\n parent.isolate(this.offset(parent), this.length()).unwrap();\n this.savedLength = 0;\n break;\n }\n parent = parent.parent;\n }\n }\n\n value() {\n return '';\n }\n}\n\nexport default Cursor;\n"],"mappings":"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","ignoreList":[]}
\No newline at end of file