@b9g/revise 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +0,0 @@
1
- {"version":3,"file":"contentarea.cjs","sources":["../src/contentarea.ts"],"sourcesContent":["/// <reference lib=\"dom\" />\nimport {Edit} from \"./edit.js\";\n\n// TODO: custom newlines?\nconst NEWLINE = \"\\n\";\n\nexport interface ContentEventDetail {\n\tedit: Edit;\n\tsource: string | null;\n}\n\nexport interface ContentEventInit extends CustomEventInit<ContentEventDetail> {}\n\nexport class ContentEvent extends CustomEvent<ContentEventDetail> {\n\tconstructor(typeArg: string, eventInit: ContentEventInit) {\n\t\t// Maybe we should do some runtime eventInit validation.\n\t\tsuper(typeArg, {bubbles: true, ...eventInit});\n\t}\n}\n\nexport type SelectionDirection = \"forward\" | \"backward\" | \"none\";\n\n/********************************************/\n/*** ContentAreaElement private property symbols ***/\n/********************************************/\nconst _cache = Symbol.for(\"ContentAreaElement._cache\");\nconst _value = Symbol.for(\"ContentAreaElement._value\");\nconst _observer = Symbol.for(\"ContentAreaElement._observer\");\nconst _onselectionchange = Symbol.for(\"ContentAreaElement._onselectionchange\");\nconst _selectionStart = Symbol.for(\"ContentAreaElement._selectionStart\");\n\nexport class ContentAreaElement extends HTMLElement {\n\tdeclare [_cache]: NodeInfoCache;\n\tdeclare [_value]: string;\n\tdeclare [_observer]: MutationObserver;\n\tdeclare [_onselectionchange]: () => void;\n\tdeclare [_selectionStart]: number;\n\tconstructor() {\n\t\tsuper();\n\n\t\tthis[_cache] = new Map();\n\t\tthis[_value] = \"\";\n\t\tthis[_observer] = new MutationObserver((records) => {\n\t\t\tvalidate(this, records);\n\t\t});\n\n\t\tthis[_selectionStart] = 0;\n\t\tthis[_onselectionchange] = () => {\n\t\t\t// We keep track of the starting node offset pair to accurately diff\n\t\t\t// edits to text nodes.\n\t\t\tvalidate(this);\n\t\t\tthis[_selectionStart] = getSelectionRange(this).start;\n\t\t};\n\n\t\tthis.addEventListener(\"input\", () => {\n\t\t\t// This is necessary for Safari bugs where fast-repeating edits which\n\t\t\t// cause >40ms of execution cause the selection to lag and make pending\n\t\t\t// edits appear elsewhere in the DOM.\n\t\t\tvalidate(this);\n\t\t});\n\t}\n\n\t/******************************/\n\t/*** Custom Element methods ***/\n\t/******************************/\n\tconnectedCallback() {\n\t\tthis[_observer].observe(this, {\n\t\t\tsubtree: true,\n\t\t\tchildList: true,\n\t\t\tcharacterData: true,\n\t\t\tattributes: true,\n\t\t\tattributeFilter: [\n\t\t\t\t\"data-content\",\n\t\t\t\t// TODO: implement these attributes\n\t\t\t\t//\"data-contentbefore\",\n\t\t\t\t//\"data-contentafter\",\n\t\t\t],\n\t\t});\n\n\t\tvalidate(this);\n\t\tdocument.addEventListener(\n\t\t\t\"selectionchange\",\n\t\t\tthis[_onselectionchange],\n\t\t\t// We use capture in an attempt to run before other event listeners.\n\t\t\ttrue,\n\t\t);\n\t}\n\n\tdisconnectedCallback() {\n\t\tthis[_cache].clear();\n\t\tthis[_value] = \"\";\n\t\tthis[_observer].disconnect();\n\t\t// JSDOM-based environments like Jest sometimes make the global document\n\t\t// null before calling the disconnectedCallback for some reason.\n\t\tif (document) {\n\t\t\tdocument.removeEventListener(\n\t\t\t\t\"selectionchange\",\n\t\t\t\tthis[_onselectionchange],\n\t\t\t\ttrue,\n\t\t\t);\n\t\t}\n\t}\n\n\tget value(): string {\n\t\tvalidate(this);\n\t\treturn this[_value];\n\t}\n\n\tget selectionStart(): number {\n\t\tvalidate(this);\n\t\treturn getSelectionRange(this).start;\n\t}\n\n\tset selectionStart(start: number) {\n\t\tvalidate(this);\n\n\t\tconst {end, direction} = getSelectionRange(this);\n\t\tsetSelectionRange(this, {start, end, direction});\n\t}\n\n\tget selectionEnd(): number {\n\t\tvalidate(this);\n\t\treturn getSelectionRange(this).end;\n\t}\n\n\tset selectionEnd(end: number) {\n\t\tvalidate(this);\n\t\tconst {start, direction} = getSelectionRange(this);\n\t\tsetSelectionRange(this, {start, end, direction});\n\t}\n\n\tget selectionDirection(): SelectionDirection {\n\t\tvalidate(this);\n\t\treturn getSelectionRange(this).direction;\n\t}\n\n\tset selectionDirection(direction: SelectionDirection) {\n\t\tvalidate(this);\n\t\tconst {start, end} = getSelectionRange(this);\n\t\tsetSelectionRange(this, {start, end, direction});\n\t}\n\n\tsetSelectionRange(\n\t\tstart: number,\n\t\tend: number,\n\t\tdirection: SelectionDirection = \"none\",\n\t): void {\n\t\tvalidate(this);\n\t\tsetSelectionRange(this, {start, end, direction});\n\t}\n\n\tindexAt(node: Node | null, offset: number): number {\n\t\tvalidate(this);\n\t\treturn indexAt(this, node, offset);\n\t}\n\n\tnodeOffsetAt(index: number): [Node | null, number] {\n\t\tvalidate(this);\n\t\treturn nodeOffsetAt(this, index);\n\t}\n\n\tsource(source: string): boolean {\n\t\treturn validate(this, this[_observer].takeRecords(), source);\n\t}\n}\n\n/*** NodeInfo.flags ***/\n/** Whether the node is old. */\nconst IS_OLD = 1 << 0;\n/** Whether the node’s info is still up-to-date. */\nconst IS_VALID = 1 << 1;\n/** Whether the node has a styling of type display: block or similar. */\nconst IS_BLOCKLIKE = 1 << 2;\n/** Whether the node is responsible for the newline before it. */\nconst PREPENDS_NEWLINE = 1 << 3;\n/** Whether the node is responsible for the newline after it. */\nconst APPENDS_NEWLINE = 1 << 4;\n\n/** Data associated with the child nodes of a ContentAreaElement. */\nclass NodeInfo {\n\t// TODO: explain the relationship of these numbers to newline stuff\n\t/** The start of this node’s contents relative to the start of the parent. */\n\tdeclare offset: number;\n\t/** The string length of this node’s contents. */\n\tdeclare length: number;\n\t/** A bitmask (see flags above) */\n\tdeclare flags: number;\n\n\tconstructor(offset: number) {\n\t\tthis.offset = offset;\n\t\tthis.length = 0;\n\t\tthis.flags = 0;\n\t}\n}\n\n/** Each ContentAreaElement is associated with its own private cache. */\ntype NodeInfoCache = Map<Node, NodeInfo>;\n\n/**\n * Should be called before calling any ContentAreaElement methods.\n *\n * This function ensures the cache is up to date.\n *\n * Dispatches \"contentchange\" events.\n *\n * @returns whether a change was detected\n */\nfunction validate(\n\t_this: ContentAreaElement,\n\trecords: Array<MutationRecord> = _this[_observer].takeRecords(),\n\tsource: string | null = null,\n): boolean {\n\tif (typeof _this !== \"object\" || _this[_cache] == null) {\n\t\tthrow new TypeError(\"this is not a ContentAreaElement\");\n\t}\n\n\tif (!invalidate(_this, records)) {\n\t\treturn false;\n\t}\n\n\tconst oldValue = _this[_value];\n\tconst edit = diff(_this, oldValue, _this[_selectionStart]);\n\t_this[_value] = edit.apply(oldValue);\n\tconst ev = new ContentEvent(\"contentchange\", {detail: {edit, source}});\n\tPromise.resolve().then(() => _this.dispatchEvent(ev));\n\treturn true;\n}\n\nfunction invalidate(\n\t_this: ContentAreaElement,\n\trecords: Array<MutationRecord>,\n): boolean {\n\tconst cache = _this[_cache];\n\tif (!cache.get(_this)) {\n\t\t// The root ContentAreaElement will not be deleted from the cache until the\n\t\t// element is removed from the DOM, so this is the first time the\n\t\t// ContentAreaElement is being validated.\n\t\treturn true;\n\t}\n\n\tlet invalid = false;\n\tfor (let i = 0; i < records.length; i++) {\n\t\tconst record = records[i];\n\t\t// We make sure all added and removed nodes and their children are deleted\n\t\t// from the cache in case of any weirdness where nodes have been moved.\n\t\tfor (let j = 0; j < record.addedNodes.length; j++) {\n\t\t\tclear(record.addedNodes[j], cache);\n\t\t}\n\n\t\tfor (let j = 0; j < record.removedNodes.length; j++) {\n\t\t\tclear(record.removedNodes[j], cache);\n\t\t}\n\n\t\tlet node = record.target;\n\t\tif (node === _this) {\n\t\t\tinvalid = true;\n\t\t\tcontinue;\n\t\t} else if (!_this.contains(node)) {\n\t\t\tclear(node, cache);\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (; node !== _this; node = node.parentNode!) {\n\t\t\tif (!cache.has(node)) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst nodeInfo = cache.get(node);\n\t\t\tif (nodeInfo) {\n\t\t\t\tnodeInfo.flags &= ~IS_VALID;\n\t\t\t}\n\n\t\t\tinvalid = true;\n\t\t}\n\t}\n\n\tif (invalid) {\n\t\tconst nodeInfo = cache.get(_this)!;\n\t\tnodeInfo.flags &= ~IS_VALID;\n\t}\n\n\treturn invalid;\n}\n\n/**\n * For a given parent node and node info cache, clear the info for the node and\n * all of its child nodes from the cache.\n */\nfunction clear(parent: Node, cache: NodeInfoCache): void {\n\tconst walker = document.createTreeWalker(\n\t\tparent,\n\t\tNodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT,\n\t);\n\n\tfor (\n\t\tlet node: Node | null = parent;\n\t\tnode !== null;\n\t\tnode = walker.nextNode()\n\t) {\n\t\tcache.delete(node);\n\t}\n}\n\n// THIS IS THE MOST COMPLICATED FUNCTION IN THE LIBRARY!\n/**\n * This function both returns an edit which represents changes to the\n * ContentAreaElement, and populates the cache with info about nodes for future\n * reads.\n */\nfunction diff(\n\t_this: ContentAreaElement,\n\toldValue: string,\n\toldSelectionStart: number,\n): Edit {\n\tconst walker = document.createTreeWalker(\n\t\t_this,\n\t\tNodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT,\n\t);\n\n\tconst cache = _this[_cache];\n\tconst stack: Array<{nodeInfo: NodeInfo; oldIndexRelative: number}> = [];\n\tlet nodeInfo: NodeInfo;\n\tlet value = \"\";\n\tfor (\n\t\tlet node: Node = _this,\n\t\t\tdescending = true,\n\t\t\t/** the current offset relative to the parent */\n\t\t\toffset = 0,\n\t\t\t/** the index into the old string */\n\t\t\toldIndex = 0,\n\t\t\t/** the index into the old string of the parent */\n\t\t\toldIndexRelative = 0,\n\t\t\t/** Whether or not the value being built currently ends with a newline */\n\t\t\thasNewline = false;\n\t\t;\n\t\tnode = walker.currentNode\n\t) {\n\t\tif (descending) {\n\t\t\t// PRE-ORDER LOGIC\n\t\t\tnodeInfo = cache.get(node)!;\n\t\t\tif (nodeInfo === undefined) {\n\t\t\t\tcache.set(node, (nodeInfo = new NodeInfo(offset)));\n\t\t\t\tif (isBlocklikeElement(node)) {\n\t\t\t\t\tnodeInfo.flags |= IS_BLOCKLIKE;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst expectedOffset = oldIndex - oldIndexRelative;\n\t\t\t\tconst deleteLength = nodeInfo.offset - expectedOffset;\n\t\t\t\tif (deleteLength < 0) {\n\t\t\t\t\t// this should never happen\n\t\t\t\t\tthrow new Error(\"cache offset error\");\n\t\t\t\t} else if (deleteLength > 0) {\n\t\t\t\t\t// deletion detected\n\t\t\t\t\toldIndex += deleteLength;\n\t\t\t\t}\n\n\t\t\t\tnodeInfo.offset = offset;\n\t\t\t}\n\n\t\t\tif (offset && !hasNewline && nodeInfo.flags & IS_BLOCKLIKE) {\n\t\t\t\t// Block-like elements prepend a newline when they appear after text or\n\t\t\t\t// inline elements.\n\t\t\t\thasNewline = true;\n\t\t\t\toffset += NEWLINE.length;\n\t\t\t\tvalue += NEWLINE;\n\t\t\t\tif (nodeInfo.flags & PREPENDS_NEWLINE) {\n\t\t\t\t\toldIndex += NEWLINE.length;\n\t\t\t\t}\n\n\t\t\t\tnodeInfo.flags |= PREPENDS_NEWLINE;\n\t\t\t} else {\n\t\t\t\tif (nodeInfo.flags & PREPENDS_NEWLINE) {\n\t\t\t\t\t// deletion detected\n\t\t\t\t\toldIndex += NEWLINE.length;\n\t\t\t\t}\n\n\t\t\t\tnodeInfo.flags &= ~PREPENDS_NEWLINE;\n\t\t\t}\n\n\t\t\tdescending = false;\n\t\t\tif (nodeInfo.flags & IS_VALID) {\n\t\t\t\t// The node and its children are unchanged, so we read from the length.\n\t\t\t\tif (nodeInfo.length) {\n\t\t\t\t\tvalue += oldValue.slice(oldIndex, oldIndex + nodeInfo.length);\n\t\t\t\t\toldIndex += nodeInfo.length;\n\t\t\t\t\toffset += nodeInfo.length;\n\t\t\t\t\thasNewline =\n\t\t\t\t\t\toldValue.slice(Math.max(0, oldIndex - NEWLINE.length), oldIndex) ===\n\t\t\t\t\t\tNEWLINE;\n\t\t\t\t}\n\t\t\t} else if (node.nodeType === Node.TEXT_NODE) {\n\t\t\t\tconst text = (node as Text).data;\n\t\t\t\tif (text.length) {\n\t\t\t\t\tvalue += text;\n\t\t\t\t\toffset += text.length;\n\t\t\t\t\thasNewline = text.endsWith(NEWLINE);\n\t\t\t\t}\n\n\t\t\t\tif (nodeInfo.flags & IS_OLD) {\n\t\t\t\t\toldIndex += nodeInfo.length;\n\t\t\t\t}\n\t\t\t} else if ((node as Element).hasAttribute(\"data-content\")) {\n\t\t\t\tconst text = (node as Element).getAttribute(\"data-content\") || \"\";\n\t\t\t\tif (text.length) {\n\t\t\t\t\tvalue += text;\n\t\t\t\t\toffset += text.length;\n\t\t\t\t\thasNewline = text.endsWith(NEWLINE);\n\t\t\t\t}\n\n\t\t\t\tif (nodeInfo.flags & IS_OLD) {\n\t\t\t\t\toldIndex += nodeInfo.length;\n\t\t\t\t}\n\t\t\t} else if (node.nodeName === \"BR\") {\n\t\t\t\tvalue += NEWLINE;\n\t\t\t\toffset += NEWLINE.length;\n\t\t\t\thasNewline = true;\n\t\t\t\tif (nodeInfo.flags & IS_OLD) {\n\t\t\t\t\toldIndex += nodeInfo.length;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tdescending = !!walker.firstChild();\n\t\t\t\tif (descending) {\n\t\t\t\t\tstack.push({nodeInfo, oldIndexRelative});\n\t\t\t\t\toffset = 0;\n\t\t\t\t\toldIndexRelative = oldIndex;\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif (!stack.length) {\n\t\t\t\t// This should never happen.\n\t\t\t\tthrow new Error(\"Stack is empty\");\n\t\t\t}\n\n\t\t\t// If the child node prepends a newline, add to offset to increase the\n\t\t\t// length of the parent node.\n\t\t\tif (nodeInfo!.flags & PREPENDS_NEWLINE) {\n\t\t\t\toffset += NEWLINE.length;\n\t\t\t}\n\n\t\t\t({nodeInfo, oldIndexRelative} = stack.pop()!);\n\t\t\toffset = nodeInfo.offset + offset;\n\t\t}\n\n\t\tif (!descending) {\n\t\t\t// POST-ORDER LOGIC\n\t\t\tif (!(nodeInfo.flags & IS_VALID)) {\n\t\t\t\t// TODO: Figure out if we should always recalculate APPENDS_NEWLINE???\n\t\t\t\tif (!hasNewline && nodeInfo.flags & IS_BLOCKLIKE) {\n\t\t\t\t\tvalue += NEWLINE;\n\t\t\t\t\toffset += NEWLINE.length;\n\t\t\t\t\thasNewline = true;\n\t\t\t\t\tnodeInfo.flags |= APPENDS_NEWLINE;\n\t\t\t\t} else {\n\t\t\t\t\tnodeInfo.flags &= ~APPENDS_NEWLINE;\n\t\t\t\t}\n\n\t\t\t\tnodeInfo.length = offset - nodeInfo.offset;\n\t\t\t\tnodeInfo.flags |= IS_VALID;\n\t\t\t}\n\n\t\t\tnodeInfo.flags |= IS_OLD;\n\n\t\t\tdescending = !!walker.nextSibling();\n\t\t\tif (!descending) {\n\t\t\t\tif (walker.currentNode === _this) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\twalker.parentNode();\n\t\t\t}\n\t\t}\n\n\t\tif (oldIndex > oldValue.length) {\n\t\t\t// This should never happen.\n\t\t\tthrow new Error(\"cache length error\");\n\t\t}\n\t}\n\n\tconst selectionStart = getSelectionRange(_this).start;\n\t// TODO: Doing a diff over the entirety of both oldValue and value is a\n\t// performance bottleneck. Figure out how to reduce the search for changed\n\t// values.\n\treturn Edit.diff(\n\t\toldValue,\n\t\tvalue,\n\t\tMath.min(oldSelectionStart, selectionStart),\n\t);\n}\n\nfunction getStartNodeOffset(): [Node | null, number] {\n\tconst selection = document.getSelection();\n\tif (selection && selection.rangeCount) {\n\t\tconst range = selection.getRangeAt(0);\n\t\treturn [range.startContainer, range.startOffset];\n\t}\n\n\treturn [null, 0];\n}\n\nconst BLOCKLIKE_DISPLAYS = new Set([\n\t\"block\",\n\t\"flex\",\n\t\"grid\",\n\t\"flow-root\",\n\t\"list-item\",\n\t\"table\",\n\t\"table-row-group\",\n\t\"table-header-group\",\n\t\"table-footer-group\",\n\t\"table-row\",\n\t\"table-caption\",\n]);\n\nfunction isBlocklikeElement(node: Node): node is Element {\n\treturn (\n\t\tnode.nodeType === Node.ELEMENT_NODE &&\n\t\tBLOCKLIKE_DISPLAYS.has(\n\t\t\t// handle two-value display syntax like `display: block flex`\n\t\t\tgetComputedStyle(node as Element).display.split(\" \")[0],\n\t\t)\n\t);\n}\n\n/***********************/\n/*** Selection Logic ***/\n/***********************/\n/**\n * Finds the string index of a node and offset pair provided by a browser API\n * like document.getSelection() for a given root and cache.\n */\nfunction indexAt(\n\t_this: ContentAreaElement,\n\tnode: Node | null,\n\toffset: number,\n): number {\n\tconst cache = _this[_cache];\n\tif (node == null || !_this.contains(node)) {\n\t\treturn -1;\n\t}\n\n\tif (!cache.has(node)) {\n\t\t// If the node is not found in the cache but is contained in the root, then\n\t\t// it is the child of an element with a data-content attribute.\n\t\toffset = 0;\n\t\twhile (!cache.has(node)) {\n\t\t\tnode = node.parentNode!;\n\t\t}\n\t}\n\n\tlet index: number;\n\tif (node.nodeType === Node.TEXT_NODE) {\n\t\tconst nodeInfo = cache.get(node)!;\n\t\tindex = offset + nodeInfo.offset;\n\t\tnode = node.parentNode!;\n\t} else {\n\t\tif (offset <= 0) {\n\t\t\tindex = 0;\n\t\t} else if (offset >= node.childNodes.length) {\n\t\t\tconst nodeInfo = cache.get(node)!;\n\t\t\tindex =\n\t\t\t\tnodeInfo.flags & APPENDS_NEWLINE\n\t\t\t\t\t? nodeInfo.length - NEWLINE.length\n\t\t\t\t\t: nodeInfo.length;\n\t\t} else {\n\t\t\tlet child: Node | null = node.childNodes[offset];\n\t\t\twhile (child !== null && !cache.has(child)) {\n\t\t\t\tchild = child.previousSibling;\n\t\t\t}\n\n\t\t\tif (child === null) {\n\t\t\t\tindex = 0;\n\t\t\t} else {\n\t\t\t\tnode = child;\n\t\t\t\tconst nodeInfo = cache.get(node)!;\n\t\t\t\t// If the offset references an element which prepends a newline\n\t\t\t\t// (\"hello<div>world</div>\"), we have to start from -1 because the\n\t\t\t\t// element’s info.offset will not account for the newline.\n\t\t\t\tindex = nodeInfo.flags & PREPENDS_NEWLINE ? -1 : 0;\n\t\t\t}\n\t\t}\n\t}\n\n\tfor (; node !== _this; node = node.parentNode!) {\n\t\tconst nodeInfo = cache.get(node)!;\n\t\tindex += nodeInfo.offset;\n\t\tif (nodeInfo.flags & PREPENDS_NEWLINE) {\n\t\t\tindex += NEWLINE.length;\n\t\t}\n\t}\n\n\treturn index;\n}\n\n/**\n * Finds the node and offset pair to use with browser APIs like\n * selection.collapse() from a given string index.\n */\nfunction nodeOffsetAt(\n\t_this: ContentAreaElement,\n\tindex: number,\n): [Node | null, number] {\n\tif (index < 0) {\n\t\treturn [null, 0];\n\t}\n\n\tconst [node, offset] = findNodeOffset(_this, index);\n\tif (node && node.nodeName === \"BR\") {\n\t\t// Some browsers seem to have trouble when calling `selection.collapse()`\n\t\t// with a BR element, so we try to avoid returning them from this function.\n\t\treturn nodeOffsetFromChild(node);\n\t}\n\n\treturn [node, offset];\n}\n\n// TODO: Can this function be inlined?\nfunction findNodeOffset(\n\t_this: ContentAreaElement,\n\tindex: number,\n): [Node | null, number] {\n\tconst cache = _this[_cache];\n\tconst walker = document.createTreeWalker(\n\t\t_this,\n\t\tNodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT,\n\t);\n\n\tfor (let node: Node | null = _this; node !== null; ) {\n\t\tconst nodeInfo = cache.get(node);\n\t\tif (nodeInfo == null) {\n\t\t\treturn nodeOffsetFromChild(node, index > 0);\n\t\t}\n\n\t\tif (nodeInfo.flags & PREPENDS_NEWLINE) {\n\t\t\tindex -= 1;\n\t\t}\n\n\t\tif (index === nodeInfo.length && node.nodeType === Node.TEXT_NODE) {\n\t\t\treturn [node, (node as Text).data.length];\n\t\t} else if (index >= nodeInfo.length) {\n\t\t\tindex -= nodeInfo.length;\n\t\t\tconst nextSibling = walker.nextSibling();\n\t\t\tif (nextSibling === null) {\n\t\t\t\t// This branch seems necessary mainly when working with data-content\n\t\t\t\t// nodes.\n\t\t\t\tif (node === _this) {\n\t\t\t\t\treturn [node, getNodeLength(node)];\n\t\t\t\t}\n\n\t\t\t\treturn nodeOffsetFromChild(walker.currentNode, true);\n\t\t\t}\n\n\t\t\tnode = nextSibling;\n\t\t} else {\n\t\t\tif (\n\t\t\t\tnode.nodeType === Node.ELEMENT_NODE &&\n\t\t\t\t(node as Element).hasAttribute(\"data-content\")\n\t\t\t) {\n\t\t\t\treturn nodeOffsetFromChild(node, index > 0);\n\t\t\t}\n\n\t\t\tconst firstChild = walker.firstChild();\n\t\t\tif (firstChild === null) {\n\t\t\t\tconst offset =\n\t\t\t\t\tnode.nodeType === Node.TEXT_NODE ? index : index > 0 ? 1 : 0;\n\t\t\t\treturn [node, offset];\n\t\t\t} else {\n\t\t\t\tnode = firstChild;\n\t\t\t}\n\t\t}\n\t}\n\n\tconst node = walker.currentNode;\n\treturn [node, getNodeLength(node)];\n}\n\nfunction getNodeLength(node: Node) {\n\tif (node.nodeType === Node.TEXT_NODE) {\n\t\treturn (node as Text).data.length;\n\t}\n\n\treturn node.childNodes.length;\n}\n\nfunction nodeOffsetFromChild(\n\tnode: Node,\n\tafter: boolean = false,\n): [Node | null, number] {\n\tconst parentNode = node.parentNode;\n\tif (parentNode === null) {\n\t\treturn [null, 0];\n\t}\n\n\tlet offset = Array.from(parentNode.childNodes).indexOf(node as ChildNode);\n\tif (after) {\n\t\toffset++;\n\t}\n\n\treturn [parentNode, offset];\n}\n\ninterface SelectionRange {\n\tstart: number;\n\tend: number;\n\tdirection: SelectionDirection;\n}\n\nfunction getSelectionRange(_this: ContentAreaElement): SelectionRange {\n\tconst selection = document.getSelection();\n\tif (!selection) {\n\t\treturn {start: 0, end: 0, direction: \"none\"};\n\t}\n\n\tconst {\n\t\tfocusNode,\n\t\tfocusOffset,\n\t\tanchorNode,\n\t\tanchorOffset,\n\t\tisCollapsed,\n\t} = selection;\n\tconst focus = Math.max(0, indexAt(_this, focusNode, focusOffset));\n\tconst anchor = isCollapsed\n\t\t? focus\n\t\t: Math.max(0, indexAt(_this, anchorNode, anchorOffset));\n\treturn {\n\t\tstart: Math.min(focus, anchor),\n\t\tend: Math.max(focus, anchor),\n\t\tdirection:\n\t\t\tfocus < anchor ? \"backward\" : focus > anchor ? \"forward\" : \"none\",\n\t};\n}\n\nfunction setSelectionRange(\n\t_this: ContentAreaElement,\n\t{start, end, direction}: SelectionRange,\n): void {\n\tconst selection = document.getSelection();\n\tif (!selection) {\n\t\treturn;\n\t}\n\n\tstart = Math.max(0, start || 0);\n\tend = Math.max(0, end || 0);\n\tif (end < start) {\n\t\tstart = end;\n\t}\n\n\t// Focus is the side of the selection where the pointer is released.\n\tconst [focus, anchor] =\n\t\tdirection === \"backward\" ? [start, end] : [end, start];\n\n\tif (focus === anchor) {\n\t\tconst [node, offset] = nodeOffsetAt(_this, focus);\n\t\tselection.collapse(node, offset);\n\t} else {\n\t\tconst [anchorNode, anchorOffset] = nodeOffsetAt(_this, anchor);\n\t\tconst [focusNode, focusOffset] = nodeOffsetAt(_this, focus);\n\t\tif (anchorNode === null && focusNode === null) {\n\t\t\tselection.collapse(null);\n\t\t} else if (anchorNode === null) {\n\t\t\tselection.collapse(focusNode, focusOffset);\n\t\t} else if (focusNode === null) {\n\t\t\tselection.collapse(anchorNode, anchorOffset);\n\t\t} else {\n\t\t\t// This method is not implemented in IE.\n\t\t\tselection.setBaseAndExtent(\n\t\t\t\tanchorNode,\n\t\t\t\tanchorOffset,\n\t\t\t\tfocusNode,\n\t\t\t\tfocusOffset,\n\t\t\t);\n\t\t}\n\t}\n}\n"],"names":["Edit"],"mappings":"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"}
package/contentarea.js DELETED
@@ -1,593 +0,0 @@
1
- /// <reference types="contentarea.d.ts" />
2
- import { Edit } from './edit.js';
3
-
4
- /// <reference lib="dom" />
5
- // TODO: custom newlines?
6
- const NEWLINE = "\n";
7
- class ContentEvent extends CustomEvent {
8
- constructor(typeArg, eventInit) {
9
- // Maybe we should do some runtime eventInit validation.
10
- super(typeArg, { bubbles: true, ...eventInit });
11
- }
12
- }
13
- /********************************************/
14
- /*** ContentAreaElement private property symbols ***/
15
- /********************************************/
16
- const _cache = Symbol.for("ContentAreaElement._cache");
17
- const _value = Symbol.for("ContentAreaElement._value");
18
- const _observer = Symbol.for("ContentAreaElement._observer");
19
- const _onselectionchange = Symbol.for("ContentAreaElement._onselectionchange");
20
- const _selectionStart = Symbol.for("ContentAreaElement._selectionStart");
21
- class ContentAreaElement extends HTMLElement {
22
- constructor() {
23
- super();
24
- this[_cache] = new Map();
25
- this[_value] = "";
26
- this[_observer] = new MutationObserver((records) => {
27
- validate(this, records);
28
- });
29
- this[_selectionStart] = 0;
30
- this[_onselectionchange] = () => {
31
- // We keep track of the starting node offset pair to accurately diff
32
- // edits to text nodes.
33
- validate(this);
34
- this[_selectionStart] = getSelectionRange(this).start;
35
- };
36
- this.addEventListener("input", () => {
37
- // This is necessary for Safari bugs where fast-repeating edits which
38
- // cause >40ms of execution cause the selection to lag and make pending
39
- // edits appear elsewhere in the DOM.
40
- validate(this);
41
- });
42
- }
43
- /******************************/
44
- /*** Custom Element methods ***/
45
- /******************************/
46
- connectedCallback() {
47
- this[_observer].observe(this, {
48
- subtree: true,
49
- childList: true,
50
- characterData: true,
51
- attributes: true,
52
- attributeFilter: [
53
- "data-content",
54
- // TODO: implement these attributes
55
- //"data-contentbefore",
56
- //"data-contentafter",
57
- ],
58
- });
59
- validate(this);
60
- document.addEventListener("selectionchange", this[_onselectionchange],
61
- // We use capture in an attempt to run before other event listeners.
62
- true);
63
- }
64
- disconnectedCallback() {
65
- this[_cache].clear();
66
- this[_value] = "";
67
- this[_observer].disconnect();
68
- // JSDOM-based environments like Jest sometimes make the global document
69
- // null before calling the disconnectedCallback for some reason.
70
- if (document) {
71
- document.removeEventListener("selectionchange", this[_onselectionchange], true);
72
- }
73
- }
74
- get value() {
75
- validate(this);
76
- return this[_value];
77
- }
78
- get selectionStart() {
79
- validate(this);
80
- return getSelectionRange(this).start;
81
- }
82
- set selectionStart(start) {
83
- validate(this);
84
- const { end, direction } = getSelectionRange(this);
85
- setSelectionRange(this, { start, end, direction });
86
- }
87
- get selectionEnd() {
88
- validate(this);
89
- return getSelectionRange(this).end;
90
- }
91
- set selectionEnd(end) {
92
- validate(this);
93
- const { start, direction } = getSelectionRange(this);
94
- setSelectionRange(this, { start, end, direction });
95
- }
96
- get selectionDirection() {
97
- validate(this);
98
- return getSelectionRange(this).direction;
99
- }
100
- set selectionDirection(direction) {
101
- validate(this);
102
- const { start, end } = getSelectionRange(this);
103
- setSelectionRange(this, { start, end, direction });
104
- }
105
- setSelectionRange(start, end, direction = "none") {
106
- validate(this);
107
- setSelectionRange(this, { start, end, direction });
108
- }
109
- indexAt(node, offset) {
110
- validate(this);
111
- return indexAt(this, node, offset);
112
- }
113
- nodeOffsetAt(index) {
114
- validate(this);
115
- return nodeOffsetAt(this, index);
116
- }
117
- source(source) {
118
- return validate(this, this[_observer].takeRecords(), source);
119
- }
120
- }
121
- /*** NodeInfo.flags ***/
122
- /** Whether the node is old. */
123
- const IS_OLD = 1 << 0;
124
- /** Whether the node’s info is still up-to-date. */
125
- const IS_VALID = 1 << 1;
126
- /** Whether the node has a styling of type display: block or similar. */
127
- const IS_BLOCKLIKE = 1 << 2;
128
- /** Whether the node is responsible for the newline before it. */
129
- const PREPENDS_NEWLINE = 1 << 3;
130
- /** Whether the node is responsible for the newline after it. */
131
- const APPENDS_NEWLINE = 1 << 4;
132
- /** Data associated with the child nodes of a ContentAreaElement. */
133
- class NodeInfo {
134
- constructor(offset) {
135
- this.offset = offset;
136
- this.length = 0;
137
- this.flags = 0;
138
- }
139
- }
140
- /**
141
- * Should be called before calling any ContentAreaElement methods.
142
- *
143
- * This function ensures the cache is up to date.
144
- *
145
- * Dispatches "contentchange" events.
146
- *
147
- * @returns whether a change was detected
148
- */
149
- function validate(_this, records = _this[_observer].takeRecords(), source = null) {
150
- if (typeof _this !== "object" || _this[_cache] == null) {
151
- throw new TypeError("this is not a ContentAreaElement");
152
- }
153
- if (!invalidate(_this, records)) {
154
- return false;
155
- }
156
- const oldValue = _this[_value];
157
- const edit = diff(_this, oldValue, _this[_selectionStart]);
158
- _this[_value] = edit.apply(oldValue);
159
- const ev = new ContentEvent("contentchange", { detail: { edit, source } });
160
- Promise.resolve().then(() => _this.dispatchEvent(ev));
161
- return true;
162
- }
163
- function invalidate(_this, records) {
164
- const cache = _this[_cache];
165
- if (!cache.get(_this)) {
166
- // The root ContentAreaElement will not be deleted from the cache until the
167
- // element is removed from the DOM, so this is the first time the
168
- // ContentAreaElement is being validated.
169
- return true;
170
- }
171
- let invalid = false;
172
- for (let i = 0; i < records.length; i++) {
173
- const record = records[i];
174
- // We make sure all added and removed nodes and their children are deleted
175
- // from the cache in case of any weirdness where nodes have been moved.
176
- for (let j = 0; j < record.addedNodes.length; j++) {
177
- clear(record.addedNodes[j], cache);
178
- }
179
- for (let j = 0; j < record.removedNodes.length; j++) {
180
- clear(record.removedNodes[j], cache);
181
- }
182
- let node = record.target;
183
- if (node === _this) {
184
- invalid = true;
185
- continue;
186
- }
187
- else if (!_this.contains(node)) {
188
- clear(node, cache);
189
- continue;
190
- }
191
- for (; node !== _this; node = node.parentNode) {
192
- if (!cache.has(node)) {
193
- break;
194
- }
195
- const nodeInfo = cache.get(node);
196
- if (nodeInfo) {
197
- nodeInfo.flags &= ~IS_VALID;
198
- }
199
- invalid = true;
200
- }
201
- }
202
- if (invalid) {
203
- const nodeInfo = cache.get(_this);
204
- nodeInfo.flags &= ~IS_VALID;
205
- }
206
- return invalid;
207
- }
208
- /**
209
- * For a given parent node and node info cache, clear the info for the node and
210
- * all of its child nodes from the cache.
211
- */
212
- function clear(parent, cache) {
213
- const walker = document.createTreeWalker(parent, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT);
214
- for (let node = parent; node !== null; node = walker.nextNode()) {
215
- cache.delete(node);
216
- }
217
- }
218
- // THIS IS THE MOST COMPLICATED FUNCTION IN THE LIBRARY!
219
- /**
220
- * This function both returns an edit which represents changes to the
221
- * ContentAreaElement, and populates the cache with info about nodes for future
222
- * reads.
223
- */
224
- function diff(_this, oldValue, oldSelectionStart) {
225
- const walker = document.createTreeWalker(_this, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT);
226
- const cache = _this[_cache];
227
- const stack = [];
228
- let nodeInfo;
229
- let value = "";
230
- for (let node = _this, descending = true,
231
- /** the current offset relative to the parent */
232
- offset = 0,
233
- /** the index into the old string */
234
- oldIndex = 0,
235
- /** the index into the old string of the parent */
236
- oldIndexRelative = 0,
237
- /** Whether or not the value being built currently ends with a newline */
238
- hasNewline = false;; node = walker.currentNode) {
239
- if (descending) {
240
- // PRE-ORDER LOGIC
241
- nodeInfo = cache.get(node);
242
- if (nodeInfo === undefined) {
243
- cache.set(node, (nodeInfo = new NodeInfo(offset)));
244
- if (isBlocklikeElement(node)) {
245
- nodeInfo.flags |= IS_BLOCKLIKE;
246
- }
247
- }
248
- else {
249
- const expectedOffset = oldIndex - oldIndexRelative;
250
- const deleteLength = nodeInfo.offset - expectedOffset;
251
- if (deleteLength < 0) {
252
- // this should never happen
253
- throw new Error("cache offset error");
254
- }
255
- else if (deleteLength > 0) {
256
- // deletion detected
257
- oldIndex += deleteLength;
258
- }
259
- nodeInfo.offset = offset;
260
- }
261
- if (offset && !hasNewline && nodeInfo.flags & IS_BLOCKLIKE) {
262
- // Block-like elements prepend a newline when they appear after text or
263
- // inline elements.
264
- hasNewline = true;
265
- offset += NEWLINE.length;
266
- value += NEWLINE;
267
- if (nodeInfo.flags & PREPENDS_NEWLINE) {
268
- oldIndex += NEWLINE.length;
269
- }
270
- nodeInfo.flags |= PREPENDS_NEWLINE;
271
- }
272
- else {
273
- if (nodeInfo.flags & PREPENDS_NEWLINE) {
274
- // deletion detected
275
- oldIndex += NEWLINE.length;
276
- }
277
- nodeInfo.flags &= ~PREPENDS_NEWLINE;
278
- }
279
- descending = false;
280
- if (nodeInfo.flags & IS_VALID) {
281
- // The node and its children are unchanged, so we read from the length.
282
- if (nodeInfo.length) {
283
- value += oldValue.slice(oldIndex, oldIndex + nodeInfo.length);
284
- oldIndex += nodeInfo.length;
285
- offset += nodeInfo.length;
286
- hasNewline =
287
- oldValue.slice(Math.max(0, oldIndex - NEWLINE.length), oldIndex) ===
288
- NEWLINE;
289
- }
290
- }
291
- else if (node.nodeType === Node.TEXT_NODE) {
292
- const text = node.data;
293
- if (text.length) {
294
- value += text;
295
- offset += text.length;
296
- hasNewline = text.endsWith(NEWLINE);
297
- }
298
- if (nodeInfo.flags & IS_OLD) {
299
- oldIndex += nodeInfo.length;
300
- }
301
- }
302
- else if (node.hasAttribute("data-content")) {
303
- const text = node.getAttribute("data-content") || "";
304
- if (text.length) {
305
- value += text;
306
- offset += text.length;
307
- hasNewline = text.endsWith(NEWLINE);
308
- }
309
- if (nodeInfo.flags & IS_OLD) {
310
- oldIndex += nodeInfo.length;
311
- }
312
- }
313
- else if (node.nodeName === "BR") {
314
- value += NEWLINE;
315
- offset += NEWLINE.length;
316
- hasNewline = true;
317
- if (nodeInfo.flags & IS_OLD) {
318
- oldIndex += nodeInfo.length;
319
- }
320
- }
321
- else {
322
- descending = !!walker.firstChild();
323
- if (descending) {
324
- stack.push({ nodeInfo, oldIndexRelative });
325
- offset = 0;
326
- oldIndexRelative = oldIndex;
327
- }
328
- }
329
- }
330
- else {
331
- if (!stack.length) {
332
- // This should never happen.
333
- throw new Error("Stack is empty");
334
- }
335
- // If the child node prepends a newline, add to offset to increase the
336
- // length of the parent node.
337
- if (nodeInfo.flags & PREPENDS_NEWLINE) {
338
- offset += NEWLINE.length;
339
- }
340
- ({ nodeInfo, oldIndexRelative } = stack.pop());
341
- offset = nodeInfo.offset + offset;
342
- }
343
- if (!descending) {
344
- // POST-ORDER LOGIC
345
- if (!(nodeInfo.flags & IS_VALID)) {
346
- // TODO: Figure out if we should always recalculate APPENDS_NEWLINE???
347
- if (!hasNewline && nodeInfo.flags & IS_BLOCKLIKE) {
348
- value += NEWLINE;
349
- offset += NEWLINE.length;
350
- hasNewline = true;
351
- nodeInfo.flags |= APPENDS_NEWLINE;
352
- }
353
- else {
354
- nodeInfo.flags &= ~APPENDS_NEWLINE;
355
- }
356
- nodeInfo.length = offset - nodeInfo.offset;
357
- nodeInfo.flags |= IS_VALID;
358
- }
359
- nodeInfo.flags |= IS_OLD;
360
- descending = !!walker.nextSibling();
361
- if (!descending) {
362
- if (walker.currentNode === _this) {
363
- break;
364
- }
365
- walker.parentNode();
366
- }
367
- }
368
- if (oldIndex > oldValue.length) {
369
- // This should never happen.
370
- throw new Error("cache length error");
371
- }
372
- }
373
- const selectionStart = getSelectionRange(_this).start;
374
- // TODO: Doing a diff over the entirety of both oldValue and value is a
375
- // performance bottleneck. Figure out how to reduce the search for changed
376
- // values.
377
- return Edit.diff(oldValue, value, Math.min(oldSelectionStart, selectionStart));
378
- }
379
- const BLOCKLIKE_DISPLAYS = new Set([
380
- "block",
381
- "flex",
382
- "grid",
383
- "flow-root",
384
- "list-item",
385
- "table",
386
- "table-row-group",
387
- "table-header-group",
388
- "table-footer-group",
389
- "table-row",
390
- "table-caption",
391
- ]);
392
- function isBlocklikeElement(node) {
393
- return (node.nodeType === Node.ELEMENT_NODE &&
394
- BLOCKLIKE_DISPLAYS.has(
395
- // handle two-value display syntax like `display: block flex`
396
- getComputedStyle(node).display.split(" ")[0]));
397
- }
398
- /***********************/
399
- /*** Selection Logic ***/
400
- /***********************/
401
- /**
402
- * Finds the string index of a node and offset pair provided by a browser API
403
- * like document.getSelection() for a given root and cache.
404
- */
405
- function indexAt(_this, node, offset) {
406
- const cache = _this[_cache];
407
- if (node == null || !_this.contains(node)) {
408
- return -1;
409
- }
410
- if (!cache.has(node)) {
411
- // If the node is not found in the cache but is contained in the root, then
412
- // it is the child of an element with a data-content attribute.
413
- offset = 0;
414
- while (!cache.has(node)) {
415
- node = node.parentNode;
416
- }
417
- }
418
- let index;
419
- if (node.nodeType === Node.TEXT_NODE) {
420
- const nodeInfo = cache.get(node);
421
- index = offset + nodeInfo.offset;
422
- node = node.parentNode;
423
- }
424
- else {
425
- if (offset <= 0) {
426
- index = 0;
427
- }
428
- else if (offset >= node.childNodes.length) {
429
- const nodeInfo = cache.get(node);
430
- index =
431
- nodeInfo.flags & APPENDS_NEWLINE
432
- ? nodeInfo.length - NEWLINE.length
433
- : nodeInfo.length;
434
- }
435
- else {
436
- let child = node.childNodes[offset];
437
- while (child !== null && !cache.has(child)) {
438
- child = child.previousSibling;
439
- }
440
- if (child === null) {
441
- index = 0;
442
- }
443
- else {
444
- node = child;
445
- const nodeInfo = cache.get(node);
446
- // If the offset references an element which prepends a newline
447
- // ("hello<div>world</div>"), we have to start from -1 because the
448
- // element’s info.offset will not account for the newline.
449
- index = nodeInfo.flags & PREPENDS_NEWLINE ? -1 : 0;
450
- }
451
- }
452
- }
453
- for (; node !== _this; node = node.parentNode) {
454
- const nodeInfo = cache.get(node);
455
- index += nodeInfo.offset;
456
- if (nodeInfo.flags & PREPENDS_NEWLINE) {
457
- index += NEWLINE.length;
458
- }
459
- }
460
- return index;
461
- }
462
- /**
463
- * Finds the node and offset pair to use with browser APIs like
464
- * selection.collapse() from a given string index.
465
- */
466
- function nodeOffsetAt(_this, index) {
467
- if (index < 0) {
468
- return [null, 0];
469
- }
470
- const [node, offset] = findNodeOffset(_this, index);
471
- if (node && node.nodeName === "BR") {
472
- // Some browsers seem to have trouble when calling `selection.collapse()`
473
- // with a BR element, so we try to avoid returning them from this function.
474
- return nodeOffsetFromChild(node);
475
- }
476
- return [node, offset];
477
- }
478
- // TODO: Can this function be inlined?
479
- function findNodeOffset(_this, index) {
480
- const cache = _this[_cache];
481
- const walker = document.createTreeWalker(_this, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT);
482
- for (let node = _this; node !== null;) {
483
- const nodeInfo = cache.get(node);
484
- if (nodeInfo == null) {
485
- return nodeOffsetFromChild(node, index > 0);
486
- }
487
- if (nodeInfo.flags & PREPENDS_NEWLINE) {
488
- index -= 1;
489
- }
490
- if (index === nodeInfo.length && node.nodeType === Node.TEXT_NODE) {
491
- return [node, node.data.length];
492
- }
493
- else if (index >= nodeInfo.length) {
494
- index -= nodeInfo.length;
495
- const nextSibling = walker.nextSibling();
496
- if (nextSibling === null) {
497
- // This branch seems necessary mainly when working with data-content
498
- // nodes.
499
- if (node === _this) {
500
- return [node, getNodeLength(node)];
501
- }
502
- return nodeOffsetFromChild(walker.currentNode, true);
503
- }
504
- node = nextSibling;
505
- }
506
- else {
507
- if (node.nodeType === Node.ELEMENT_NODE &&
508
- node.hasAttribute("data-content")) {
509
- return nodeOffsetFromChild(node, index > 0);
510
- }
511
- const firstChild = walker.firstChild();
512
- if (firstChild === null) {
513
- const offset = node.nodeType === Node.TEXT_NODE ? index : index > 0 ? 1 : 0;
514
- return [node, offset];
515
- }
516
- else {
517
- node = firstChild;
518
- }
519
- }
520
- }
521
- const node = walker.currentNode;
522
- return [node, getNodeLength(node)];
523
- }
524
- function getNodeLength(node) {
525
- if (node.nodeType === Node.TEXT_NODE) {
526
- return node.data.length;
527
- }
528
- return node.childNodes.length;
529
- }
530
- function nodeOffsetFromChild(node, after = false) {
531
- const parentNode = node.parentNode;
532
- if (parentNode === null) {
533
- return [null, 0];
534
- }
535
- let offset = Array.from(parentNode.childNodes).indexOf(node);
536
- if (after) {
537
- offset++;
538
- }
539
- return [parentNode, offset];
540
- }
541
- function getSelectionRange(_this) {
542
- const selection = document.getSelection();
543
- if (!selection) {
544
- return { start: 0, end: 0, direction: "none" };
545
- }
546
- const { focusNode, focusOffset, anchorNode, anchorOffset, isCollapsed, } = selection;
547
- const focus = Math.max(0, indexAt(_this, focusNode, focusOffset));
548
- const anchor = isCollapsed
549
- ? focus
550
- : Math.max(0, indexAt(_this, anchorNode, anchorOffset));
551
- return {
552
- start: Math.min(focus, anchor),
553
- end: Math.max(focus, anchor),
554
- direction: focus < anchor ? "backward" : focus > anchor ? "forward" : "none",
555
- };
556
- }
557
- function setSelectionRange(_this, { start, end, direction }) {
558
- const selection = document.getSelection();
559
- if (!selection) {
560
- return;
561
- }
562
- start = Math.max(0, start || 0);
563
- end = Math.max(0, end || 0);
564
- if (end < start) {
565
- start = end;
566
- }
567
- // Focus is the side of the selection where the pointer is released.
568
- const [focus, anchor] = direction === "backward" ? [start, end] : [end, start];
569
- if (focus === anchor) {
570
- const [node, offset] = nodeOffsetAt(_this, focus);
571
- selection.collapse(node, offset);
572
- }
573
- else {
574
- const [anchorNode, anchorOffset] = nodeOffsetAt(_this, anchor);
575
- const [focusNode, focusOffset] = nodeOffsetAt(_this, focus);
576
- if (anchorNode === null && focusNode === null) {
577
- selection.collapse(null);
578
- }
579
- else if (anchorNode === null) {
580
- selection.collapse(focusNode, focusOffset);
581
- }
582
- else if (focusNode === null) {
583
- selection.collapse(anchorNode, anchorOffset);
584
- }
585
- else {
586
- // This method is not implemented in IE.
587
- selection.setBaseAndExtent(anchorNode, anchorOffset, focusNode, focusOffset);
588
- }
589
- }
590
- }
591
-
592
- export { ContentAreaElement, ContentEvent };
593
- //# sourceMappingURL=contentarea.js.map