@juit/vue-tiny-editor 1.0.27 → 1.0.29
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.
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../lib/range.ts","../lib/sanitize.ts","../lib/replace.ts","../lib/selection.ts","../lib/tags.ts","../lib/svg/bold.svg","../lib/svg/italic.svg","../lib/svg/link.svg","../lib/svg/mention.svg","../lib/svg/send.svg","../lib/svg/text.svg","../lib/editor.vue"],"sourcesContent":["/** A DOM {@link Range} with an optional flag indicating its direction */\nexport interface DirectedRange extends Range {\n /** If `end` should come before `start` (for user selection) */\n backwards?: boolean\n}\n\n/** Offsets for a selected region (in characters) */\nexport interface Offsets {\n /** The start character position */\n start: number\n /** The end character position */\n end: number\n /** If the selection is backwards */\n backwards?: boolean\n}\n\n/**\n * Figure out if the specified `needle` {@link Range} is entirely enclosed in\n * the `haystack` {@link Range} or {@link Node} and return the `haystack` if\n * true.\n *\n * @param haystack The outer range\n * @param needle The inner range\n * @returns The `needle` if it is entirely enclosed in the `haystack`\n */\nexport function containsRange(\n haystack: Node | Range,\n needle: Range,\n): Range | null {\n if (haystack instanceof Node) haystack = rangeFromContents(haystack)\n\n // Check if the selection is entirely within the tag\n const inRange =\n haystack.isPointInRange(needle.startContainer, needle.startOffset) &&\n haystack.isPointInRange(needle.endContainer, needle.endOffset)\n\n // Return the needle if it is within the haystack\n return inRange ? needle : null\n}\n\n/**\n * Create a new {@link Range} object that selects the specified node\n *\n * @param node The node to select\n * @returns A new {@link Range} created with `selectNode(...)`\n */\nexport function rangeFromNode(node: Node): Range {\n const range = document.createRange()\n range.selectNode(node)\n return range\n}\n\n/**\n * Create a new {@link Range} object that selects the _contents_ of the\n * specified node\n *\n * @param node The node to select\n * @returns A new {@link Range} created with `selectNodeContents(...)`\n */\nexport function rangeFromContents(node: Node): Range {\n const range = document.createRange()\n range.selectNodeContents(node)\n return range\n}\n\n/**\n * Retrieve the characters contained in a parent {@link Node} up to the\n * specified {@link Node} and offset.\n */\nexport function getCharacters(\n parent: Node,\n child: Node,\n offset: number,\n): string {\n const parentRange = rangeFromContents(parent)\n\n if (! parentRange.isPointInRange(child, offset)) {\n throw new Error('Range out of bounds')\n }\n\n const range = document.createRange()\n range.setStart(parent, 0)\n range.setEnd(child, offset)\n\n return range.toString()\n}\n\n/**\n * Retrieve the character {@link Offsets} for the specified {@link Range}.\n *\n * @param parent The container {@link Node} for the {@link Range}\n * @param range The {@link Range} to calculate offsets for\n * @returns The {@link Offsets} for the specified {@link Range}\n */\nexport function getRangeOffsets(\n parent: Node,\n range: DirectedRange,\n): Offsets {\n const result = { start: 0, end: 0, backwards: range.backwards }\n\n result.start = result.end = getCharacters(parent, range.startContainer, range.startOffset).length\n if (range.collapsed) return result\n\n result.end = getCharacters(parent, range.endContainer, range.endOffset).length\n return result\n}\n\n/**\n * Return a {@link Range} for the specified character {@link Offsets}.\n *\n * @param parent The container {@link Node} for the {@link Range}\n * @returns The {@link Range} for the specified {@link Offsets}\n */\nexport function getOffsetsRange(\n parent: Node,\n offsets: Offsets,\n): DirectedRange {\n const range: DirectedRange = rangeFromContents(parent)\n if (offsets.backwards) range.backwards = true\n let { start, end } = offsets\n\n let firstNode: Text | null = null\n let lastNode: Text | null = null\n let foundStart: boolean | string = false\n let foundEnd: boolean | string = false\n\n const iterator = document.createNodeIterator(parent, NodeFilter.SHOW_TEXT)\n for (\n let text = iterator.nextNode() as Text | null;\n text && ((start >= 0) || (end > 0));\n text = iterator.nextNode() as Text | null\n ) {\n if (! firstNode) firstNode = text\n lastNode = text\n\n const length = text.nodeValue!.length\n\n // start is always calculated at the *start* of the text node\n if ((start >= 0) && (start < length)) {\n foundStart = true\n range.setStart(text, start)\n if (start === end) {\n foundEnd = 'extra'\n range.setEnd(text, end)\n return range\n }\n }\n\n // end is always calculated at the *end* of the text node\n if ((end > 0) && (end <= length)) {\n foundEnd = true\n range.setEnd(text, end)\n if (end === start) {\n foundStart = 'extra'\n range.setStart(text, start)\n return range\n }\n }\n\n start -= length\n end -= length\n }\n\n if ((! foundStart) && (firstNode)) range.setStart(firstNode, 0)\n if ((! foundEnd) && (lastNode)) range.setEnd(lastNode, lastNode.length)\n\n return range\n}\n","import { getOffsetsRange, getRangeOffsets, rangeFromContents } from './range'\n\nimport type { Offsets } from './range'\n\n/** Tags that will be stripped from output */\nconst STRIP_TAGS = new Set([\n 'button',\n 'embed',\n 'form',\n 'iframe',\n 'input',\n 'object',\n 'script',\n 'select',\n 'style',\n 'textarea',\n])\n\n/** Parse an URL and return the normalized HREF if it's a valid HTTP url */\nfunction parseURL(string?: string | null): string | null {\n if (! string) return null\n if (! string.match(/^https?:\\/\\/[^\\s]+$/)) return null\n try {\n return new URL(string).href\n } catch {\n return null\n }\n}\n\n/**\n * Wrap a document fragment in a tag unless `noWrap` is `true`\n *\n * @param fragment The fragment to wrap\n * @param tagName The tag to wrap the fragment in\n * @param noWrap If `true`, the fragment will not be wrapped\n * @param attribues Additional attributes to add to the tag\n * @returns The wrapped fragment or the fragment itself\n */\nfunction maybeWrap(\n fragment: DocumentFragment,\n tagName: string,\n noWrap: boolean = false,\n): DocumentFragment | Element {\n if (noWrap) return fragment\n\n const tag = document.createElement(tagName)\n tag.append(fragment)\n return tag\n}\n\n/**\n * Sanitize a {@link Node}'s content for styles (bold, italic, ...)\n *\n * The `state` object is used to track the current state of the sanitizer: if\n * `bold` is `true`, then all nested `<b>` and `<strong>` tags will be stripped,\n * the same works for `italic` with `<i>` and `<em>`.\n *\n * @param fragment The fragment to sanitize\n * @param state The current state of the sanitizer\n * @returns The sanitized fragment\n */\nfunction sanitizeStyles(\n parent: Element,\n state: {\n bold?: boolean,\n italic?: boolean,\n } = {},\n): DocumentFragment {\n const result = document.createDocumentFragment()\n\n for (const node of parent.childNodes) {\n // Text nodes are cloned if they have contents\n if (node.nodeType === Node.TEXT_NODE) {\n if ((node as Text).nodeValue) result.append(node.cloneNode())\n\n // Elements are processed recursively\n } else if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as Element\n const tagName = element.tagName.toLowerCase()\n\n // Strip unwanted tags\n if (STRIP_TAGS.has(tagName)) continue\n\n const previous = result.lastChild?.nodeType === Node.ELEMENT_NODE ? {\n element: result.lastChild as Element,\n tagName: (result.lastChild as Element).tagName.toLowerCase(),\n } : null\n\n const child = document.createDocumentFragment()\n\n // Bold / strong\n if ((tagName === 'b') || (tagName === 'strong')) {\n const fragment = sanitizeStyles(element, { ...state, bold: true })\n if (! fragment.hasChildNodes()) continue\n\n if (previous?.tagName === 'b') previous.element.append(fragment)\n else child.append(maybeWrap(fragment, 'b', state.bold))\n\n // Italic / emphasis\n } else if ((tagName === 'i') || (tagName === 'em')) {\n const fragment = sanitizeStyles(element, { ...state, italic: true })\n if (! fragment.hasChildNodes()) continue\n\n if (previous?.tagName === 'i') previous.element.append(fragment)\n else child.append(maybeWrap(fragment, 'i', state.italic))\n\n // Paragraphs, divs, line breaks\n } else if (tagName === 'br') {\n child.append(document.createElement('br'))\n } else if ((tagName === 'div') || (tagName === 'p') || (tagName === 'pre') ||\n (tagName === 'h1') || (tagName === 'h2') || (tagName === 'h3') ||\n (tagName === 'h4') || (tagName === 'h5') || (tagName === 'h6') ||\n (tagName === 'ul') || (tagName === 'ol') || (tagName === 'li')) {\n child.append(document.createElement('br'))\n const fragment = sanitizeStyles(element, state)\n if (fragment.hasChildNodes()) {\n child.append(fragment)\n child.append(document.createElement('br'))\n }\n\n // Mentions are implemented as <link rel=\"mention\" name=\"...\" title=\"...\">\n } else if (tagName === 'link') {\n const rel = element.getAttribute('rel')\n if (rel !== 'mention') continue\n\n const name = element.getAttribute('name')\n const title = element.getAttribute('title')\n if (!(name && title)) continue\n\n const link = document.createElement('link')\n link.setAttribute('rel', 'mention')\n link.setAttribute('title', title)\n link.setAttribute('name', name)\n child.append(link)\n\n // Anything else is processed recursively (strip tag, keep contents)\n } else {\n child.append(sanitizeStyles(element, state))\n }\n\n if (child.hasChildNodes()) result.append(child)\n }\n }\n\n return result\n}\n\n/** Merge offsets with HREF (exported for tests) */\nexport function mergeOffsets(list: (Offsets & { href: string })[]): (Offsets & { href: string })[] {\n // Map all our offsets to an array of objects with an index\n const offsets = list.map((offset, i) => ({ ...offset, index: i }))\n\n // Sort by start, keep longest first, if seame, the first one has priority\n offsets.sort((a, b) => {\n return a.start < b.start ? -1 :\n a.start > b.start ? 1 :\n a.end < b.end ? 1 :\n a.end > b.end ? -1 :\n /* v8 ignore next 3 */\n a.index < b.index ? -1 :\n a.index > b.index ? 1 :\n 0\n })\n\n // Find all ranges that are nested/overlapping within another range\n const nested = new Set<Offsets>()\n\n for (let i = 0; i < offsets.length; i++) {\n const outer = offsets[i]!\n for (let j = i + 1; j < offsets.length; j++) {\n const inner = offsets[j]!\n if (nested.has(inner)) continue\n if (inner.start === inner.end) nested.add(inner)\n else if ((inner.start >= outer.start) && (inner.start < outer.end)) nested.add(inner)\n else if ((inner.end >= outer.start) && (inner.end < outer.end)) nested.add(inner)\n }\n }\n\n // All our anchor offsets\n return offsets\n // Filter out any nested/overlapping offsets\n .filter((offset) => ! nested.has(offset))\n // Join consecutive offsets with the same href\n .filter((offset, i, offsets) => {\n const previous = offsets[i - 1]\n if ((previous?.end === offset.start) && (previous.href === offset.href)) {\n previous.end = offset.end\n return false\n } else {\n return true\n }\n })\n}\n\n/**\n * Find the various links in a {@link Node}, and return their offsets and hrefs.\n *\n * @param parent The {@link Node} for which links will be sanitized\n * @returns An array of offsets and related hrefs of all links\n */\nfunction sanitizeLinks(parent: Element): (Offsets & { href: string })[] {\n // Process links parsed directly from the text ()\n const textRanges: (Offsets & { href: string })[] = []\n\n const re = /(https?:\\/\\/[^\\s]+)/gm\n const plain = parent.textContent!\n for (let match = re.exec(plain); match; match = re.exec(plain)) {\n const text = match[1]!\n const href = parseURL(text)\n if (href) textRanges.push({ start: match.index, end: match.index + text.length, href })\n }\n\n // Process links parsed from <a> elements\n const linkRanges: (Offsets & { href: string })[] = []\n\n const iterator = document.createNodeIterator(parent, NodeFilter.SHOW_ELEMENT, (node) => {\n return node.nodeName.toLowerCase() === 'a' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP\n })\n\n for (let node = iterator.nextNode(); node; node = iterator.nextNode()) {\n const element = node as Element\n\n const href = parseURL(element.getAttribute('href'))\n if (href) {\n const range = rangeFromContents(element)\n linkRanges.push({ ...getRangeOffsets(parent, range), href })\n }\n }\n\n return mergeOffsets([ ...textRanges, ...linkRanges ])\n}\n\n/**\n * Strip all empty elements out from the children of a specified {@link Node}.\n *\n * @param parent The {@link Element} to sanitize\n */\nfunction sanitizeEmpty(parent: Element): void {\n parent.normalize()\n\n const iterator = document.createNodeIterator(parent, NodeFilter.SHOW_ELEMENT)\n for (let node = iterator.nextNode(); node; node = iterator.nextNode()) {\n if (node === parent) continue // obviously skip the parent node\n if (node.nodeName.toLowerCase() === 'link') continue // mentions\n if (node.hasChildNodes()) continue // non-empty elements\n if (node.nodeName.toLowerCase() === 'br') { // keep line breaks\n node.parentNode!.replaceChild(document.createTextNode('\\n'), node)\n }\n // remove empty elements\n node.parentNode?.removeChild(node)\n }\n\n parent.normalize()\n // Edge case, empty editor\n if (! parent.hasChildNodes()) {\n parent.append(document.createElement('br'))\n }\n}\n\n/**\n * Sanitize the contents of a {@link Element} normalizing styles (bold,\n * italic, ...) and links.\n *\n * @param parent The {@link Element} to sanitize\n * @returns The same `parent` specified as an argument\n */\nexport function sanitize(parent: Element): Element {\n // Edge case: empty editor\n if (parent.innerHTML === '<br>') return parent\n\n // Cleanup before we start\n parent.normalize()\n\n // Save any and all links\n const offsets = sanitizeLinks(parent)\n\n // Sanitize the content\n const sanitized = sanitizeStyles(parent)\n // Append a space if the last child was a link (mention)\n if (sanitized.lastChild?.nodeName.toLowerCase() === 'link') {\n sanitized.append(document.createTextNode(' '))\n }\n\n // Replace our contents\n parent.replaceChildren(sanitized)\n\n // Restore the links\n offsets.forEach((offsets) => {\n const range = getOffsetsRange(parent, offsets)\n const wrapped = range.extractContents()\n const link = document.createElement('a')\n link.setAttribute('href', offsets.href)\n link.append(wrapped)\n range.insertNode(link)\n })\n\n // Cleanup any empty elements which might have been introduced\n // when restoring the links above\n sanitizeEmpty(parent)\n\n // Return the same element we were given\n return parent\n}\n","import { containsRange, getRangeOffsets, rangeFromContents } from './range'\nimport { sanitize } from './sanitize'\n\nimport type { Offsets } from './range'\n\nexport function replaceRange(\n parent: Element,\n range: Range,\n html: DocumentFragment | string,\n): Offsets {\n if (typeof html === 'string') {\n if (! containsRange(parent, range)) throw new Error('Range out of bounds')\n\n const body = new DOMParser().parseFromString(html, 'text/html').body\n html = document.createDocumentFragment()\n html.append(...body.childNodes)\n }\n\n // Save our selection offsets\n const offsets = getRangeOffsets(parent, range)\n const beforeLength = parent.textContent?.length || 0\n\n // Split into ranges (before and after)\n const parentRange = rangeFromContents(parent)\n\n const beforeRange = document.createRange()\n beforeRange.setStart(parentRange.startContainer, parentRange.startOffset)\n beforeRange.setEnd(range.startContainer, range.startOffset)\n\n const afterRange = document.createRange()\n afterRange.setStart(range.endContainer, range.endOffset)\n afterRange.setEnd(parentRange.endContainer, parentRange.endOffset)\n\n // Extract the content to keep\n const before = beforeRange.extractContents()\n const after = afterRange.extractContents()\n\n // Re-add all our content and sanitize\n parent.replaceChildren() // wipe everything\n parent.append(before, html, after)\n sanitize(parent)\n\n // Adjust our selection offsets depending on pasted data\n const afterLength = parent.textContent?.length || 0\n return { ...offsets, end: offsets.end + afterLength - beforeLength }\n}\n","import { containsRange, getOffsetsRange, getRangeOffsets } from './range'\n\nimport type { DirectedRange, Offsets } from './range'\n\n/**\n * Retrieve the current selection as a {@link DirectedRange} within a parent\n * {@link Element}\n *\n * @param parent The element defining the boundaries of accepatable selections\n * @returns The current selection as a {@link DirectedRange} if within bounds\n */\nexport function getSelectionRange(\n parent: Element,\n): DirectedRange | null {\n const selection = document.getSelection()\n if (! selection?.rangeCount) return null\n\n const selectionRange = selection.getRangeAt(0)\n if (! containsRange(parent, selectionRange)) return null\n\n const backwards =\n selection.isCollapsed ? false :\n selection.anchorNode === selection.focusNode ? selection.anchorOffset > selection.focusOffset :\n selection.anchorNode!.compareDocumentPosition(selection.focusNode!) === Node.DOCUMENT_POSITION_PRECEDING\n return Object.assign(selectionRange, { backwards })\n}\n\n/**\n * Retrieve the current selection as character {@link Offsets} within a parent\n * {@link Element}\n *\n * @param parent The element defining the boundaries of accepatable selections\n * @returns The current selection as a {@link DirectedRange} if within bounds\n */\nexport function getSelectionOffsets(\n parent: Element,\n): Offsets | null {\n const range = getSelectionRange(parent)\n return range ? getRangeOffsets(parent, range) : null\n}\n\n/**\n * Restore the current selection from character {@link Offsets} in the\n * specified {@link Element}\n *\n * @param parent The element defining the boundaries of accepatable selections\n * @param offsets The character offsets to restore\n */\nexport function restoreSelection(\n parent: Element,\n offsets: Offsets,\n collapse = false,\n): DirectedRange {\n if (collapse) offsets = { start: offsets.end, end: offsets.end }\n const range = getOffsetsRange(parent, offsets)\n const { startContainer, startOffset, endContainer, endOffset, backwards } = range\n\n // Selection can be forwards or backwards... restore correctly\n const selection = document.getSelection()\n\n if (selection) {\n if (backwards) {\n selection.setBaseAndExtent(endContainer, endOffset, startContainer, startOffset)\n } else {\n selection.setBaseAndExtent(startContainer, startOffset, endContainer, endOffset)\n }\n }\n\n return range\n}\n","import { containsRange, rangeFromContents, rangeFromNode } from './range'\nimport { sanitize } from './sanitize'\n\n/**\n * Figure out if the current selection entirely enclosed in an element\n * with the specified tag name (normally bold, italic, ...)\n *\n * @param parent The element whose contents we are searching\n * @param range The current selection (if any)\n * @param tagName The tag name to check for (e.g. 'b', 'i', ...)\n * @returns The parent node with the specified tag name if found\n */\nexport function isTagged(\n parent: Node,\n range: Range,\n tagName: string,\n): Element | null {\n // Get the parent range, and make sure the selection is within it\n const parentRange = rangeFromContents(parent)\n if (! containsRange(parent, range)) return null\n\n // Walk up from our common ancestor to to the parent to see if we find\n // an element with the specified tag name\n let ancestor: Node | null = range.commonAncestorContainer\n while (ancestor && parentRange.intersectsNode(ancestor)) {\n if ((ancestor.nodeType === Node.ELEMENT_NODE) &&\n (ancestor.nodeName.toLowerCase() === tagName)) {\n return ancestor as Element\n }\n ancestor = ancestor.parentNode\n }\n\n // Nothing found!\n return null\n}\n\n/**\n * Toggle a tag within the specified {@link Element}, around the specified\n * selection.\n *\n * @param parent The element whose contents we are modifying\n * @param range The range to toggle the tag around\n * @param tagName The tag name (e.g. 'b', 'i', ...) to toggle\n * @param attributes Any attributes to set on the tag (will be sanitized)\n */\nexport function toggleTag(\n parent: Element,\n range: Range,\n tagName: string,\n attributes: Record<string, string> = {},\n): void {\n if (range.collapsed) return\n if (! containsRange(parent, range)) return\n\n const ancestor = isTagged(parent, range, tagName)\n\n const ancestorRange = ancestor ?\n rangeFromNode(ancestor) :\n rangeFromContents(range.commonAncestorContainer)\n\n const before = document.createRange()\n before.setStart(ancestorRange.startContainer, ancestorRange.startOffset)\n before.setEnd(range.startContainer, range.startOffset)\n\n const after = document.createRange()\n after.setStart(range.endContainer, range.endOffset)\n after.setEnd(ancestorRange.endContainer, ancestorRange.endOffset)\n\n // Remove back to front...\n const afterFragment = after.extractContents()\n const selectedFragment = range.extractContents()\n const beforeFragment = before.extractContents()\n ancestorRange.deleteContents() // wipe everything, just in case\n\n // Create our fragment\n const fragment = document.createDocumentFragment()\n fragment.append(beforeFragment)\n\n // Append our selected fragment, possibly wrapped in a tag\n if (ancestor) {\n fragment.append(selectedFragment)\n } else {\n const tag = document.createElement(tagName)\n Object.entries(attributes).forEach((attr) => tag.setAttribute(...attr))\n tag.append(selectedFragment)\n fragment.append(tag)\n }\n\n // Surround with before and after fragments\n // fragment.insertBefore(beforeFragment, fragment.firstChild)\n fragment.append(afterFragment)\n\n // Insert and sanitize\n ancestorRange.insertNode(fragment)\n sanitize(parent)\n}\n","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M272-200v-560h221q65 0 120 40t55 111q0 51-23 78.5T602-491q25 11 55.5 41t30.5 90q0 89-65 124.5T501-200zm121-112h104q48 0 58.5-24.5T566-372t-10.5-35.5T494-432H393zm0-228h93q33 0 48-17t15-38q0-24-17-39t-44-15h-95z\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M200-200v-100h160l120-360H320v-100h400v100H580L460-300h140v100z\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M440-280H280q-83 0-141.5-58.5T80-480t58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85 35 85 85 35h160zM320-440v-80h320v80zm200 160v-80h160q50 0 85-35t35-85-35-85-85-35H520v-80h160q83 0 141.5 58.5T880-480t-58.5 141.5T680-280z\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M480-80q-83 0-156-31.5T197-197t-85.5-127T80-480t31.5-156T197-763t127-85.5T480-880t156 31.5T763-763t85.5 127T880-480v58q0 59-40.5 100.5T740-280q-35 0-66-15t-52-43q-29 29-65.5 43.5T480-280q-83 0-141.5-58.5T280-480t58.5-141.5T480-680t141.5 58.5T680-480v58q0 26 17 44t43 18 43-18 17-44v-58q0-134-93-227t-227-93-227 93-93 227 93 227 227 93h200v80zm0-280q50 0 85-35t35-85-35-85-85-35-85 35-35 85 35 85 85 35\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M792-443 176-183q-20 8-38-3.5T120-220v-520q0-22 18-33.5t38-3.5l616 260q25 11 25 37t-25 37M200-280l474-200-474-200v140l240 60-240 60zm0 0v-400z\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M280-160v-520H80v-120h520v120H400v520zm360 0v-320H520v-120h360v120H760v320z\" }, null, -1)\n ])))\n}\nexport default { render: render }","<template>\n <div class=\"juit-tiny-edit\" :class=\"{ '-jte-editable': editable, '-jte-dark': dark }\">\n <div ref=\"rootRef\" class=\"-jte-root\">\n <!-- our mentions popup -->\n <div ref=\"mentionsPopupRef\" class=\"-jte-mentions-popup\">\n <ul ref=\"mentionsListRef\">\n <li\n v-for=\"(mention, i) in mentionsEntries\"\n :key=\"i\"\n :class=\"{ '-jte-mentions-selected': mentionsIndex === i }\"\n @mousedown=\"applyMention(mention.name, mention.value)\"\n >\n <div class=\"-jte-mentions-entry\">\n {{ mention.value }}\n <span class=\"-jte-mentions-ref\">{{ mention.name }}</span>\n </div>\n </li>\n </ul>\n <div v-if=\"! mentionsReady\" class=\"-jte-mentions-waiting\">\n {{ mentionsText }}{{ dotsString }}\n </div>\n <div v-else-if=\"! mentionsEntries.length\" class=\"-jte-mentions-none\">\n {{ mentionsText }}\n </div>\n </div>\n\n <!-- our links popup -->\n <div\n ref=\"linksPopupRef\"\n class=\"-jte-links-popup\"\n @click=\"activeLink = previousLink\"\n @focusin=\"activeLink = previousLink\"\n >\n <div>\n <iconText class=\"-jte-icon\" />\n <input v-model=\"linkText\" type=\"text\">\n </div>\n <div :class=\"{ '-jte-error': !linkValid }\">\n <iconLink class=\"-jte-icon\" />\n <input v-model=\"linkHref\" type=\"text\">\n </div>\n </div>\n\n <div v-if=\"placeholder && (! html)\" class=\"-jte-placeholder\">\n {{ placeholder }}\n </div>\n\n <!-- our editor -->\n <div\n ref=\"editorRef\"\n class=\"-jte-editor\"\n :contenteditable=\"editable ? 'plaintext-only' : 'false'\"\n @beforeinput=\"onBeforeInput($event as InputEvent)\"\n @input=\"onInput($event as InputEvent)\"\n @keydown=\"onKeydown($event)\"\n @paste=\"onPaste($event)\"\n @focusin=\"activeLink = null\"\n />\n\n <div class=\"-jte-toolbox\">\n <iconMention class=\"-jte-icon -jte-icon-mention\" :class=\"{ '-jte-disabled': ! selected?.collapsed }\" @click=\"insertMention\" />\n <iconBold class=\"-jte-icon -jte-icon-bold\" :class=\"iconClass(isBold, 'b')\" @click=\"applyTag('b')\" />\n <iconItalic class=\"-jte-icon -jte-icon-italic\" :class=\"iconClass(isItalic, 'i')\" @click=\"applyTag('i')\" />\n <iconLink class=\"-jte-icon -jte-icon-href\" :class=\"iconClass(isLink)\" @click=\"applyTag('a', { href: 'http://__placeholder__/' })\" />\n <iconSend\n v-if=\"onSubmit\"\n class=\"-jte-icon -jte-icon-send\"\n :class=\"{ '-jte-disabled': !html, '-jte-active': !!html }\"\n @click=\"submit\"\n />\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'\n\nimport { getCharacters, getOffsetsRange, getRangeOffsets } from './range'\nimport { replaceRange } from './replace'\nimport { sanitize } from './sanitize'\nimport { getSelectionOffsets, getSelectionRange, restoreSelection } from './selection'\nimport { isTagged, toggleTag } from './tags'\n// SVGs\nimport iconBold from './svg/bold.svg'\nimport iconItalic from './svg/italic.svg'\nimport iconLink from './svg/link.svg'\nimport iconMention from './svg/mention.svg'\nimport iconSend from './svg/send.svg'\nimport iconText from './svg/text.svg'\n\nimport type { PropType } from 'vue'\nimport type { Offsets } from './range'\n\n/* ==== DEFS ================================================================ */\n\ndefineOptions({ name: 'JuitTinyEditor' })\n\nconst model = defineModel({\n type: String,\n required: false,\n default: '',\n})\n\nconst props = defineProps({\n /** A flag indicating if content can be edited (default: `true`) */\n editable: {\n type: Boolean,\n required: false,\n default: true,\n },\n /** A flag indicating whether dark mode is enabled or not */\n dark: {\n type: Boolean,\n required: false,\n default: false,\n },\n /** A placeholder for when no text is available */\n placeholder: {\n type: String,\n required: false,\n default: '',\n },\n /** A list of mentions to be used in the editor */\n mentions: {\n type: Object as PropType<Record<string, string> | null>,\n required: false,\n default: () => ({}),\n },\n /** Used as `@mention=\"...\"`, notify the current mention text (`@abc...`) */\n onMention: {\n type: Function as PropType<((text: string) => any) | undefined>,\n required: false,\n default: undefined,\n },\n /** Used as `@submit=\"...\"`, will display the send button and emit HTML */\n onSubmit: {\n type: Function as PropType<((html: string) => any) | undefined>,\n required: false,\n default: undefined,\n },\n})\n\nwatch(() => props.dark, (dark) => {\n rootRef.value?.style.setProperty('color-scheme', dark ? 'dark light' : 'light dark')\n // document.body.classList.toggle('dark', dark)\n})\n\n/* ==== REFS ================================================================ */\n\n/** Reference to our root container */\nconst rootRef = shallowRef<HTMLDivElement | null>(null)\n/** Reference to our content editable DIV */\nconst editorRef = shallowRef<HTMLDivElement | null>(null)\n/** Reference to our popup */\nconst mentionsPopupRef = shallowRef<HTMLDivElement | null>(null)\n/** Reference to our mentions list (in the popup) DIV */\nconst mentionsListRef = shallowRef<HTMLDivElement | null>(null)\n/** Reference to our links popup */\nconst linksPopupRef = shallowRef<HTMLDivElement | null>(null)\n\n/** The `Range` of the current selection, if within our editor */\nconst selected = shallowRef<Range & { backwards?: boolean } | null>(null)\n\n/** The HTML string contained by the editor */\nconst html = ref('')\n\n/** Index of the current mention */\nconst mentionsIndex = ref(0)\n/** Range of the current mention (while being inserted) */\nconst mentionsRange = shallowRef<Range | null>(null)\n/** Text of the current mention (including the leading \"@\") */\nconst mentionsText = computed(() => mentionsRange.value?.toString() || '')\n/** Computed to see if mentions (from our prop) are ready */\nconst mentionsReady = computed(() => !! props.mentions)\n/** Computed list of mentions that match the current text */\nconst mentionsEntries = computed(() => {\n const text = mentionsText.value.substring(1).toLowerCase()\n\n if (! props.mentions) return []\n return Object.entries(props.mentions)\n .filter(([ _, name ]) => name.toLowerCase().startsWith(text))\n .sort(([ _a, a ], [ _b, b ]) => a.localeCompare(b))\n .map(([ name, value ]) => ({ name, value }))\n})\n\n/** The link associated with the *current* selection in the editor */\nconst selectedLink = ref<Element | null>(null)\n/** The link currently being handled by the links popup */\nconst activeLink = ref(null as any)\n/** The last link highlighted by the editor (updated by the watch below) */\nconst previousLink = ref<Element | null>(null)\n/** The current link, either in the editor or in the links popup */\nconst isLink = computed<Element | null>(() => selectedLink.value || activeLink.value)\n/* Watch the selected link and remember the last viable one */\nwatch(selectedLink, (link) => link && (previousLink.value = link))\n/** The text of the current link */\nconst linkText = ref('')\n/** The href of the current link */\nconst linkHref = ref('')\n/** Validate the link's own href (must be empty or an URL) */\nconst linkValid = computed(() => {\n if (! linkHref.value) return true\n try {\n new URL(linkHref.value)\n return true\n } catch {\n return false\n }\n})\n/** Update the content and href of the current link from the popup */\nwatch([ linkText, linkHref ], ([ text, href ]) => {\n if (! isLink.value) return\n isLink.value.textContent = text\n isLink.value.setAttribute('href', href)\n commit()\n})\n\n\n/** Whether the current selection is all bold */\nconst isBold = ref<Element | null>(null)\n/** Whether the current selection is all italic */\nconst isItalic = ref<Element | null>(null)\n\n/** How many dots are we displaying (in \"waiting for mentions\" animation) */\nconst dots = ref(0)\n/** String of dots to display (in \"waiting for mentions\" animation) */\nconst dotsString = computed(() => '.' + '.'.repeat(dots.value % 5))\n/** Interval animating our \"waiting for mentions\" text */\nlet dotsInterval: ReturnType<typeof setInterval> | null = null\n/** The _next_ tag to wrap our input into (when enabling bold on collapsed) */\nconst nextTag = ref('')\n/** When the selected range changes, reset the next tag */\nwatch((selected), (newRange, oldRange) => {\n if (oldRange === null) return\n if (newRange === null) return\n\n if ((oldRange.startContainer == newRange.startContainer) &&\n (oldRange.startOffset == newRange.startOffset) &&\n (oldRange.endContainer == newRange.endContainer) &&\n (oldRange.endOffset == newRange.endOffset) &&\n (oldRange?.backwards == newRange?.backwards)) return\n nextTag.value = ''\n})\n\n/** Our history (for undo) */\nconst history: (Offsets & { content: string })[] = []\n\n/* ==== FUNCTIONS =========================================================== */\n\n/** Compute the class for an icon (bold or italic) */\nfunction iconClass(element: Element | null, tag?: string): string {\n if (! selected.value) return '-jte-disabled'\n\n // If we don't have a tag, we're not processing zero-width selections\n if (! tag) {\n if (element) return '-jte-active'\n if (selected.value.collapsed) return '-jte-disabled'\n return ''\n }\n\n // styled, but next input will remove the style\n if (element && (nextTag.value === tag)) return ''\n // either styled *or* next input will add the style\n if (element || nextTag.value === tag) return '-jte-active'\n // not styled, but can be styled\n return ''\n}\n\n/** Commit changes to the model converting our editor into HTML */\nfunction commit(): void {\n if (! editorRef.value) return void (html.value = '')\n\n // Clean our HTML, then update the model\n const string = editorRef.value.innerHTML\n html.value = string === '<br>' ? '' : // edge case: empty editor\n string.replaceAll(' ', ' ').trimEnd() // remove trailing whitespace\n}\n\n/** Mark a new entry in our history */\nfunction historySave(): void {\n if (! editorRef.value) return\n\n // Save our history\n if (history[history.length - 1]?.content !== html.value) {\n const offsets = getSelectionOffsets(editorRef.value) || { start: 0, end: 0 }\n history.push({ ...offsets, content: html.value })\n if (history.length > 100) history.shift()\n }\n}\n\n/** Undo the last change */\nfunction historyUndo(): void {\n if (! editorRef.value) return\n if (! selected.value) return\n\n const entry = history.pop()\n if (! entry) return\n\n const body = new DOMParser().parseFromString(entry.content, 'text/html').body\n editorRef.value.replaceChildren(...body.childNodes)\n restoreSelection(editorRef.value, entry)\n}\n\n/** Compute the mention range according to changes th the selection */\nfunction computeMention(): void {\n if (mentionsRange.value && selected.value?.collapsed) {\n const range = mentionsRange.value.cloneRange()\n range.setEnd(selected.value.startContainer, selected.value.startOffset)\n if (range.toString().match(/^@[^\\r\\n]*$/)) {\n mentionsRange.value = range\n return\n }\n }\n mentionsRange.value = null\n}\n\n/** Replace the current mentions range with a proper mention */\nfunction applyMention(name: string, value: string): void {\n if (! editorRef.value) return\n if (! mentionsRange.value) return\n\n const editor = editorRef.value\n\n historySave()\n const fragment = document.createDocumentFragment()\n const element = document.createElement('link')\n element.setAttribute('rel', 'mention')\n element.setAttribute('name', name)\n element.setAttribute('title', value)\n fragment.append(element, ' ')\n\n const offsets = replaceRange(editor, mentionsRange.value, fragment)\n selectWithOffsets(offsets, true)\n mentionsRange.value = null\n commit()\n}\n\n/** Apply a \"tag\" (bold, italic, ...) */\nfunction applyTag(tagName: string, attributes: Record<string, string> = {}): void {\n if (! editorRef.value) return\n if (! selected.value) return\n\n const editor = editorRef.value\n\n // If nothing is selected, this is reserved for the next tag\n if (selected.value.collapsed) {\n nextTag.value = tagName\n editor.focus()\n return\n }\n\n historySave()\n const offsets = getRangeOffsets(editor, selected.value)\n toggleTag(editor, selected.value, tagName, attributes)\n selectWithOffsets(offsets)\n commit()\n}\n\nfunction insertMention(): void {\n if (! editorRef.value) return\n if (! selected.value?.collapsed) return\n\n const editor = editorRef.value\n\n const offsets = getRangeOffsets(editor, selected.value)\n\n const at = document.createTextNode('@')\n selected.value.insertNode(at)\n\n offsets.end += 1\n mentionsRange.value = getOffsetsRange(editor, offsets)\n offsets.start += 1\n selectWithOffsets(offsets)\n}\n\nfunction selectWithOffsets(offsets: Offsets, collapse?: boolean): void {\n if (! editorRef.value) return\n\n const editor = editorRef.value\n selected.value = restoreSelection(editor, offsets, collapse)\n editor.focus()\n}\n\nfunction submit(): void {\n if (! html.value) return\n props.onSubmit?.(html.value)\n}\n\n/* ==== DOM EVENTS ========================================================== */\n\n/** Trigger shortcuts for applying bold, italic, ... */\nfunction onKeydown(event: KeyboardEvent): void {\n if (event.metaKey || event.ctrlKey) {\n switch (event.key) {\n case 'b':\n applyTag('b')\n event.preventDefault()\n break\n case 'i':\n applyTag('i')\n event.preventDefault()\n break\n case 'z':\n historyUndo()\n event.preventDefault()\n break\n }\n } else if (mentionsRange.value) {\n switch (event.key) {\n case 'Home':\n mentionsIndex.value = 0\n event.preventDefault()\n break\n case 'ArrowUp':\n mentionsIndex.value = Math.max(mentionsIndex.value - 1, 0)\n event.preventDefault()\n break\n case 'PageUp':\n mentionsIndex.value = Math.max(mentionsIndex.value - 4, 0)\n event.preventDefault()\n break\n case 'ArrowDown':\n mentionsIndex.value = Math.min(mentionsIndex.value + 1, mentionsEntries.value.length - 1)\n event.preventDefault()\n break\n case 'PageDown':\n mentionsIndex.value = Math.min(mentionsIndex.value + 4, mentionsEntries.value.length - 1)\n event.preventDefault()\n break\n case 'End':\n mentionsIndex.value = mentionsEntries.value.length - 1\n event.preventDefault()\n break\n case 'Escape':\n mentionsRange.value = null\n event.preventDefault()\n break\n case 'Enter': {\n const mention = mentionsEntries.value[mentionsIndex.value]\n if (mention) applyMention(mention.name, mention.value)\n event.preventDefault()\n break\n }\n }\n }\n}\n\n/** Replace the current selection with the (sanitized) content from clipboard */\nfunction onPaste(event: ClipboardEvent): void {\n event.preventDefault()\n if (! editorRef.value) return\n if (! selected.value) return\n\n const data = event.clipboardData\n if (! data) return\n\n const text = data.getData('text/html') || data.getData('text/plain')\n if (! text) return\n\n const offsets = replaceRange(editorRef.value, selected.value, text)\n\n selectWithOffsets(offsets, true)\n commit()\n}\n\n/** If deleting a bunch of content, ensure links are removed, too... */\nfunction onBeforeInput(event: InputEvent): void {\n if (! editorRef.value) return\n if (! selected.value) return\n\n historySave()\n\n if ((!selected.value.collapsed) &&\n ((event.inputType === 'deleteContentBackward') ||\n (event.inputType === 'deleteContentForward'))) {\n event.preventDefault()\n event.stopPropagation()\n const offsets = replaceRange(editorRef.value, selected.value, '')\n selectWithOffsets(offsets, true)\n commit()\n }\n}\n\n/** Someone typed something in our editor, let's process it */\nfunction onInput(event: InputEvent): void {\n const editor = editorRef.value\n if (! editor) return\n\n // Save the current selection and mentions\n const mentions = mentionsRange.value ? getRangeOffsets(editor, mentionsRange.value) : null\n const selection = getSelectionOffsets(editor)\n\n // See if we need to process this event with style (bold, italic, ...)\n if (nextTag.value &&\n selection &&\n (selection.start === selection.end) &&\n (event.inputType === 'insertText') &&\n (event.data !== '@') &&\n (! mentionsRange.value)) {\n const range = getOffsetsRange(editor, { start: selection.start - 1, end: selection.end })\n toggleTag(editor, range, nextTag.value)\n }\n nextTag.value = ''\n\n // First thing first: sanitize the content (this will also take care of links)\n sanitize(editor)\n\n // Restore mentions range *after* sanitization\n if (mentions) mentionsRange.value = getOffsetsRange(editor, mentions)\n\n // Restore the selection range\n if (selection) {\n // Edge case when all content is deleted: insert a single \"<br>\"\n if (editor.innerHTML === '<br>') {\n document.getSelection()?.setBaseAndExtent(editor, 0, editor, 0)\n editor.focus()\n } else {\n selectWithOffsets(selection)\n }\n }\n\n // When the input is \"@\" we might want to trigger a mention\n if ((! isLink.value) &&\n (! mentionsRange.value) &&\n (event.data === '@') &&\n (event.inputType === 'insertText')) {\n const selection = document.getSelection()\n if (! selection?.anchorNode) return\n const text = getCharacters(editor, selection.anchorNode, selection.anchorOffset)\n if (text.match(/(\\s|^)@$/)) {\n const start = text.length === 1 ? 0 : text.length - 1\n const end = text.length\n const offsets: Offsets = { start, end }\n mentionsRange.value = getOffsetsRange(editor, offsets)\n }\n } else {\n computeMention()\n }\n\n // Commit the changes\n commit()\n}\n\n\n/* Update the `selected` Range when the document selection changes */\nfunction onSelectionChange(_event: Event): void {\n // Wipe all link information if we leave our component\n if (!(rootRef.value && getSelectionRange(rootRef.value))) {\n selectedLink.value = null\n previousLink.value = null\n activeLink.value = null\n }\n\n // Handle the current editor selection and remember its range (if any)\n selected.value = editorRef.value ? getSelectionRange(editorRef.value) : null\n\n // Process any changes to mentions\n computeMention()\n}\n\n/* ===== WATCHES AND COMPONENT LIFECYCLE ==================================== */\n\n/* Watch our local refs and emit */\nwatch(mentionsText, (text) => text && props.onMention?.(text))\n\nonMounted(() => {\n // Bind DOM events\n document.addEventListener('selectionchange', onSelectionChange)\n\n // References to our editor, popup, and list\n if (! rootRef.value) throw new Error('Root not referenced')\n if (! editorRef.value) throw new Error('Editor not referenced')\n if (! mentionsPopupRef.value) throw new Error('Popup not referenced')\n if (! mentionsListRef.value) throw new Error('List not referenced')\n if (! linksPopupRef.value) throw new Error('Popup not referenced')\n const root = rootRef.value\n const editor = editorRef.value\n const mentionsPopup = mentionsPopupRef.value\n const mentionsList = mentionsListRef.value\n const linksPopup = linksPopupRef.value\n\n /* On changes to the _local_ HTML, trigger an emit on our model value */\n watch(html, (html) => {\n if (html !== model.value) model.value = html // no changes, ignore!\n })\n\n /* Watch our model, when the value is different from the local HTML, parse */\n watch(model, (model) => {\n if (model === html.value) return // no changes, ignore!\n\n // wrap the text in <html><body>...</body></html>, if we don't do it then\n // any initial <link> tag will be moved to the <head> of the document...\n const string = `<html><body>${model}</body></html>`\n const body = new DOMParser().parseFromString(string, 'text/html').body\n sanitize(body)\n editor.replaceChildren(...body.childNodes)\n commit()\n }, { immediate: true })\n\n /* Watch our mentions availability and trigger a waiting animation */\n watch(mentionsReady, (available) => {\n dots.value = 0\n if (! available) dotsInterval = setInterval(() => dots.value = dots.value + 1, 200)\n else if (dotsInterval) clearInterval(dotsInterval)\n }, { immediate: true })\n\n /* Watch our links and update the popup */\n watch(isLink, (link) => {\n // Always update (or reset?) the link text and href of a link\n linkText.value = link?.textContent || ''\n linkHref.value = link?.getAttribute('href') || ''\n\n // If the link is a placeholder, consider this to be an empty link\n if (linkHref.value === 'http://__placeholder__/') {\n link?.setAttribute('href', '')\n linkHref.value = ''\n }\n\n // If we are no longer editing a link, sanitize and hide the popup\n if (! link) {\n const offsets = getSelectionOffsets(editor)\n sanitize(editor)\n if (offsets) restoreSelection(editor, offsets)\n\n linksPopup.style.display = 'none'\n linksPopup.style.top = '0'\n linksPopup.style.left = '0'\n return\n }\n\n // Wait for the next tick to make sure the DOM is updated\n nextTick(() => {\n // Position and show the popup\n linksPopup.style.display = 'flex'\n\n const rootRect = root.getBoundingClientRect()\n const rangeRect = link.getBoundingClientRect()\n const editorRect = editor.getBoundingClientRect()\n\n const popupStyle = getComputedStyle(linksPopup)\n const popupWidth = parseInt(popupStyle.width)\n const popupHeight = parseInt(popupStyle.height)\n\n const fitsTop = (rangeRect.top - popupHeight) - 6 > editorRect.top\n const fitsBottom = (rangeRect.bottom + popupHeight + 3) < editorRect.bottom\n const fitsRight = rangeRect.left + popupWidth < editorRect.right\n\n linksPopup.style.top = (! fitsBottom && fitsTop) ?\n `${rangeRect.top - popupHeight - rootRect.top - 6}px` : // over the link\n `${rangeRect.bottom - rootRect.top + 3}px` // below the link\n linksPopup.style.left = fitsRight ?\n `${rangeRect.left - rootRect.left}px` : // right of the link\n `${editorRect.right - popupWidth - rootRect.left}px` // aligned to the right of the editor\n })\n }, { immediate: true })\n\n /* Watch our mentions index and update the popup */\n watch([ mentionsIndex, mentionsRange, mentionsEntries ], ([ index, range, entries ], [ oldIndex = -1 ]) => {\n // Make sure that our index is within acceptable bounds\n if (index >= entries.length) index = Math.max(entries.length - 1, 0)\n mentionsIndex.value = index\n\n // No list or no popup, nothing to do\n if (! mentionsPopup) return\n if (! mentionsList) return\n\n // No range, hide and reset the popup\n if (! range) {\n mentionsIndex.value = 0\n mentionsPopup.style.display = 'none'\n mentionsPopup.style.top = '0'\n mentionsPopup.style.left = '0'\n mentionsList.scrollTop = 0\n return\n }\n\n // Wait for the next tick to make sure the DOM is updated\n nextTick(() => {\n // Position and show the popup\n mentionsPopup.style.display = 'block'\n\n const rootRect = root.getBoundingClientRect()\n const rangeRect = range.getBoundingClientRect()\n const editorRect = editor.getBoundingClientRect()\n\n const popupStyle = getComputedStyle(mentionsPopup)\n const popupWidth = parseInt(popupStyle.width)\n const popupHeight = parseInt(popupStyle.height)\n\n const fitsTop = (rangeRect.top - popupHeight) - 6 > editorRect.top\n const fitsBottom = (rangeRect.bottom + popupHeight + 3) < editorRect.bottom\n const fitsRight = rangeRect.left + popupWidth < editorRect.right\n\n mentionsPopup.style.top = (! fitsBottom && fitsTop) ?\n `${rangeRect.top - popupHeight - rootRect.top - 6}px` : // over the link\n `${rangeRect.bottom - rootRect.top + 3}px` // below the link\n mentionsPopup.style.left = fitsRight ?\n `${rangeRect.left - rootRect.left}px` : // right of the link\n `${editorRect.right - popupWidth - rootRect.left}px` // aligned to the right of the editor\n\n // Scroll the list to make sure the selected item is visible\n const item = mentionsList.querySelectorAll('li')[mentionsIndex.value]\n if (! item) return\n\n const style = getComputedStyle(item)\n const topBorder = parseFloat(style.getPropertyValue('border-top-width')) || 0\n const bottomBorder= parseFloat(style.getPropertyValue('border-bottom-width')) || 0\n\n // Get the bounding rectangles for the list and the selected item\n const listRect = mentionsList.getBoundingClientRect()\n const itemRect = item.getBoundingClientRect()\n\n // Some CSS magic to scroll the list\n const topTop = itemRect.top - listRect.top\n const btmBtm = itemRect.bottom - listRect.bottom\n const relativeBtm = itemRect.bottom - listRect.top\n\n // Going down\n if (index > oldIndex) {\n // Element is hidden below the visible list\n if (relativeBtm > listRect.height) {\n mentionsList.scrollTop += btmBtm - bottomBorder\n // Element is hidden above the visible list\n } else if (topTop < 0) {\n mentionsList.scrollTop += topTop + topBorder\n }\n // Going up\n } else {\n // Element is hidden above the visible list\n if (topTop < 0) {\n mentionsList.scrollTop += topTop + topBorder\n // Element is hidden below the visible list\n } else if (btmBtm > 0) {\n mentionsList.scrollTop += relativeBtm - listRect.height - bottomBorder\n }\n }\n })\n }, { immediate: true })\n\n /* Watch the selected range and infer state for bold, italic, ... */\n watch(selected, (selected) => {\n if (selected) {\n selectedLink.value = isTagged(editor, selected, 'a')\n isBold.value = isTagged(editor, selected, 'b')\n isItalic.value = isTagged(editor, selected, 'i')\n } else {\n selectedLink.value = null\n isBold.value = null\n isItalic.value = null\n }\n }, { immediate: true })\n})\n\nonUnmounted(() => {\n document.removeEventListener('selectionchange', onSelectionChange)\n if (dotsInterval) clearInterval(dotsInterval)\n})\n</script>\n\n<style lang=\"pcss\">\n.juit-tiny-edit {\n\n /* ===== LIGHT THEME (default) ============================================ */\n\n .-jte-root {\n /* border radius */\n --jte-curr-border-radius: var(--jte-border-radius, 4px);\n\n /* main colors */\n --jte-curr-text: var(--jte-text, currentColor);\n --jte-curr-background: var(--jte-background, transparent);\n --jte-curr-active: var(--jte-active, #009);\n --jte-curr-mention: var(--jte-mention, #99f);\n --jte-curr-popup: var(--jte-popup, #fff);\n --jte-curr-link: var(--jte-link, #009);\n --jte-curr-error: var(--jte-error, #933);\n\n /* derived colors */\n --jte-curr-inactive: var(--jte-inactive, color-mix(in srgb, var(--jte-curr-text) 50%, transparent));\n --jte-curr-border: var(--jte-border, color-mix(in srgb, var(--jte-curr-text) 12.5%, transparent));\n --jte-curr-shade: var(--jte-shade, color-mix(in srgb, var(--jte-curr-text) 6.25%, transparent));\n --jte-curr-active-shade: var(--jte-active-shade, color-mix(in srgb, var(--jte-curr-active) 6.25%, transparent));\n\n /* button colors */\n --jte-curr-button-text: var(--jte-button-text, var(--jte-curr-text));\n --jte-curr-button-border: var(--jte-button-border, var(--jte-curr-border));\n --jte-curr-button-background: var(--jte-button-background, var(--jte-curr-background));\n\n --jte-curr-button-hovered-text: var(--jte-button-hovered-text, var(--jte-curr-text));\n --jte-curr-button-hovered-border: var(--jte-button-hovered-border, var(--jte-curr-border));\n --jte-curr-button-hovered-background: var(--jte-button-hovered-background, var(--jte-curr-shade));\n\n --jte-curr-button-active-text: var(--jte-button-active-text, var(--jte-curr-active));\n --jte-curr-button-active-border: var(--jte-button-active-border, var(--jte-curr-border));\n --jte-curr-button-active-background: var(--jte-button-active-background, var(--jte-curr-active-shade));\n\n --jte-curr-button-disabled-text: var(--jte-button-disabled-text, var(--jte-curr-inactive));\n --jte-curr-button-disabled-border: var(--jte-button-disabled-border, var(--jte-curr-border));\n --jte-curr-button-disabled-background: var(--jte-button-disabled-background, var(--jte-curr-shade));\n\n /* inputs within popup */\n --jte-curr-input-text: var(--jte-input-text, var(--jte-curr-text));\n --jte-curr-input-background: var(--jte-input-background, transparent);\n --jte-curr-input-border: var(--jte-input-border, var(--jte-curr-border));\n --jte-curr-input-shade: var(--jte-input-shade, var(--jte-curr-shade));\n }\n\n /* ===== DARK THEME ======================================================= */\n\n &.-jte-dark .-jte-root {\n /* main colors */\n --jte-curr-text: var(--jte-dark-text, currentColor);\n --jte-curr-background: var(--jte-dark-background, transparent);\n --jte-curr-active: var(--jte-dark-active, #99f);\n --jte-curr-mention: var(--jte-dark-mention, #669);\n --jte-curr-popup: var(--jte-dark-popup, #111);\n --jte-curr-link: var(--jte-dark-link, #99f);\n --jte-curr-error: var(--jte-dark-error, #f99);\n\n /* derived colors */\n --jte-curr-inactive: var(--jte-dark-inactive, color-mix(in srgb, var(--jte-curr-text) 50%, transparent));\n --jte-curr-border: var(--jte-dark-border, color-mix(in srgb, var(--jte-curr-text) 12.5%, transparent));\n --jte-curr-shade: var(--jte-dark-shade, color-mix(in srgb, var(--jte-curr-text) 12.5%, transparent));\n --jte-curr-active-shade: var(--jte-dark-active-shade, color-mix(in srgb, var(--jte-curr-active) 6.25%, transparent));\n\n /* button colors */\n --jte-curr-button-text: var(--jte-dark-button-text, var(--jte-curr-text));\n --jte-curr-button-border: var(--jte-dark-button-border, var(--jte-curr-border));\n --jte-curr-button-background: var(--jte-dark-button-background, var(--jte-curr-background));\n\n --jte-curr-button-hovered-text: var(--jte-dark-button-hovered-text, var(--jte-curr-text));\n --jte-curr-button-hovered-border: var(--jte-dark-button-hovered-border, var(--jte-curr-border));\n --jte-curr-button-hovered-background: var(--jte-dark-button-hovered-background, var(--jte-curr-shade));\n\n --jte-curr-button-active-text: var(--jte-dark-button-active-text, var(--jte-curr-active));\n --jte-curr-button-active-border: var(--jte-dark-button-active-border, var(--jte-curr-border));\n --jte-curr-button-active-background: var(--jte-dark-button-active-background, var(--jte-curr-active-shade));\n\n --jte-curr-button-disabled-text: var(--jte-dark-button-disabled-text, var(--jte-curr-inactive));\n --jte-curr-button-disabled-border: var(--jte-dark-button-disabled-border, var(--jte-curr-border));\n --jte-curr-button-disabled-background: var(--jte-dark-button-disabled-background, var(--jte-curr-shade));\n\n /* inputs within popup */\n --jte-curr-input-text: var(--jte-dark-input-text, var(--jte-curr-text));\n --jte-curr-input-background: var(--jte-dark-input-background, transparent);\n --jte-curr-input-border: var(--jte-dark-input-border, var(--jte-curr-border));\n --jte-curr-input-shade: var(--jte-dark-input-shade, var(--jte-curr-shade));\n }\n\n /* ======================================================================== *\n * STYLING *\n * ======================================================================== */\n\n .-jte-root {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n position: relative;\n box-sizing: content-box;\n\n color: var(--jte-curr-text);\n background-color: var(--jte-curr-background);\n\n /* ===== TOOLBAR ======================================================== */\n\n .-jte-toolbox {\n display: none;\n user-select: none;\n position: relative;\n height: 1.75em;\n overflow: hidden;\n margin-top: 0.75em;\n padding: 0 0.25em 0 0.25em;\n\n .-jte-icon {\n width: 1em;\n height: 1em;\n padding: 0.125em 0.25em;\n margin: 0 0.25em;\n vertical-align: top;\n\n color: var(--jte-curr-button-text);\n background-color: var(--jte-curr-button-background);\n border: 1px solid var(--jte-curr-button-border);\n border-radius: var(--jte-curr-border-radius);\n\n &.-jte-icon-mention {\n margin-left: 0.125em;\n }\n\n &.-jte-icon-send {\n margin-left: auto;\n margin-right: 0.125em;\n padding-left: 1em;\n padding-right: 1em;\n }\n\n &.-jte-disabled {\n color: var(--jte-curr-button-disabled-text);\n border-color: var(--jte-curr-button-disabled-border);\n background-color: var(--jte-curr-button-disabled-background);\n }\n\n &.-jte-active {\n color: var(--jte-curr-button-active-text);\n border-color: var(--jte-curr-button-active-border);\n background-color: var(--jte-curr-button-active-background);\n }\n\n &:active:not(.-jte-disabled) {\n color: var(--jte-curr-button-active-text);\n border-color: var(--jte-curr-button-active-border);\n background-color: var(--jte-curr-button-active-background);\n }\n\n &:hover:not(.-jte-disabled):not(.-jte-active):not(:active) {\n color: var(--jte-curr-button-hovered-text);\n border-color: var(--jte-curr-button-hovered-border);\n background-color: var(--jte-curr-button-hovered-background);\n }\n }\n }\n\n /* ===== PLACEHOLDER ==================================================== */\n\n .-jte-placeholder {\n position: absolute;\n padding: 0.25em;\n pointer-events: none;\n color: var(--jte-curr-inactive);\n }\n\n /* ===== EDITOR ========================================================= */\n\n .-jte-editor {\n width: 100%;\n box-sizing: border-box;\n padding: 0.25em;\n white-space: pre-wrap;\n\n border-radius: var(--jte-curr-border-radius);\n border: 1px solid var(--jte-curr-border);\n\n &:focus {\n outline: 1px solid var(--jte-curr-active);\n border: 1px solid var(--jte-curr-active);\n }\n\n /** Links */\n a {\n color: var(--jte-curr-link);\n }\n\n /** Our mentions (this needs to be \"inline\" for contenteditable to work) */\n link[rel=\"mention\"] {\n display: inline;\n\n cursor: default;\n user-select: none;\n font-weight: normal;\n font-style: normal;\n text-decoration: none;\n\n font-size: 0.85em;\n overflow: hidden;\n\n &:before {\n padding: 0 0.25em;\n content: '\\200b'; /* zero width space */\n\n background-color: var(--jte-curr-mention);\n border: 1px solid var(--jte-curr-border);\n border-radius: var(--jte-curr-border-radius);\n border-right: none;\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n\n &:after {\n content: attr(title);\n padding: 0 0.5em 0 0.25em;\n\n background-color: var(--jte-curr-shade);\n border: 1px solid var(--jte-curr-border);\n border-radius: var(--jte-curr-border-radius);\n border-left: none;\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n }\n }\n\n /* ===== POPUPS ========================================================= */\n\n /** Popups basics */\n .-jte-mentions-popup, .-jte-links-popup {\n display: none;\n position: absolute;\n z-index: 1000;\n\n font-size: 0.85em;\n background-color: var(--jte-curr-popup);\n border-radius: var(--jte-curr-border-radius);\n border: 1px solid var(--jte-curr-border);\n }\n\n /* ===== MENTIONS POPUP ================================================= */\n\n /** Our mentions popup */\n .-jte-mentions-popup {\n overflow: hidden;\n cursor: pointer;\n min-width: 8em;\n\n /** Our list of mentions */\n ul {\n overflow: scroll;\n max-height: calc(7.5em + 3px);\n white-space: nowrap;\n margin: 0;\n padding: 0;\n }\n\n /** Each mention entry */\n li {\n list-style-type: none;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n color: var(--jte-curr-inactive);\n\n /** Inside of our mentions entry */\n .-jte-mentions-entry {\n padding-block: 0.125em;\n padding-left: 0.25em;\n padding-right: 1.5em;\n border-left-width: 0.5em;\n border-left-style: solid;\n border-left-color: transparent;\n\n /** Refs want a smaller font */\n .-jte-mentions-ref {\n margin-left: 0.5em;\n font-size: 0.85em;\n font-style: italic;\n }\n }\n\n /** Selected list item */\n &.-jte-mentions-selected {\n color: var(--jte-curr-input-text);\n background-color: var(--jte-curr-shade);\n border-color: var(--jte-curr-input-border);\n\n /** Apply our \"tag\" to the slected mention */\n .-jte-mentions-entry {\n border-color: var(--jte-curr-mention);\n }\n }\n\n /** No borders for first and last item */\n &:first-child { border-top: none; }\n &:last-child { border-bottom: none; }\n }\n\n /** Basic style for waiting / no item found */\n .-jte-mentions-waiting, .-jte-mentions-none {\n font-size: 0.85em;\n color: var(--jte-curr-inactive);\n border-left-width: 0.5em;\n border-left-style: solid;\n border-color: transparent;\n padding-block: 0.125em;\n padding-inline: 0.25em;\n }\n\n /** Waiting is in italic */\n .-jte-mentions-waiting {\n font-style: italic;\n }\n\n /** None has a grey tab */\n .-jte-mentions-none {\n border-color: var(--jte-curr-shade);\n }\n }\n\n /* ===== LINKS POPUP ==================================================== */\n\n .-jte-links-popup {\n user-select: none;\n padding: 0.125em;\n width: 20em;\n flex-direction: column;\n\n div {\n display: flex;\n flex-direction: row;\n margin: 0.25em;\n position: relative;\n background-color: var(--jte-curr-input-background);\n border-radius: var(--jte-curr-border-radius);\n }\n\n .-jte-icon {\n display: block;\n position: absolute;\n\n top: 1px;\n left: 1px;\n\n width: 1.25em;\n height: 1.25em;\n padding: 0.125em 0.25em;\n margin: 0;\n\n color: var(--jte-curr-input-text);\n background-color: var(--jte-curr-input-shade);\n border: none;\n border-radius: calc(var(--jte-curr-border-radius) - 1px);\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n border-right: none;\n }\n\n input {\n display: block;\n flex-grow: 1;\n\n font-size: 1em;\n height: 1.25em;\n padding: 0.125em 0.25em 0.125em 2.25em;\n margin: 0;\n\n color: var(--jte-curr-input-text);\n border: 1px solid var(--jte-curr-input-border);\n border-radius: var(--jte-curr-border-radius);\n background-color: transparent;\n\n &:focus {\n outline: 1px solid var(--jte-curr-active);\n border: 1px solid var(--jte-curr-active);\n }\n }\n\n .-jte-error {\n .-jte-icon, input {\n color: var(--jte-curr-error);\n }\n }\n }\n }\n\n /* ===== TOOLBOX VISIBLE ONLY ON EDITABLE ================================= */\n\n &.-jte-editable {\n .-jte-editor {\n padding-bottom: 2.5em;\n margin-bottom: -2.5em;\n }\n\n .-jte-toolbox {\n display: flex;\n }\n }\n}\n</style>\n"],"names":["containsRange","haystack","needle","rangeFromContents","rangeFromNode","node","range","getCharacters","parent","child","offset","getRangeOffsets","result","getOffsetsRange","offsets","start","end","firstNode","lastNode","foundStart","foundEnd","iterator","text","length","STRIP_TAGS","parseURL","string","maybeWrap","fragment","tagName","noWrap","tag","sanitizeStyles","state","element","previous","_a","name","title","link","mergeOffsets","list","i","a","b","nested","outer","j","inner","sanitizeLinks","textRanges","re","plain","match","href","linkRanges","sanitizeEmpty","sanitize","sanitized","wrapped","replaceRange","html","body","beforeLength","parentRange","beforeRange","afterRange","before","after","afterLength","_b","getSelectionRange","selection","selectionRange","backwards","getSelectionOffsets","restoreSelection","collapse","startContainer","startOffset","endContainer","endOffset","isTagged","ancestor","toggleTag","attributes","ancestorRange","afterFragment","selectedFragment","beforeFragment","attr","_hoisted_1","render","_ctx","_cache","_openBlock","_createElementBlock","_createElementVNode","iconBold","iconItalic","iconLink","iconMention","iconSend","iconText","model","_useModel","__props","props","watch","dark","rootRef","shallowRef","editorRef","mentionsPopupRef","mentionsListRef","linksPopupRef","selected","ref","mentionsIndex","mentionsRange","mentionsText","computed","mentionsReady","mentionsEntries","_","value","selectedLink","activeLink","previousLink","isLink","linkText","linkHref","linkValid","commit","isBold","isItalic","dots","dotsString","dotsInterval","nextTag","newRange","oldRange","history","iconClass","historySave","historyUndo","entry","computeMention","applyMention","editor","selectWithOffsets","applyTag","insertMention","at","submit","onKeydown","event","mention","onPaste","data","onBeforeInput","onInput","mentions","onSelectionChange","_event","onMounted","root","mentionsPopup","mentionsList","linksPopup","available","nextTick","rootRect","rangeRect","editorRect","popupStyle","popupWidth","popupHeight","fitsTop","fitsBottom","fitsRight","index","entries","oldIndex","item","style","topBorder","bottomBorder","listRect","itemRect","topTop","btmBtm","relativeBtm","onUnmounted"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../lib/range.ts","../lib/sanitize.ts","../lib/replace.ts","../lib/selection.ts","../lib/tags.ts","../lib/svg/bold.svg","../lib/svg/italic.svg","../lib/svg/link.svg","../lib/svg/mention.svg","../lib/svg/send.svg","../lib/svg/text.svg","../lib/editor.vue"],"sourcesContent":["/** A DOM {@link Range} with an optional flag indicating its direction */\nexport interface DirectedRange extends Range {\n /** If `end` should come before `start` (for user selection) */\n backwards?: boolean\n}\n\n/** Offsets for a selected region (in characters) */\nexport interface Offsets {\n /** The start character position */\n start: number\n /** The end character position */\n end: number\n /** If the selection is backwards */\n backwards?: boolean\n}\n\n/**\n * Figure out if the specified `needle` {@link Range} is entirely enclosed in\n * the `haystack` {@link Range} or {@link Node} and return the `haystack` if\n * true.\n *\n * @param haystack The outer range\n * @param needle The inner range\n * @returns The `needle` if it is entirely enclosed in the `haystack`\n */\nexport function containsRange(\n haystack: Node | Range,\n needle: Range,\n): Range | null {\n if (haystack instanceof Node) haystack = rangeFromContents(haystack)\n\n // Check if the selection is entirely within the tag\n const inRange =\n haystack.isPointInRange(needle.startContainer, needle.startOffset) &&\n haystack.isPointInRange(needle.endContainer, needle.endOffset)\n\n // Return the needle if it is within the haystack\n return inRange ? needle : null\n}\n\n/**\n * Create a new {@link Range} object that selects the specified node\n *\n * @param node The node to select\n * @returns A new {@link Range} created with `selectNode(...)`\n */\nexport function rangeFromNode(node: Node): Range {\n const range = document.createRange()\n range.selectNode(node)\n return range\n}\n\n/**\n * Create a new {@link Range} object that selects the _contents_ of the\n * specified node\n *\n * @param node The node to select\n * @returns A new {@link Range} created with `selectNodeContents(...)`\n */\nexport function rangeFromContents(node: Node): Range {\n const range = document.createRange()\n range.selectNodeContents(node)\n return range\n}\n\n/**\n * Retrieve the characters contained in a parent {@link Node} up to the\n * specified {@link Node} and offset.\n */\nexport function getCharacters(\n parent: Node,\n child: Node,\n offset: number,\n): string {\n const parentRange = rangeFromContents(parent)\n\n if (! parentRange.isPointInRange(child, offset)) {\n throw new Error('Range out of bounds')\n }\n\n const range = document.createRange()\n range.setStart(parent, 0)\n range.setEnd(child, offset)\n\n return range.toString()\n}\n\n/**\n * Retrieve the character {@link Offsets} for the specified {@link Range}.\n *\n * @param parent The container {@link Node} for the {@link Range}\n * @param range The {@link Range} to calculate offsets for\n * @returns The {@link Offsets} for the specified {@link Range}\n */\nexport function getRangeOffsets(\n parent: Node,\n range: DirectedRange,\n): Offsets {\n const result = { start: 0, end: 0, backwards: range.backwards }\n\n result.start = result.end = getCharacters(parent, range.startContainer, range.startOffset).length\n if (range.collapsed) return result\n\n result.end = getCharacters(parent, range.endContainer, range.endOffset).length\n return result\n}\n\n/**\n * Return a {@link Range} for the specified character {@link Offsets}.\n *\n * @param parent The container {@link Node} for the {@link Range}\n * @returns The {@link Range} for the specified {@link Offsets}\n */\nexport function getOffsetsRange(\n parent: Node,\n offsets: Offsets,\n): DirectedRange {\n const range: DirectedRange = rangeFromContents(parent)\n if (offsets.backwards) range.backwards = true\n let { start, end } = offsets\n\n let firstNode: Text | null = null\n let lastNode: Text | null = null\n let foundStart: boolean | string = false\n let foundEnd: boolean | string = false\n\n const iterator = document.createNodeIterator(parent, NodeFilter.SHOW_TEXT)\n for (\n let text = iterator.nextNode() as Text | null;\n text && ((start >= 0) || (end > 0));\n text = iterator.nextNode() as Text | null\n ) {\n if (! firstNode) firstNode = text\n lastNode = text\n\n const length = text.nodeValue!.length\n\n // start is always calculated at the *start* of the text node\n if ((start >= 0) && (start < length)) {\n foundStart = true\n range.setStart(text, start)\n if (start === end) {\n foundEnd = 'extra'\n range.setEnd(text, end)\n return range\n }\n }\n\n // end is always calculated at the *end* of the text node\n if ((end > 0) && (end <= length)) {\n foundEnd = true\n range.setEnd(text, end)\n if (end === start) {\n foundStart = 'extra'\n range.setStart(text, start)\n return range\n }\n }\n\n start -= length\n end -= length\n }\n\n if ((! foundStart) && (firstNode)) range.setStart(firstNode, 0)\n if ((! foundEnd) && (lastNode)) range.setEnd(lastNode, lastNode.length)\n\n return range\n}\n","import { getOffsetsRange, getRangeOffsets, rangeFromContents } from './range'\n\nimport type { Offsets } from './range'\n\n/** Tags that will be stripped from output */\nconst STRIP_TAGS = new Set([\n 'button',\n 'embed',\n 'form',\n 'iframe',\n 'input',\n 'object',\n 'script',\n 'select',\n 'style',\n 'textarea',\n])\n\n/** Parse an URL and return the normalized HREF if it's a valid HTTP url */\nfunction parseURL(string?: string | null): string | null {\n if (! string) return null\n if (! string.match(/^https?:\\/\\/[^\\s]+$/)) return null\n try {\n return new URL(string).href\n } catch {\n return null\n }\n}\n\n/**\n * Wrap a document fragment in a tag unless `noWrap` is `true`\n *\n * @param fragment The fragment to wrap\n * @param tagName The tag to wrap the fragment in\n * @param noWrap If `true`, the fragment will not be wrapped\n * @param attribues Additional attributes to add to the tag\n * @returns The wrapped fragment or the fragment itself\n */\nfunction maybeWrap(\n fragment: DocumentFragment,\n tagName: string,\n noWrap: boolean = false,\n): DocumentFragment | Element {\n if (noWrap) return fragment\n\n const tag = document.createElement(tagName)\n tag.append(fragment)\n return tag\n}\n\n/**\n * Sanitize a {@link Node}'s content for styles (bold, italic, ...)\n *\n * The `state` object is used to track the current state of the sanitizer: if\n * `bold` is `true`, then all nested `<b>` and `<strong>` tags will be stripped,\n * the same works for `italic` with `<i>` and `<em>`.\n *\n * @param fragment The fragment to sanitize\n * @param state The current state of the sanitizer\n * @returns The sanitized fragment\n */\nfunction sanitizeStyles(\n parent: Element,\n state: {\n bold?: boolean,\n italic?: boolean,\n } = {},\n): DocumentFragment {\n const result = document.createDocumentFragment()\n\n for (const node of parent.childNodes) {\n // Text nodes are cloned if they have contents\n if (node.nodeType === Node.TEXT_NODE) {\n if ((node as Text).nodeValue) result.append(node.cloneNode())\n\n // Elements are processed recursively\n } else if (node.nodeType === Node.ELEMENT_NODE) {\n const element = node as Element\n const tagName = element.tagName.toLowerCase()\n\n // Strip unwanted tags\n if (STRIP_TAGS.has(tagName)) continue\n\n const previous = result.lastChild?.nodeType === Node.ELEMENT_NODE ? {\n element: result.lastChild as Element,\n tagName: (result.lastChild as Element).tagName.toLowerCase(),\n } : null\n\n const child = document.createDocumentFragment()\n\n // Bold / strong\n if ((tagName === 'b') || (tagName === 'strong')) {\n const fragment = sanitizeStyles(element, { ...state, bold: true })\n if (! fragment.hasChildNodes()) continue\n\n if (previous?.tagName === 'b') previous.element.append(fragment)\n else child.append(maybeWrap(fragment, 'b', state.bold))\n\n // Italic / emphasis\n } else if ((tagName === 'i') || (tagName === 'em')) {\n const fragment = sanitizeStyles(element, { ...state, italic: true })\n if (! fragment.hasChildNodes()) continue\n\n if (previous?.tagName === 'i') previous.element.append(fragment)\n else child.append(maybeWrap(fragment, 'i', state.italic))\n\n // Paragraphs, divs, line breaks\n } else if (tagName === 'br') {\n child.append(document.createElement('br'))\n } else if ((tagName === 'div') || (tagName === 'p') || (tagName === 'pre') ||\n (tagName === 'h1') || (tagName === 'h2') || (tagName === 'h3') ||\n (tagName === 'h4') || (tagName === 'h5') || (tagName === 'h6') ||\n (tagName === 'ul') || (tagName === 'ol') || (tagName === 'li')) {\n child.append(document.createElement('br'))\n const fragment = sanitizeStyles(element, state)\n if (fragment.hasChildNodes()) {\n child.append(fragment)\n child.append(document.createElement('br'))\n }\n\n // Mentions are implemented as <link rel=\"mention\" name=\"...\" title=\"...\">\n } else if (tagName === 'link') {\n const rel = element.getAttribute('rel')\n if (rel !== 'mention') continue\n\n const name = element.getAttribute('name')\n const title = element.getAttribute('title')\n if (!(name && title)) continue\n\n const link = document.createElement('link')\n link.setAttribute('rel', 'mention')\n link.setAttribute('title', title)\n link.setAttribute('name', name)\n child.append(link)\n\n // Anything else is processed recursively (strip tag, keep contents)\n } else {\n child.append(sanitizeStyles(element, state))\n }\n\n if (child.hasChildNodes()) result.append(child)\n }\n }\n\n return result\n}\n\n/** Merge offsets with HREF (exported for tests) */\nexport function mergeOffsets(list: (Offsets & { href: string })[]): (Offsets & { href: string })[] {\n // Map all our offsets to an array of objects with an index\n const offsets = list.map((offset, i) => ({ ...offset, index: i }))\n\n // Sort by start, keep longest first, if seame, the first one has priority\n offsets.sort((a, b) => {\n return a.start < b.start ? -1 :\n a.start > b.start ? 1 :\n a.end < b.end ? 1 :\n a.end > b.end ? -1 :\n /* v8 ignore next 3 */\n a.index < b.index ? -1 :\n a.index > b.index ? 1 :\n 0\n })\n\n // Find all ranges that are nested/overlapping within another range\n const nested = new Set<Offsets>()\n\n for (let i = 0; i < offsets.length; i++) {\n const outer = offsets[i]!\n for (let j = i + 1; j < offsets.length; j++) {\n const inner = offsets[j]!\n if (nested.has(inner)) continue\n if (inner.start === inner.end) nested.add(inner)\n else if ((inner.start >= outer.start) && (inner.start < outer.end)) nested.add(inner)\n else if ((inner.end >= outer.start) && (inner.end < outer.end)) nested.add(inner)\n }\n }\n\n // All our anchor offsets\n return offsets\n // Filter out any nested/overlapping offsets\n .filter((offset) => ! nested.has(offset))\n // Join consecutive offsets with the same href\n .filter((offset, i, offsets) => {\n const previous = offsets[i - 1]\n if ((previous?.end === offset.start) && (previous.href === offset.href)) {\n previous.end = offset.end\n return false\n } else {\n return true\n }\n })\n}\n\n/**\n * Find the various links in a {@link Node}, and return their offsets and hrefs.\n *\n * @param parent The {@link Node} for which links will be sanitized\n * @returns An array of offsets and related hrefs of all links\n */\nfunction sanitizeLinks(parent: Element): (Offsets & { href: string })[] {\n // Process links parsed directly from the text ()\n const textRanges: (Offsets & { href: string })[] = []\n\n const re = /(https?:\\/\\/[^\\s]+)/gm\n const plain = parent.textContent!\n for (let match = re.exec(plain); match; match = re.exec(plain)) {\n const text = match[1]!\n const href = parseURL(text)\n if (href) textRanges.push({ start: match.index, end: match.index + text.length, href })\n }\n\n // Process links parsed from <a> elements\n const linkRanges: (Offsets & { href: string })[] = []\n\n const iterator = document.createNodeIterator(parent, NodeFilter.SHOW_ELEMENT, (node) => {\n return node.nodeName.toLowerCase() === 'a' ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP\n })\n\n for (let node = iterator.nextNode(); node; node = iterator.nextNode()) {\n const element = node as Element\n\n const href = parseURL(element.getAttribute('href'))\n if (href) {\n const range = rangeFromContents(element)\n linkRanges.push({ ...getRangeOffsets(parent, range), href })\n }\n }\n\n return mergeOffsets([ ...textRanges, ...linkRanges ])\n}\n\n/**\n * Strip all empty elements out from the children of a specified {@link Node}.\n *\n * @param parent The {@link Element} to sanitize\n */\nfunction sanitizeEmpty(parent: Element): void {\n parent.normalize()\n\n const iterator = document.createNodeIterator(parent, NodeFilter.SHOW_ELEMENT)\n for (let node = iterator.nextNode(); node; node = iterator.nextNode()) {\n if (node === parent) continue // obviously skip the parent node\n if (node.nodeName.toLowerCase() === 'link') continue // mentions\n if (node.hasChildNodes()) continue // non-empty elements\n if (node.nodeName.toLowerCase() === 'br') { // keep line breaks\n node.parentNode!.replaceChild(document.createTextNode('\\n'), node)\n }\n // remove empty elements\n node.parentNode?.removeChild(node)\n }\n\n parent.normalize()\n // Edge case, empty editor\n if (! parent.hasChildNodes()) {\n parent.append(document.createElement('br'))\n }\n}\n\n/**\n * Sanitize the contents of a {@link Element} normalizing styles (bold,\n * italic, ...) and links.\n *\n * @param parent The {@link Element} to sanitize\n * @returns The same `parent` specified as an argument\n */\nexport function sanitize(parent: Element): Element {\n // Edge case: empty editor\n if (parent.innerHTML === '<br>') return parent\n\n // Cleanup before we start\n parent.normalize()\n\n // Save any and all links\n const offsets = sanitizeLinks(parent)\n\n // Sanitize the content\n const sanitized = sanitizeStyles(parent)\n // Append a space if the last child was a link (mention)\n if (sanitized.lastChild?.nodeName.toLowerCase() === 'link') {\n sanitized.append(document.createTextNode(' '))\n }\n\n // Replace our contents\n parent.replaceChildren(sanitized)\n\n // Restore the links\n offsets.forEach((offsets) => {\n const range = getOffsetsRange(parent, offsets)\n const wrapped = range.extractContents()\n const link = document.createElement('a')\n link.setAttribute('href', offsets.href)\n link.append(wrapped)\n range.insertNode(link)\n })\n\n // Cleanup any empty elements which might have been introduced\n // when restoring the links above\n sanitizeEmpty(parent)\n\n // Return the same element we were given\n return parent\n}\n","import { containsRange, getRangeOffsets, rangeFromContents } from './range'\nimport { sanitize } from './sanitize'\n\nimport type { Offsets } from './range'\n\nexport function replaceRange(\n parent: Element,\n range: Range,\n html: DocumentFragment | string,\n): Offsets {\n if (typeof html === 'string') {\n if (! containsRange(parent, range)) throw new Error('Range out of bounds')\n\n const body = new DOMParser().parseFromString(html, 'text/html').body\n html = document.createDocumentFragment()\n html.append(...body.childNodes)\n }\n\n // Save our selection offsets\n const offsets = getRangeOffsets(parent, range)\n const beforeLength = parent.textContent?.length || 0\n\n // Split into ranges (before and after)\n const parentRange = rangeFromContents(parent)\n\n const beforeRange = document.createRange()\n beforeRange.setStart(parentRange.startContainer, parentRange.startOffset)\n beforeRange.setEnd(range.startContainer, range.startOffset)\n\n const afterRange = document.createRange()\n afterRange.setStart(range.endContainer, range.endOffset)\n afterRange.setEnd(parentRange.endContainer, parentRange.endOffset)\n\n // Extract the content to keep\n const before = beforeRange.extractContents()\n const after = afterRange.extractContents()\n\n // Re-add all our content and sanitize\n parent.replaceChildren() // wipe everything\n parent.append(before, html, after)\n sanitize(parent)\n\n // Adjust our selection offsets depending on pasted data\n const afterLength = parent.textContent?.length || 0\n return { ...offsets, end: offsets.end + afterLength - beforeLength }\n}\n","import { containsRange, getOffsetsRange, getRangeOffsets } from './range'\n\nimport type { DirectedRange, Offsets } from './range'\n\n/**\n * Retrieve the current selection as a {@link DirectedRange} within a parent\n * {@link Element}\n *\n * @param parent The element defining the boundaries of accepatable selections\n * @returns The current selection as a {@link DirectedRange} if within bounds\n */\nexport function getSelectionRange(\n parent: Element,\n): DirectedRange | null {\n const selection = document.getSelection()\n if (! selection?.rangeCount) return null\n\n const selectionRange = selection.getRangeAt(0)\n if (! containsRange(parent, selectionRange)) return null\n\n const backwards =\n selection.isCollapsed ? false :\n selection.anchorNode === selection.focusNode ? selection.anchorOffset > selection.focusOffset :\n selection.anchorNode!.compareDocumentPosition(selection.focusNode!) === Node.DOCUMENT_POSITION_PRECEDING\n return Object.assign(selectionRange, { backwards })\n}\n\n/**\n * Retrieve the current selection as character {@link Offsets} within a parent\n * {@link Element}\n *\n * @param parent The element defining the boundaries of accepatable selections\n * @returns The current selection as a {@link DirectedRange} if within bounds\n */\nexport function getSelectionOffsets(\n parent: Element,\n): Offsets | null {\n const range = getSelectionRange(parent)\n return range ? getRangeOffsets(parent, range) : null\n}\n\n/**\n * Restore the current selection from character {@link Offsets} in the\n * specified {@link Element}\n *\n * @param parent The element defining the boundaries of accepatable selections\n * @param offsets The character offsets to restore\n */\nexport function restoreSelection(\n parent: Element,\n offsets: Offsets,\n collapse = false,\n): DirectedRange {\n if (collapse) offsets = { start: offsets.end, end: offsets.end }\n const range = getOffsetsRange(parent, offsets)\n const { startContainer, startOffset, endContainer, endOffset, backwards } = range\n\n // Selection can be forwards or backwards... restore correctly\n const selection = document.getSelection()\n\n if (selection) {\n if (backwards) {\n selection.setBaseAndExtent(endContainer, endOffset, startContainer, startOffset)\n } else {\n selection.setBaseAndExtent(startContainer, startOffset, endContainer, endOffset)\n }\n }\n\n return range\n}\n","import { containsRange, rangeFromContents, rangeFromNode } from './range'\nimport { sanitize } from './sanitize'\n\n/**\n * Figure out if the current selection entirely enclosed in an element\n * with the specified tag name (normally bold, italic, ...)\n *\n * @param parent The element whose contents we are searching\n * @param range The current selection (if any)\n * @param tagName The tag name to check for (e.g. 'b', 'i', ...)\n * @returns The parent node with the specified tag name if found\n */\nexport function isTagged(\n parent: Node,\n range: Range,\n tagName: string,\n): Element | null {\n // Get the parent range, and make sure the selection is within it\n const parentRange = rangeFromContents(parent)\n if (! containsRange(parent, range)) return null\n\n // Walk up from our common ancestor to to the parent to see if we find\n // an element with the specified tag name\n let ancestor: Node | null = range.commonAncestorContainer\n while (ancestor && parentRange.intersectsNode(ancestor)) {\n if ((ancestor.nodeType === Node.ELEMENT_NODE) &&\n (ancestor.nodeName.toLowerCase() === tagName)) {\n return ancestor as Element\n }\n ancestor = ancestor.parentNode\n }\n\n // Nothing found!\n return null\n}\n\n/**\n * Toggle a tag within the specified {@link Element}, around the specified\n * selection.\n *\n * @param parent The element whose contents we are modifying\n * @param range The range to toggle the tag around\n * @param tagName The tag name (e.g. 'b', 'i', ...) to toggle\n * @param attributes Any attributes to set on the tag (will be sanitized)\n */\nexport function toggleTag(\n parent: Element,\n range: Range,\n tagName: string,\n attributes: Record<string, string> = {},\n): void {\n if (range.collapsed) return\n if (! containsRange(parent, range)) return\n\n const ancestor = isTagged(parent, range, tagName)\n\n const ancestorRange = ancestor ?\n rangeFromNode(ancestor) :\n rangeFromContents(range.commonAncestorContainer)\n\n const before = document.createRange()\n before.setStart(ancestorRange.startContainer, ancestorRange.startOffset)\n before.setEnd(range.startContainer, range.startOffset)\n\n const after = document.createRange()\n after.setStart(range.endContainer, range.endOffset)\n after.setEnd(ancestorRange.endContainer, ancestorRange.endOffset)\n\n // Remove back to front...\n const afterFragment = after.extractContents()\n const selectedFragment = range.extractContents()\n const beforeFragment = before.extractContents()\n ancestorRange.deleteContents() // wipe everything, just in case\n\n // Create our fragment\n const fragment = document.createDocumentFragment()\n fragment.append(beforeFragment)\n\n // Append our selected fragment, possibly wrapped in a tag\n if (ancestor) {\n fragment.append(selectedFragment)\n } else {\n const tag = document.createElement(tagName)\n Object.entries(attributes).forEach((attr) => tag.setAttribute(...attr))\n tag.append(selectedFragment)\n fragment.append(tag)\n }\n\n // Surround with before and after fragments\n // fragment.insertBefore(beforeFragment, fragment.firstChild)\n fragment.append(afterFragment)\n\n // Insert and sanitize\n ancestorRange.insertNode(fragment)\n sanitize(parent)\n}\n","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M272-200v-560h221q65 0 120 40t55 111q0 51-23 78.5T602-491q25 11 55.5 41t30.5 90q0 89-65 124.5T501-200zm121-112h104q48 0 58.5-24.5T566-372t-10.5-35.5T494-432H393zm0-228h93q33 0 48-17t15-38q0-24-17-39t-44-15h-95z\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M200-200v-100h160l120-360H320v-100h400v100H580L460-300h140v100z\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M440-280H280q-83 0-141.5-58.5T80-480t58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85 35 85 85 35h160zM320-440v-80h320v80zm200 160v-80h160q50 0 85-35t35-85-35-85-85-35H520v-80h160q83 0 141.5 58.5T880-480t-58.5 141.5T680-280z\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M480-80q-83 0-156-31.5T197-197t-85.5-127T80-480t31.5-156T197-763t127-85.5T480-880t156 31.5T763-763t85.5 127T880-480v58q0 59-40.5 100.5T740-280q-35 0-66-15t-52-43q-29 29-65.5 43.5T480-280q-83 0-141.5-58.5T280-480t58.5-141.5T480-680t141.5 58.5T680-480v58q0 26 17 44t43 18 43-18 17-44v-58q0-134-93-227t-227-93-227 93-93 227 93 227 227 93h200v80zm0-280q50 0 85-35t35-85-35-85-85-35-85 35-35 85 35 85 85 35\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M792-443 176-183q-20 8-38-3.5T120-220v-520q0-22 18-33.5t38-3.5l616 260q25 11 25 37t-25 37M200-280l474-200-474-200v140l240 60-240 60zm0 0v-400z\" }, null, -1)\n ])))\n}\nexport default { render: render }","import { createElementVNode as _createElementVNode, openBlock as _openBlock, createElementBlock as _createElementBlock } from \"vue\"\n\nconst _hoisted_1 = {\n xmlns: \"http://www.w3.org/2000/svg\",\n fill: \"currentColor\",\n viewBox: \"0 -960 960 960\"\n}\n\nexport function render(_ctx, _cache) {\n return (_openBlock(), _createElementBlock(\"svg\", _hoisted_1, _cache[0] || (_cache[0] = [\n _createElementVNode(\"path\", { d: \"M280-160v-520H80v-120h520v120H400v520zm360 0v-320H520v-120h360v120H760v320z\" }, null, -1)\n ])))\n}\nexport default { render: render }","<template>\n <div class=\"juit-tiny-edit\" :class=\"{ '-jte-editable': editable, '-jte-dark': dark }\">\n <div ref=\"rootRef\" class=\"-jte-root\">\n <!-- our mentions popup -->\n <div ref=\"mentionsPopupRef\" class=\"-jte-mentions-popup\">\n <ul ref=\"mentionsListRef\">\n <li\n v-for=\"(mention, i) in mentionsEntries\"\n :key=\"i\"\n :class=\"{ '-jte-mentions-selected': mentionsIndex === i }\"\n @mousedown=\"applyMention(mention.name, mention.value)\"\n >\n <div class=\"-jte-mentions-entry\">\n {{ mention.value }}\n <span class=\"-jte-mentions-ref\">{{ mention.name }}</span>\n </div>\n </li>\n </ul>\n <div v-if=\"! mentionsReady\" class=\"-jte-mentions-waiting\">\n {{ mentionsText }}{{ dotsString }}\n </div>\n <div v-else-if=\"! mentionsEntries.length\" class=\"-jte-mentions-none\">\n {{ mentionsText }}\n </div>\n </div>\n\n <!-- our links popup -->\n <div\n ref=\"linksPopupRef\"\n class=\"-jte-links-popup\"\n @click=\"activeLink = previousLink\"\n @focusin=\"activeLink = previousLink\"\n >\n <div>\n <iconText class=\"-jte-icon\" />\n <input v-model=\"linkText\" type=\"text\">\n </div>\n <div :class=\"{ '-jte-error': !linkValid }\">\n <iconLink class=\"-jte-icon\" />\n <input v-model=\"linkHref\" type=\"text\">\n </div>\n </div>\n\n <div v-if=\"placeholder && (! html)\" class=\"-jte-placeholder\">\n {{ placeholder }}\n </div>\n\n <!-- our editor -->\n <div\n ref=\"editorRef\"\n class=\"-jte-editor\"\n :contenteditable=\"editable ? 'plaintext-only' : 'false'\"\n @beforeinput=\"onBeforeInput($event as InputEvent)\"\n @input=\"onInput($event as InputEvent)\"\n @keydown=\"onKeydown($event)\"\n @paste=\"onPaste($event)\"\n @focusin=\"activeLink = null\"\n />\n\n <div class=\"-jte-toolbox\">\n <iconMention class=\"-jte-icon -jte-icon-mention\" :class=\"{ '-jte-disabled': ! selected?.collapsed }\" @click=\"insertMention\" />\n <iconBold class=\"-jte-icon -jte-icon-bold\" :class=\"iconClass(isBold, 'b')\" @click=\"applyTag('b')\" />\n <iconItalic class=\"-jte-icon -jte-icon-italic\" :class=\"iconClass(isItalic, 'i')\" @click=\"applyTag('i')\" />\n <iconLink class=\"-jte-icon -jte-icon-href\" :class=\"iconClass(isLink)\" @click=\"applyTag('a', { href: 'http://__placeholder__/' })\" />\n <iconSend\n v-if=\"onSubmit\"\n class=\"-jte-icon -jte-icon-send\"\n :class=\"{ '-jte-disabled': !html, '-jte-active': !!html }\"\n @click=\"submit\"\n />\n </div>\n </div>\n </div>\n</template>\n\n<script setup lang=\"ts\">\nimport { computed, nextTick, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'\n\nimport { getCharacters, getOffsetsRange, getRangeOffsets } from './range'\nimport { replaceRange } from './replace'\nimport { sanitize } from './sanitize'\nimport { getSelectionOffsets, getSelectionRange, restoreSelection } from './selection'\nimport { isTagged, toggleTag } from './tags'\n// SVGs\nimport iconBold from './svg/bold.svg'\nimport iconItalic from './svg/italic.svg'\nimport iconLink from './svg/link.svg'\nimport iconMention from './svg/mention.svg'\nimport iconSend from './svg/send.svg'\nimport iconText from './svg/text.svg'\n\nimport type { PropType } from 'vue'\nimport type { Offsets } from './range'\n\n/* ==== DEFS ================================================================ */\n\ndefineOptions({ name: 'JuitTinyEditor' })\n\nconst model = defineModel({\n type: String,\n required: false,\n default: '',\n})\n\nconst props = defineProps({\n /** A flag indicating if content can be edited (default: `true`) */\n editable: {\n type: Boolean,\n required: false,\n default: true,\n },\n /** A flag indicating whether dark mode is enabled or not */\n dark: {\n type: Boolean,\n required: false,\n default: false,\n },\n /** A placeholder for when no text is available */\n placeholder: {\n type: String,\n required: false,\n default: '',\n },\n /** A list of mentions to be used in the editor */\n mentions: {\n type: Object as PropType<Record<string, string> | null>,\n required: false,\n default: () => ({}),\n },\n /** Used as `@mention=\"...\"`, notify the current mention text (`@abc...`) */\n onMention: {\n type: Function as PropType<((text: string) => any) | undefined>,\n required: false,\n default: undefined,\n },\n /** Used as `@submit=\"...\"`, will display the send button and emit HTML */\n onSubmit: {\n type: Function as PropType<((html: string) => any) | undefined>,\n required: false,\n default: undefined,\n },\n})\n\nwatch(() => props.dark, (dark) => {\n rootRef.value?.style.setProperty('color-scheme', dark ? 'dark light' : 'light dark')\n // document.body.classList.toggle('dark', dark)\n})\n\n/* ==== REFS ================================================================ */\n\n/** Reference to our root container */\nconst rootRef = shallowRef<HTMLDivElement | null>(null)\n/** Reference to our content editable DIV */\nconst editorRef = shallowRef<HTMLDivElement | null>(null)\n/** Reference to our popup */\nconst mentionsPopupRef = shallowRef<HTMLDivElement | null>(null)\n/** Reference to our mentions list (in the popup) DIV */\nconst mentionsListRef = shallowRef<HTMLDivElement | null>(null)\n/** Reference to our links popup */\nconst linksPopupRef = shallowRef<HTMLDivElement | null>(null)\n\n/** The `Range` of the current selection, if within our editor */\nconst selected = shallowRef<Range & { backwards?: boolean } | null>(null)\n\n/** The HTML string contained by the editor */\nconst html = ref('')\n\n/** Index of the current mention */\nconst mentionsIndex = ref(0)\n/** Range of the current mention (while being inserted) */\nconst mentionsRange = shallowRef<Range | null>(null)\n/** Text of the current mention (including the leading \"@\") */\nconst mentionsText = computed(() => mentionsRange.value?.toString() || '')\n/** Computed to see if mentions (from our prop) are ready */\nconst mentionsReady = computed(() => !! props.mentions)\n/** Computed list of mentions that match the current text */\nconst mentionsEntries = computed(() => {\n const text = mentionsText.value.substring(1).toLowerCase()\n\n if (! props.mentions) return []\n return Object.entries(props.mentions)\n .filter(([ _, name ]) => name.toLowerCase().startsWith(text))\n .sort(([ _a, a ], [ _b, b ]) => a.localeCompare(b))\n .map(([ name, value ]) => ({ name, value }))\n})\n\n/** The link associated with the *current* selection in the editor */\nconst selectedLink = ref<Element | null>(null)\n/** The link currently being handled by the links popup */\nconst activeLink = ref(null as any)\n/** The last link highlighted by the editor (updated by the watch below) */\nconst previousLink = ref<Element | null>(null)\n/** The current link, either in the editor or in the links popup */\nconst isLink = computed<Element | null>(() => selectedLink.value || activeLink.value)\n/* Watch the selected link and remember the last viable one */\nwatch(selectedLink, (link) => link && (previousLink.value = link))\n/** The text of the current link */\nconst linkText = ref('')\n/** The href of the current link */\nconst linkHref = ref('')\n/** Validate the link's own href (must be empty or an URL) */\nconst linkValid = computed(() => {\n if (! linkHref.value) return true\n try {\n new URL(linkHref.value)\n return true\n } catch {\n return false\n }\n})\n/** Update the content and href of the current link from the popup */\nwatch([ linkText, linkHref ], ([ text, href ]) => {\n if (! isLink.value) return\n isLink.value.textContent = text\n isLink.value.setAttribute('href', href)\n commit()\n})\n\n\n/** Whether the current selection is all bold */\nconst isBold = ref<Element | null>(null)\n/** Whether the current selection is all italic */\nconst isItalic = ref<Element | null>(null)\n\n/** How many dots are we displaying (in \"waiting for mentions\" animation) */\nconst dots = ref(0)\n/** String of dots to display (in \"waiting for mentions\" animation) */\nconst dotsString = computed(() => '.' + '.'.repeat(dots.value % 5))\n/** Interval animating our \"waiting for mentions\" text */\nlet dotsInterval: ReturnType<typeof setInterval> | null = null\n/** The _next_ tag to wrap our input into (when enabling bold on collapsed) */\nconst nextTag = ref('')\n/** When the selected range changes, reset the next tag */\nwatch((selected), (newRange, oldRange) => {\n if (oldRange === null) return\n if (newRange === null) return\n\n if ((oldRange.startContainer == newRange.startContainer) &&\n (oldRange.startOffset == newRange.startOffset) &&\n (oldRange.endContainer == newRange.endContainer) &&\n (oldRange.endOffset == newRange.endOffset) &&\n (oldRange?.backwards == newRange?.backwards)) return\n nextTag.value = ''\n})\n\n/** Our history (for undo) */\nconst history: (Offsets & { content: string })[] = []\n\n/* ==== FUNCTIONS =========================================================== */\n\n/** Compute the class for an icon (bold or italic) */\nfunction iconClass(element: Element | null, tag?: string): string {\n if (! selected.value) return '-jte-disabled'\n\n // If we don't have a tag, we're not processing zero-width selections\n if (! tag) {\n if (element) return '-jte-active'\n if (selected.value.collapsed) return '-jte-disabled'\n return ''\n }\n\n // styled, but next input will remove the style\n if (element && (nextTag.value === tag)) return ''\n // either styled *or* next input will add the style\n if (element || nextTag.value === tag) return '-jte-active'\n // not styled, but can be styled\n return ''\n}\n\n/** Commit changes to the model converting our editor into HTML */\nfunction commit(): void {\n if (! editorRef.value) return void (html.value = '')\n\n // Clean our HTML, then update the model\n const string = editorRef.value.innerHTML\n html.value = string === '<br>' ? '' : // edge case: empty editor\n string.replaceAll(' ', ' ').trimEnd() // remove trailing whitespace\n}\n\n/** Mark a new entry in our history */\nfunction historySave(): void {\n if (! editorRef.value) return\n\n // Save our history\n if (history[history.length - 1]?.content !== html.value) {\n const offsets = getSelectionOffsets(editorRef.value) || { start: 0, end: 0 }\n history.push({ ...offsets, content: html.value })\n if (history.length > 100) history.shift()\n }\n}\n\n/** Undo the last change */\nfunction historyUndo(): void {\n if (! editorRef.value) return\n if (! selected.value) return\n\n const entry = history.pop()\n if (! entry) return\n\n const body = new DOMParser().parseFromString(entry.content, 'text/html').body\n editorRef.value.replaceChildren(...body.childNodes)\n restoreSelection(editorRef.value, entry)\n}\n\n/** Compute the mention range according to changes th the selection */\nfunction computeMention(): void {\n if (mentionsRange.value && selected.value?.collapsed) {\n const range = mentionsRange.value.cloneRange()\n range.setEnd(selected.value.startContainer, selected.value.startOffset)\n if (range.toString().match(/^@[^\\r\\n]*$/)) {\n mentionsRange.value = range\n return\n }\n }\n mentionsRange.value = null\n}\n\n/** Replace the current mentions range with a proper mention */\nfunction applyMention(name: string, value: string): void {\n if (! editorRef.value) return\n if (! mentionsRange.value) return\n\n const editor = editorRef.value\n\n historySave()\n const fragment = document.createDocumentFragment()\n const element = document.createElement('link')\n element.setAttribute('rel', 'mention')\n element.setAttribute('name', name)\n element.setAttribute('title', value)\n fragment.append(element, ' ')\n\n const offsets = replaceRange(editor, mentionsRange.value, fragment)\n selectWithOffsets(offsets, true)\n mentionsRange.value = null\n commit()\n}\n\n/** Apply a \"tag\" (bold, italic, ...) */\nfunction applyTag(tagName: string, attributes: Record<string, string> = {}): void {\n if (! editorRef.value) return\n if (! selected.value) return\n\n const editor = editorRef.value\n\n // If nothing is selected, this is reserved for the next tag\n if (selected.value.collapsed) {\n nextTag.value = tagName\n editor.focus()\n return\n }\n\n historySave()\n const offsets = getRangeOffsets(editor, selected.value)\n toggleTag(editor, selected.value, tagName, attributes)\n selectWithOffsets(offsets)\n commit()\n}\n\nfunction insertMention(): void {\n if (! editorRef.value) return\n if (! selected.value?.collapsed) return\n\n const editor = editorRef.value\n\n const offsets = getRangeOffsets(editor, selected.value)\n\n const at = document.createTextNode('@')\n selected.value.insertNode(at)\n\n offsets.end += 1\n mentionsRange.value = getOffsetsRange(editor, offsets)\n offsets.start += 1\n selectWithOffsets(offsets)\n}\n\nfunction selectWithOffsets(offsets: Offsets, collapse?: boolean): void {\n if (! editorRef.value) return\n\n const editor = editorRef.value\n selected.value = restoreSelection(editor, offsets, collapse)\n editor.focus()\n}\n\nfunction submit(): void {\n if (! html.value) return\n props.onSubmit?.(html.value)\n}\n\n/* ==== DOM EVENTS ========================================================== */\n\n/** Trigger shortcuts for applying bold, italic, ... */\nfunction onKeydown(event: KeyboardEvent): void {\n if (event.metaKey || event.ctrlKey) {\n switch (event.key) {\n case 'b':\n applyTag('b')\n event.preventDefault()\n break\n case 'i':\n applyTag('i')\n event.preventDefault()\n break\n case 'z':\n historyUndo()\n event.preventDefault()\n break\n }\n } else if (mentionsRange.value) {\n switch (event.key) {\n case 'Home':\n mentionsIndex.value = 0\n event.preventDefault()\n break\n case 'ArrowUp':\n mentionsIndex.value = Math.max(mentionsIndex.value - 1, 0)\n event.preventDefault()\n break\n case 'PageUp':\n mentionsIndex.value = Math.max(mentionsIndex.value - 4, 0)\n event.preventDefault()\n break\n case 'ArrowDown':\n mentionsIndex.value = Math.min(mentionsIndex.value + 1, mentionsEntries.value.length - 1)\n event.preventDefault()\n break\n case 'PageDown':\n mentionsIndex.value = Math.min(mentionsIndex.value + 4, mentionsEntries.value.length - 1)\n event.preventDefault()\n break\n case 'End':\n mentionsIndex.value = mentionsEntries.value.length - 1\n event.preventDefault()\n break\n case 'Escape':\n mentionsRange.value = null\n event.preventDefault()\n break\n case 'Enter': {\n const mention = mentionsEntries.value[mentionsIndex.value]\n if (mention) applyMention(mention.name, mention.value)\n event.preventDefault()\n break\n }\n }\n }\n}\n\n/** Replace the current selection with the (sanitized) content from clipboard */\nfunction onPaste(event: ClipboardEvent): void {\n event.preventDefault()\n if (! editorRef.value) return\n if (! selected.value) return\n\n const data = event.clipboardData\n if (! data) return\n\n const text = data.getData('text/html') || data.getData('text/plain')\n if (! text) return\n\n const offsets = replaceRange(editorRef.value, selected.value, text)\n\n selectWithOffsets(offsets, true)\n commit()\n}\n\n/** If deleting a bunch of content, ensure links are removed, too... */\nfunction onBeforeInput(event: InputEvent): void {\n if (! editorRef.value) return\n if (! selected.value) return\n\n historySave()\n\n if ((!selected.value.collapsed) &&\n ((event.inputType === 'deleteContentBackward') ||\n (event.inputType === 'deleteContentForward'))) {\n event.preventDefault()\n event.stopPropagation()\n const offsets = replaceRange(editorRef.value, selected.value, '')\n selectWithOffsets(offsets, true)\n commit()\n }\n}\n\n/** Someone typed something in our editor, let's process it */\nfunction onInput(event: InputEvent): void {\n const editor = editorRef.value\n if (! editor) return\n\n // Save the current selection and mentions\n const mentions = mentionsRange.value ? getRangeOffsets(editor, mentionsRange.value) : null\n const selection = getSelectionOffsets(editor)\n\n // See if we need to process this event with style (bold, italic, ...)\n if (nextTag.value &&\n selection &&\n (selection.start === selection.end) &&\n (event.inputType === 'insertText') &&\n (event.data !== '@') &&\n (! mentionsRange.value)) {\n const range = getOffsetsRange(editor, { start: selection.start - 1, end: selection.end })\n toggleTag(editor, range, nextTag.value)\n }\n nextTag.value = ''\n\n // First thing first: sanitize the content (this will also take care of links)\n sanitize(editor)\n\n // Restore mentions range *after* sanitization\n if (mentions) mentionsRange.value = getOffsetsRange(editor, mentions)\n\n // Restore the selection range\n if (selection) {\n // Edge case when all content is deleted: insert a single \"<br>\"\n if (editor.innerHTML === '<br>') {\n document.getSelection()?.setBaseAndExtent(editor, 0, editor, 0)\n editor.focus()\n } else {\n selectWithOffsets(selection)\n }\n }\n\n // When the input is \"@\" we might want to trigger a mention\n if ((! isLink.value) &&\n (! mentionsRange.value) &&\n (event.data === '@') &&\n (event.inputType === 'insertText')) {\n const selection = document.getSelection()\n if (! selection?.anchorNode) return\n const text = getCharacters(editor, selection.anchorNode, selection.anchorOffset)\n if (text.match(/(\\s|^)@$/)) {\n const start = text.length === 1 ? 0 : text.length - 1\n const end = text.length\n const offsets: Offsets = { start, end }\n mentionsRange.value = getOffsetsRange(editor, offsets)\n }\n } else {\n computeMention()\n }\n\n // Commit the changes\n commit()\n}\n\n\n/* Update the `selected` Range when the document selection changes */\nfunction onSelectionChange(_event: Event): void {\n // Wipe all link information if we leave our component\n if (!(rootRef.value && getSelectionRange(rootRef.value))) {\n selectedLink.value = null\n previousLink.value = null\n activeLink.value = null\n }\n\n // Handle the current editor selection and remember its range (if any)\n selected.value = editorRef.value ? getSelectionRange(editorRef.value) : null\n\n // Process any changes to mentions\n computeMention()\n}\n\n/* ===== WATCHES AND COMPONENT LIFECYCLE ==================================== */\n\n/* Watch our local refs and emit */\nwatch(mentionsText, (text) => text && props.onMention?.(text))\n\nonMounted(() => {\n // Bind DOM events\n document.addEventListener('selectionchange', onSelectionChange)\n\n // References to our editor, popup, and list\n if (! rootRef.value) throw new Error('Root not referenced')\n if (! editorRef.value) throw new Error('Editor not referenced')\n if (! mentionsPopupRef.value) throw new Error('Popup not referenced')\n if (! mentionsListRef.value) throw new Error('List not referenced')\n if (! linksPopupRef.value) throw new Error('Popup not referenced')\n const root = rootRef.value\n const editor = editorRef.value\n const mentionsPopup = mentionsPopupRef.value\n const mentionsList = mentionsListRef.value\n const linksPopup = linksPopupRef.value\n\n /* On changes to the _local_ HTML, trigger an emit on our model value */\n watch(html, (html) => {\n if (html !== model.value) model.value = html // no changes, ignore!\n })\n\n /* Watch our model, when the value is different from the local HTML, parse */\n watch(model, (model) => {\n if (model === html.value) return // no changes, ignore!\n\n // wrap the text in <html><body>...</body></html>, if we don't do it then\n // any initial <link> tag will be moved to the <head> of the document...\n const string = `<html><body>${model}</body></html>`\n const body = new DOMParser().parseFromString(string, 'text/html').body\n sanitize(body)\n editor.replaceChildren(...body.childNodes)\n commit()\n }, { immediate: true })\n\n /* Watch our mentions availability and trigger a waiting animation */\n watch(mentionsReady, (available) => {\n dots.value = 0\n if (! available) dotsInterval = setInterval(() => dots.value = dots.value + 1, 200)\n else if (dotsInterval) clearInterval(dotsInterval)\n }, { immediate: true })\n\n /* Watch our links and update the popup */\n watch(isLink, (link) => {\n // Always update (or reset?) the link text and href of a link\n linkText.value = link?.textContent || ''\n linkHref.value = link?.getAttribute('href') || ''\n\n // If the link is a placeholder, consider this to be an empty link\n if (linkHref.value === 'http://__placeholder__/') {\n link?.setAttribute('href', '')\n linkHref.value = ''\n }\n\n // If we are no longer editing a link, sanitize and hide the popup\n if (! link) {\n const offsets = getSelectionOffsets(editor)\n sanitize(editor)\n if (offsets) restoreSelection(editor, offsets)\n\n linksPopup.style.display = 'none'\n linksPopup.style.top = '0'\n linksPopup.style.left = '0'\n return\n }\n\n // Wait for the next tick to make sure the DOM is updated\n nextTick(() => {\n // Position and show the popup\n linksPopup.style.display = 'flex'\n\n const rootRect = root.getBoundingClientRect()\n const rangeRect = link.getBoundingClientRect()\n const editorRect = editor.getBoundingClientRect()\n\n const popupStyle = getComputedStyle(linksPopup)\n const popupWidth = parseInt(popupStyle.width)\n const popupHeight = parseInt(popupStyle.height)\n\n const fitsTop = (rangeRect.top - popupHeight) - 6 > editorRect.top\n const fitsBottom = (rangeRect.bottom + popupHeight + 3) < editorRect.bottom\n const fitsRight = rangeRect.left + popupWidth < editorRect.right\n\n linksPopup.style.top = (! fitsBottom && fitsTop) ?\n `${rangeRect.top - popupHeight - rootRect.top - 6}px` : // over the link\n `${rangeRect.bottom - rootRect.top + 3}px` // below the link\n linksPopup.style.left = fitsRight ?\n `${rangeRect.left - rootRect.left}px` : // right of the link\n `${editorRect.right - popupWidth - rootRect.left}px` // aligned to the right of the editor\n })\n }, { immediate: true })\n\n /* Watch our mentions index and update the popup */\n watch([ mentionsIndex, mentionsRange, mentionsEntries ], ([ index, range, entries ], [ oldIndex = -1 ]) => {\n // Make sure that our index is within acceptable bounds\n if (index >= entries.length) index = Math.max(entries.length - 1, 0)\n mentionsIndex.value = index\n\n // No list or no popup, nothing to do\n if (! mentionsPopup) return\n if (! mentionsList) return\n\n // No range, hide and reset the popup\n if (! range) {\n mentionsIndex.value = 0\n mentionsPopup.style.display = 'none'\n mentionsPopup.style.top = '0'\n mentionsPopup.style.left = '0'\n mentionsList.scrollTop = 0\n return\n }\n\n // Wait for the next tick to make sure the DOM is updated\n nextTick(() => {\n // Position and show the popup\n mentionsPopup.style.display = 'block'\n\n const rootRect = root.getBoundingClientRect()\n const rangeRect = range.getBoundingClientRect()\n const editorRect = editor.getBoundingClientRect()\n\n const popupStyle = getComputedStyle(mentionsPopup)\n const popupWidth = parseInt(popupStyle.width)\n const popupHeight = parseInt(popupStyle.height)\n\n const fitsTop = (rangeRect.top - popupHeight) - 6 > editorRect.top\n const fitsBottom = (rangeRect.bottom + popupHeight + 3) < editorRect.bottom\n const fitsRight = rangeRect.left + popupWidth < editorRect.right\n\n mentionsPopup.style.top = (! fitsBottom && fitsTop) ?\n `${rangeRect.top - popupHeight - rootRect.top - 6}px` : // over the link\n `${rangeRect.bottom - rootRect.top + 3}px` // below the link\n mentionsPopup.style.left = fitsRight ?\n `${rangeRect.left - rootRect.left}px` : // right of the link\n `${editorRect.right - popupWidth - rootRect.left}px` // aligned to the right of the editor\n\n // Scroll the list to make sure the selected item is visible\n const item = mentionsList.querySelectorAll('li')[mentionsIndex.value]\n if (! item) return\n\n const style = getComputedStyle(item)\n const topBorder = parseFloat(style.getPropertyValue('border-top-width')) || 0\n const bottomBorder= parseFloat(style.getPropertyValue('border-bottom-width')) || 0\n\n // Get the bounding rectangles for the list and the selected item\n const listRect = mentionsList.getBoundingClientRect()\n const itemRect = item.getBoundingClientRect()\n\n // Some CSS magic to scroll the list\n const topTop = itemRect.top - listRect.top\n const btmBtm = itemRect.bottom - listRect.bottom\n const relativeBtm = itemRect.bottom - listRect.top\n\n // Going down\n if (index > oldIndex) {\n // Element is hidden below the visible list\n if (relativeBtm > listRect.height) {\n mentionsList.scrollTop += btmBtm - bottomBorder\n // Element is hidden above the visible list\n } else if (topTop < 0) {\n mentionsList.scrollTop += topTop + topBorder\n }\n // Going up\n } else {\n // Element is hidden above the visible list\n if (topTop < 0) {\n mentionsList.scrollTop += topTop + topBorder\n // Element is hidden below the visible list\n } else if (btmBtm > 0) {\n mentionsList.scrollTop += relativeBtm - listRect.height - bottomBorder\n }\n }\n })\n }, { immediate: true })\n\n /* Watch the selected range and infer state for bold, italic, ... */\n watch(selected, (selected) => {\n if (selected) {\n selectedLink.value = isTagged(editor, selected, 'a')\n isBold.value = isTagged(editor, selected, 'b')\n isItalic.value = isTagged(editor, selected, 'i')\n } else {\n selectedLink.value = null\n isBold.value = null\n isItalic.value = null\n }\n }, { immediate: true })\n})\n\nonUnmounted(() => {\n document.removeEventListener('selectionchange', onSelectionChange)\n if (dotsInterval) clearInterval(dotsInterval)\n})\n</script>\n\n<style lang=\"pcss\">\n.juit-tiny-edit {\n\n /* ===== LIGHT THEME (default) ============================================ */\n\n .-jte-root {\n /* border radius */\n --jte-curr-border-radius: var(--jte-border-radius, 4px);\n\n /* main colors */\n --jte-curr-text: var(--jte-text, currentColor);\n --jte-curr-background: var(--jte-background, transparent);\n --jte-curr-active: var(--jte-active, #009);\n --jte-curr-mention: var(--jte-mention, #99f);\n --jte-curr-popup: var(--jte-popup, #fff);\n --jte-curr-link: var(--jte-link, #009);\n --jte-curr-error: var(--jte-error, #933);\n\n /* derived colors */\n --jte-curr-inactive: var(--jte-inactive, color-mix(in srgb, var(--jte-curr-text) 50%, transparent));\n --jte-curr-border: var(--jte-border, color-mix(in srgb, var(--jte-curr-text) 12.5%, transparent));\n --jte-curr-shade: var(--jte-shade, color-mix(in srgb, var(--jte-curr-text) 6.25%, transparent));\n --jte-curr-active-shade: var(--jte-active-shade, color-mix(in srgb, var(--jte-curr-active) 6.25%, transparent));\n\n /* button colors */\n --jte-curr-button-text: var(--jte-button-text, var(--jte-curr-text));\n --jte-curr-button-border: var(--jte-button-border, var(--jte-curr-border));\n --jte-curr-button-background: var(--jte-button-background, var(--jte-curr-background));\n\n --jte-curr-button-hovered-text: var(--jte-button-hovered-text, var(--jte-curr-text));\n --jte-curr-button-hovered-border: var(--jte-button-hovered-border, var(--jte-curr-border));\n --jte-curr-button-hovered-background: var(--jte-button-hovered-background, var(--jte-curr-shade));\n\n --jte-curr-button-active-text: var(--jte-button-active-text, var(--jte-curr-active));\n --jte-curr-button-active-border: var(--jte-button-active-border, var(--jte-curr-border));\n --jte-curr-button-active-background: var(--jte-button-active-background, var(--jte-curr-active-shade));\n\n --jte-curr-button-disabled-text: var(--jte-button-disabled-text, var(--jte-curr-inactive));\n --jte-curr-button-disabled-border: var(--jte-button-disabled-border, var(--jte-curr-border));\n --jte-curr-button-disabled-background: var(--jte-button-disabled-background, var(--jte-curr-shade));\n\n /* inputs within popup */\n --jte-curr-input-text: var(--jte-input-text, var(--jte-curr-text));\n --jte-curr-input-background: var(--jte-input-background, transparent);\n --jte-curr-input-border: var(--jte-input-border, var(--jte-curr-border));\n --jte-curr-input-shade: var(--jte-input-shade, var(--jte-curr-shade));\n }\n\n /* ===== DARK THEME ======================================================= */\n\n &.-jte-dark .-jte-root {\n /* main colors */\n --jte-curr-text: var(--jte-dark-text, currentColor);\n --jte-curr-background: var(--jte-dark-background, transparent);\n --jte-curr-active: var(--jte-dark-active, #99f);\n --jte-curr-mention: var(--jte-dark-mention, #669);\n --jte-curr-popup: var(--jte-dark-popup, #111);\n --jte-curr-link: var(--jte-dark-link, #99f);\n --jte-curr-error: var(--jte-dark-error, #f99);\n\n /* derived colors */\n --jte-curr-inactive: var(--jte-dark-inactive, color-mix(in srgb, var(--jte-curr-text) 50%, transparent));\n --jte-curr-border: var(--jte-dark-border, color-mix(in srgb, var(--jte-curr-text) 12.5%, transparent));\n --jte-curr-shade: var(--jte-dark-shade, color-mix(in srgb, var(--jte-curr-text) 12.5%, transparent));\n --jte-curr-active-shade: var(--jte-dark-active-shade, color-mix(in srgb, var(--jte-curr-active) 6.25%, transparent));\n\n /* button colors */\n --jte-curr-button-text: var(--jte-dark-button-text, var(--jte-curr-text));\n --jte-curr-button-border: var(--jte-dark-button-border, var(--jte-curr-border));\n --jte-curr-button-background: var(--jte-dark-button-background, var(--jte-curr-background));\n\n --jte-curr-button-hovered-text: var(--jte-dark-button-hovered-text, var(--jte-curr-text));\n --jte-curr-button-hovered-border: var(--jte-dark-button-hovered-border, var(--jte-curr-border));\n --jte-curr-button-hovered-background: var(--jte-dark-button-hovered-background, var(--jte-curr-shade));\n\n --jte-curr-button-active-text: var(--jte-dark-button-active-text, var(--jte-curr-active));\n --jte-curr-button-active-border: var(--jte-dark-button-active-border, var(--jte-curr-border));\n --jte-curr-button-active-background: var(--jte-dark-button-active-background, var(--jte-curr-active-shade));\n\n --jte-curr-button-disabled-text: var(--jte-dark-button-disabled-text, var(--jte-curr-inactive));\n --jte-curr-button-disabled-border: var(--jte-dark-button-disabled-border, var(--jte-curr-border));\n --jte-curr-button-disabled-background: var(--jte-dark-button-disabled-background, var(--jte-curr-shade));\n\n /* inputs within popup */\n --jte-curr-input-text: var(--jte-dark-input-text, var(--jte-curr-text));\n --jte-curr-input-background: var(--jte-dark-input-background, transparent);\n --jte-curr-input-border: var(--jte-dark-input-border, var(--jte-curr-border));\n --jte-curr-input-shade: var(--jte-dark-input-shade, var(--jte-curr-shade));\n }\n\n /* ======================================================================== *\n * STYLING *\n * ======================================================================== */\n\n .-jte-root {\n display: flex;\n flex-direction: column;\n align-items: stretch;\n position: relative;\n box-sizing: content-box;\n\n color: var(--jte-curr-text);\n background-color: var(--jte-curr-background);\n\n /* ===== TOOLBAR ======================================================== */\n\n .-jte-toolbox {\n display: none;\n user-select: none;\n position: relative;\n height: 1.75em;\n overflow: hidden;\n margin-top: 0.75em;\n padding: 0 0.25em 0 0.25em;\n\n .-jte-icon {\n width: 1em;\n height: 1em;\n padding: 0.125em 0.25em;\n margin: 0 0.25em;\n vertical-align: top;\n\n color: var(--jte-curr-button-text);\n background-color: var(--jte-curr-button-background);\n border: 1px solid var(--jte-curr-button-border);\n border-radius: var(--jte-curr-border-radius);\n\n &.-jte-icon-mention {\n margin-left: 0.125em;\n }\n\n &.-jte-icon-send {\n margin-left: auto;\n margin-right: 0.125em;\n padding-left: 1em;\n padding-right: 1em;\n }\n\n &.-jte-disabled {\n color: var(--jte-curr-button-disabled-text);\n border-color: var(--jte-curr-button-disabled-border);\n background-color: var(--jte-curr-button-disabled-background);\n }\n\n &.-jte-active {\n color: var(--jte-curr-button-active-text);\n border-color: var(--jte-curr-button-active-border);\n background-color: var(--jte-curr-button-active-background);\n }\n\n &:active:not(.-jte-disabled) {\n color: var(--jte-curr-button-active-text);\n border-color: var(--jte-curr-button-active-border);\n background-color: var(--jte-curr-button-active-background);\n }\n\n &:hover:not(.-jte-disabled):not(.-jte-active):not(:active) {\n color: var(--jte-curr-button-hovered-text);\n border-color: var(--jte-curr-button-hovered-border);\n background-color: var(--jte-curr-button-hovered-background);\n }\n }\n }\n\n /* ===== PLACEHOLDER ==================================================== */\n\n .-jte-placeholder {\n position: absolute;\n padding: 0.25em;\n pointer-events: none;\n color: var(--jte-curr-inactive);\n }\n\n /* ===== EDITOR ========================================================= */\n\n .-jte-editor {\n width: 100%;\n box-sizing: border-box;\n padding: 0.25em;\n white-space: pre-wrap;\n\n border-radius: var(--jte-curr-border-radius);\n border: 1px solid var(--jte-curr-border);\n\n &:focus {\n outline: 1px solid var(--jte-curr-active);\n border: 1px solid var(--jte-curr-active);\n }\n\n /** Links */\n a {\n color: var(--jte-curr-link);\n }\n\n /** Our mentions (this needs to be \"inline\" for contenteditable to work) */\n link[rel=\"mention\"] {\n display: inline;\n\n cursor: default;\n user-select: none;\n font-weight: normal;\n font-style: normal;\n text-decoration: none;\n\n font-size: 0.85em;\n overflow: hidden;\n\n &:before {\n padding: 0 0.25em;\n content: '\\200b'; /* zero width space */\n\n background-color: var(--jte-curr-mention);\n border: 1px solid var(--jte-curr-border);\n border-radius: var(--jte-curr-border-radius);\n border-right: none;\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n }\n\n &:after {\n content: attr(title);\n padding: 0 0.5em 0 0.25em;\n\n background-color: var(--jte-curr-shade);\n border: 1px solid var(--jte-curr-border);\n border-radius: var(--jte-curr-border-radius);\n border-left: none;\n border-top-left-radius: 0;\n border-bottom-left-radius: 0;\n }\n }\n }\n\n /* ===== POPUPS ========================================================= */\n\n /** Popups basics */\n .-jte-mentions-popup, .-jte-links-popup {\n display: none;\n position: absolute;\n z-index: 1000;\n\n font-size: 0.85em;\n background-color: var(--jte-curr-popup);\n border-radius: var(--jte-curr-border-radius);\n border: 1px solid var(--jte-curr-border);\n }\n\n /* ===== MENTIONS POPUP ================================================= */\n\n /** Our mentions popup */\n .-jte-mentions-popup {\n overflow: hidden;\n cursor: pointer;\n min-width: 8em;\n\n /** Our list of mentions */\n ul {\n overflow: scroll;\n max-height: calc(7.5em + 3px);\n white-space: nowrap;\n margin: 0;\n padding: 0;\n }\n\n /** Each mention entry */\n li {\n list-style-type: none;\n border-top: 1px solid transparent;\n border-bottom: 1px solid transparent;\n color: var(--jte-curr-inactive);\n\n /** Inside of our mentions entry */\n .-jte-mentions-entry {\n padding-block: 0.125em;\n padding-left: 0.25em;\n padding-right: 1.5em;\n border-left-width: 0.5em;\n border-left-style: solid;\n border-left-color: transparent;\n\n /** Refs want a smaller font */\n .-jte-mentions-ref {\n margin-left: 0.5em;\n font-size: 0.85em;\n font-style: italic;\n }\n }\n\n /** Selected list item */\n &.-jte-mentions-selected {\n color: var(--jte-curr-input-text);\n background-color: var(--jte-curr-shade);\n border-color: var(--jte-curr-input-border);\n\n /** Apply our \"tag\" to the slected mention */\n .-jte-mentions-entry {\n border-color: var(--jte-curr-mention);\n }\n }\n\n /** No borders for first and last item */\n &:first-child { border-top: none; }\n &:last-child { border-bottom: none; }\n }\n\n /** Basic style for waiting / no item found */\n .-jte-mentions-waiting, .-jte-mentions-none {\n font-size: 0.85em;\n color: var(--jte-curr-inactive);\n border-left-width: 0.5em;\n border-left-style: solid;\n border-color: transparent;\n padding-block: 0.125em;\n padding-inline: 0.25em;\n }\n\n /** Waiting is in italic */\n .-jte-mentions-waiting {\n font-style: italic;\n }\n\n /** None has a grey tab */\n .-jte-mentions-none {\n border-color: var(--jte-curr-shade);\n }\n }\n\n /* ===== LINKS POPUP ==================================================== */\n\n .-jte-links-popup {\n user-select: none;\n padding: 0.125em;\n width: 20em;\n flex-direction: column;\n\n div {\n display: flex;\n flex-direction: row;\n margin: 0.25em;\n position: relative;\n background-color: var(--jte-curr-input-background);\n border-radius: var(--jte-curr-border-radius);\n }\n\n .-jte-icon {\n display: block;\n position: absolute;\n\n top: 1px;\n left: 1px;\n\n width: 1.25em;\n height: 1.25em;\n padding: 0.125em 0.25em;\n margin: 0;\n\n color: var(--jte-curr-input-text);\n background-color: var(--jte-curr-input-shade);\n border: none;\n border-radius: calc(var(--jte-curr-border-radius) - 1px);\n border-top-right-radius: 0;\n border-bottom-right-radius: 0;\n border-right: none;\n }\n\n input {\n display: block;\n flex-grow: 1;\n\n font-size: 1em;\n height: 1.25em;\n padding: 0.125em 0.25em 0.125em 2.25em;\n margin: 0;\n\n color: var(--jte-curr-input-text);\n border: 1px solid var(--jte-curr-input-border);\n border-radius: var(--jte-curr-border-radius);\n background-color: transparent;\n\n &:focus {\n outline: 1px solid var(--jte-curr-active);\n border: 1px solid var(--jte-curr-active);\n }\n }\n\n .-jte-error {\n .-jte-icon, input {\n color: var(--jte-curr-error);\n }\n }\n }\n }\n\n /* ===== TOOLBOX VISIBLE ONLY ON EDITABLE ================================= */\n\n &.-jte-editable {\n .-jte-editor {\n padding-bottom: 2.5em;\n margin-bottom: -2.5em;\n }\n\n .-jte-toolbox {\n display: flex;\n }\n }\n}\n</style>\n"],"names":["containsRange","haystack","needle","rangeFromContents","rangeFromNode","node","range","getCharacters","parent","child","offset","getRangeOffsets","result","getOffsetsRange","offsets","start","end","firstNode","lastNode","foundStart","foundEnd","iterator","text","length","STRIP_TAGS","parseURL","string","maybeWrap","fragment","tagName","noWrap","tag","sanitizeStyles","state","element","previous","_a","name","title","link","mergeOffsets","list","i","a","b","nested","outer","j","inner","sanitizeLinks","textRanges","re","plain","match","href","linkRanges","sanitizeEmpty","sanitize","sanitized","wrapped","replaceRange","html","body","beforeLength","parentRange","beforeRange","afterRange","before","after","afterLength","_b","getSelectionRange","selection","selectionRange","backwards","getSelectionOffsets","restoreSelection","collapse","startContainer","startOffset","endContainer","endOffset","isTagged","ancestor","toggleTag","attributes","ancestorRange","afterFragment","selectedFragment","beforeFragment","attr","_hoisted_1","render","_ctx","_cache","_openBlock","_createElementBlock","_createElementVNode","iconBold","iconItalic","iconLink","iconMention","iconSend","iconText","model","_useModel","__props","props","watch","dark","rootRef","shallowRef","editorRef","mentionsPopupRef","mentionsListRef","linksPopupRef","selected","ref","mentionsIndex","mentionsRange","mentionsText","computed","mentionsReady","mentionsEntries","_","value","selectedLink","activeLink","previousLink","isLink","linkText","linkHref","linkValid","commit","isBold","isItalic","dots","dotsString","dotsInterval","nextTag","newRange","oldRange","history","iconClass","historySave","historyUndo","entry","computeMention","applyMention","editor","selectWithOffsets","applyTag","insertMention","at","submit","onKeydown","event","mention","onPaste","data","onBeforeInput","onInput","mentions","onSelectionChange","_event","onMounted","root","mentionsPopup","mentionsList","linksPopup","available","nextTick","rootRect","rangeRect","editorRect","popupStyle","popupWidth","popupHeight","fitsTop","fitsBottom","fitsRight","index","entries","oldIndex","item","style","topBorder","bottomBorder","listRect","itemRect","topTop","btmBtm","relativeBtm","onUnmounted","_normalizeClass","_Fragment","_renderList","$event","_hoisted_2","_hoisted_3","_toDisplayString","_hoisted_5","_hoisted_4","_createVNode","_unref","_hoisted_6","_hoisted_8","_createBlock"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juit/vue-tiny-editor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.29",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -17,23 +17,23 @@
|
|
|
17
17
|
"test": "vitest"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
|
-
"vue": "^3.5.
|
|
20
|
+
"vue": "^3.5.16"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@plugjs/eslint-plugin": "^0.3.
|
|
23
|
+
"@plugjs/eslint-plugin": "^0.3.26",
|
|
24
24
|
"@types/node": "<21",
|
|
25
25
|
"@vitejs/plugin-vue": "^5.2.4",
|
|
26
|
-
"@vitest/coverage-v8": "^3.1.
|
|
26
|
+
"@vitest/coverage-v8": "^3.1.4",
|
|
27
27
|
"@vue/test-utils": "^2.4.6",
|
|
28
|
-
"eslint": "^9.
|
|
28
|
+
"eslint": "^9.27.0",
|
|
29
29
|
"eslint-plugin-vue": "^10.1.0",
|
|
30
30
|
"jsdom": "^26.1.0",
|
|
31
31
|
"typescript": "<5.7",
|
|
32
32
|
"vite": "<6",
|
|
33
33
|
"vite-plugin-dts": "^4.5.4",
|
|
34
34
|
"vite-svg-loader": "^5.1.0",
|
|
35
|
-
"vitest": "^3.1.
|
|
36
|
-
"vue": "^3.5.
|
|
35
|
+
"vitest": "^3.1.4",
|
|
36
|
+
"vue": "^3.5.16",
|
|
37
37
|
"vue-tsc": "^2.2.10"
|
|
38
38
|
},
|
|
39
39
|
"author": "Juit Developers <developers@juit.com>",
|