{"mappings":"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","names":["globalKey","prismGlobal","plainTextGrammar","rest","tokenize","languages","prismGlobal","plainTextGrammar","doc","addListener","target","type","listener","options","numLines","str","start","end","count","languageMap","selectionChange","doc","addListener","getLineEnd","text","position","addListener2","element","type","listener","options","addListener","addTextareaListener","editor","voidlessLangs","voidTags","getClosestToken","editor","selector","marginLeft","marginRight","position","value","line","numLines","walker","doc","node","offset","getLineEnd","userAgent","doc","isMac","isChrome","isWebKit","createTagMatcher","editor","pairMap","code","tags","tagIndex","sp","stack","matchTags","tokens","language","value","matchTagsRecursive","position","noVoidTags","voidlessLangs","i","l","token","content","length","openLen","name","tagName","notSelfClosing","voidTags","lang","getClosestTagIndex","pos","openEl","closeEl","pairs","highlight","remove","el","start","end","newEl1","newEl2","index","getClosestToken","highlightTagPunctuation","className","alwaysHighlight","getPunctuation","selectionChange","tag","addTextareaListener"],"sources":["../esm/npm/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.2/node_modules/@cyfung1031/prism-code-editor/src/prism/core.js","../esm/npm/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.2/node_modules/@cyfung1031/prism-code-editor/src/core.ts","../esm/npm/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.2/node_modules/@cyfung1031/prism-code-editor/src/utils/local.ts","../esm/npm/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.2/node_modules/@cyfung1031/prism-code-editor/src/utils/index.ts","../esm/npm/@cyfung1031/prism-code-editor@5.2.0-cdn-esm.2/node_modules/@cyfung1031/prism-code-editor/src/extensions/matchTags.ts"],"sourcesContent":["// Slimmed down Prism core with most high level functions removed\n\nvar globalKey = Symbol.for(\"prism-code-editor.prism\");\nvar prismGlobal = globalThis[globalKey] || (globalThis[globalKey] = {});\n\nvar plainTextGrammar = prismGlobal.plainTextGrammar || (prismGlobal.plainTextGrammar = {});\nvar rest = prismGlobal.rest || (prismGlobal.rest = Symbol.for(\"prism-code-editor.prism.rest\"));\nvar tokenize =\n\tprismGlobal.tokenize || (prismGlobal.tokenize = Symbol.for(\"prism-code-editor.prism.tokenize\"));\n\n/** @param {*} id */\nvar resolve = id =\u003e typeof id == 'string' ? languages[id] : id;\n\n/** @type {Record\u003cstring, any\u003e} */\nvar languages =\n\tprismGlobal.languages ||\n\t(prismGlobal.languages = {\n\t\tplain: plainTextGrammar,\n\t\tplaintext: plainTextGrammar,\n\t\ttext: plainTextGrammar,\n\t\ttxt: plainTextGrammar,\n\t});\n\n/**\n * @param {string} text\n * @param {any} grammar\n */\nvar tokenizeText = (text, grammar) =\u003e (grammar[tokenize] || withoutTokenizer)(text, grammar);\n\n/**\n * @param {string} text\n * @param {any} grammar\n */\nvar withoutTokenizer = (text, grammar) =\u003e {\n\t/** @type {LinkedListNode} */\n\tvar startNode = [text];\n\tvar restGrammar;\n\t/** @type {(string | Token)[]} */\n\tvar array = [], i = 0;\n\twhile (restGrammar = resolve(grammar[rest])) {\n\t\tdelete grammar[rest];\n\t\tObject.assign(grammar, restGrammar);\n\t}\n\n\tmatchGrammar(text, grammar, startNode, 0);\n\n\twhile (array[i++] = startNode[0], startNode = startNode[1]);\n\treturn array;\n};\n\n/**\n * @param {string} string\n * @param {RegExp} pattern\n * @param {string} replacement\n */\nvar escapeHtml = (string, pattern, replacement) =\u003e {\n\treturn string.replace(/\u0026/g, '\u0026amp;').replace(pattern, replacement);\n};\n\nvar closingTag = '\u003c/span\u003e';\nvar openingTags = '';\nvar closingTags = '';\n\n/** @param {(string | Token)[]} tokens */\nvar highlightTokens = tokens =\u003e {\n\tvar str = '', token, i = 0;\n\twhile (token = tokens[i++]) str += stringify(token);\n\treturn str;\n};\n\n/**\n * @param {string | Token | (string | Token)[]} token\n * @returns {string}\n */\nvar stringify = token =\u003e {\n\tif (token instanceof Token) {\n\t\tvar { type, alias, content } = token;\n\t\tvar prevOpening = openingTags;\n\t\tvar prevClosing = closingTags;\n\t\tvar opening = `\u003cspan class=\"token ${\n\t\t\ttype + (alias ? ' ' + alias : '') +\n\t\t\t(type == 'keyword' \u0026\u0026 typeof content == 'string' ? ' keyword-' + content : '')\n\t\t}\"\u003e`;\n\n\t\tclosingTags += closingTag;\n\t\topeningTags += opening;\n\t\tvar contentStr = stringify(content);\n\t\topeningTags = prevOpening;\n\t\tclosingTags = prevClosing;\n\t\treturn opening + contentStr + closingTag;\n\t}\n\n\tif (typeof token != 'string') return highlightTokens(token);\n\n\ttoken = escapeHtml(token, /\u003c/g, '\u0026lt;');\n\tif (closingTags \u0026\u0026 token.includes('\\n')) {\n\t\treturn token.replace(/\\n/g, closingTags + '\\n' + openingTags);\n\t}\n\treturn token;\n};\n\n/**\n * @param {string} text\n * @param {*} ref\n */\nvar highlightText = (text, ref) =\u003e highlightTokens(tokenizeText(text, resolve(ref)));\n\n/**\n * @param {string} text\n * @param {any} grammar\n * @param {LinkedListNode} startNode\n * @param {number} startPos\n * @param {[string, number, number]} [rematch]\n *\n * @typedef {[string | Token, LinkedListNode?]} LinkedListNode\n */\nvar matchGrammar = (text, grammar, startNode, startPos, rematch) =\u003e {\n\tfor (var token in grammar) {\n\t\tif (grammar[token]) for (var j = 0, p = grammar[token], patternObj, patterns = Array.isArray(p) ? p : [p]; patternObj = patterns[j]; j++) {\n\t\t\tif (rematch \u0026\u0026 rematch[0] == token \u0026\u0026 rematch[1] == j) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t/** @type {RegExp} */\n\t\t\tvar pattern = patternObj.pattern || patternObj;\n\t\t\tvar inside = resolve(patternObj.inside);\n\t\t\tvar lookbehind = patternObj.lookbehind;\n\t\t\tvar greedy = pattern.global;\n\t\t\tvar alias = patternObj.alias;\n\n\t\t\tfor ( // iterate the token list and keep track of the current token/string position\n\t\t\t\tvar currentNode = startNode, pos = startPos;\n\t\t\t\tcurrentNode \u0026\u0026 (!rematch || pos \u003c rematch[2]);\n\t\t\t\tpos += currentNode[0].length, currentNode = currentNode[1]\n\t\t\t) {\n\t\t\t\tvar str = currentNode[0];\n\t\t\t\tvar removeCount = 0;\n\t\t\t\tvar match;\n\n\t\t\t\tif (str instanceof Token) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tpattern.lastIndex = greedy ? pos : 0;\n\t\t\t\tmatch = pattern.exec(greedy ? text : str);\n\n\t\t\t\tif (!match \u0026\u0026 greedy) {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tif (!(match \u0026\u0026 match[0])) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// change the match to remove the text matched by the Prism lookbehind group\n\t\t\t\tvar lookbehindLength = lookbehind \u0026\u0026 match[1] ? match[1].length : 0;\n\t\t\t\tvar from = match.index + lookbehindLength;\n\t\t\t\tvar matchStr = match[0].slice(lookbehindLength);\n\t\t\t\tvar to = from + matchStr.length;\n\t\t\t\tvar k, p;\n\n\t\t\t\tif (greedy) {\n\t\t\t\t\t// find the node that contains the match\n\t\t\t\t\tfor (\n\t\t\t\t\t\t;\n\t\t\t\t\t\tp = pos + currentNode[0].length, from \u003e= p;\n\t\t\t\t\t\tcurrentNode = currentNode[1], pos = p\n\t\t\t\t\t);\n\n\t\t\t\t\t// the current node is a Token, then the match starts inside another Token, which is invalid\n\t\t\t\t\tif (currentNode[0] instanceof Token) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t// find the last node which is affected by this match\n\t\t\t\t\tfor (\n\t\t\t\t\t\tk = currentNode, p = pos;\n\t\t\t\t\t\t(p += k[0].length) \u003c to;\n\t\t\t\t\t\tk = k[1], removeCount++\n\t\t\t\t\t);\n\n\t\t\t\t\t// replace with the new match\n\t\t\t\t\tstr = text.slice(pos, p);\n\t\t\t\t\tfrom -= pos;\n\t\t\t\t\tto -= pos;\n\t\t\t\t}\n\n\t\t\t\tvar after = str.slice(to);\n\t\t\t\tvar reach = pos + str.length;\n\t\t\t\tvar newToken = new Token(token, inside ? tokenizeText(matchStr, inside) : matchStr, matchStr, alias);\n\t\t\t\tvar next = currentNode, i = 0;\n\t\t\t\tvar nestedRematch;\n\n\t\t\t\twhile (next = next[1], i++ \u003c removeCount);\n\n\t\t\t\tif (after) {\n\t\t\t\t\tif (!next || next[0] instanceof Token) next = [after, next];\n\t\t\t\t\telse next[0] = after + next[0];\n\t\t\t\t};\n\n\t\t\t\tpos += from;\n\t\t\t\tcurrentNode[0] = from ? str.slice(0, from) : newToken;\n\n\t\t\t\tif (from) currentNode = currentNode[1] = [newToken, next];\n\t\t\t\telse currentNode[1] = next;\n\n\t\t\t\tif (removeCount) {\n\t\t\t\t\t// at least one Token object was removed, so we have to do some rematching\n\t\t\t\t\t// this can only happen if the current pattern is greedy\n\n\t\t\t\t\tmatchGrammar(text, grammar, currentNode, pos, nestedRematch = [token, j, reach]);\n\t\t\t\t\treach = nestedRematch[2];\n\t\t\t\t}\n\n\t\t\t\tif (rematch \u0026\u0026 reach \u003e rematch[2]) rematch[2] = reach;\n\t\t\t}\n\t\t}\n\t}\n};\n\n/**\n * Creates a new token.\n *\n * @param {string} type\n * @param {string | any[]} content\n * @param {string} matchedStr\n * @param {string} alias\n * @class\n */\nfunction Token(type, content, matchedStr, alias) {\n\tthis.type = type;\n\tthis.content = content;\n\tthis.alias = alias;\n\tthis.length = matchedStr.length;\n}\n\nexport {\n\trest,\n\ttokenize,\n\tToken,\n\tlanguages,\n\ttokenizeText,\n\twithoutTokenizer,\n\tresolve,\n\tescapeHtml,\n\thighlightTokens,\n\thighlightText\n};\n","import type {\r\n\tEditorOptions,\r\n\tPrismEditor,\r\n\tKeyCommandCallback,\r\n\tLanguage,\r\n\tInputCommandCallback,\r\n\tEditorEventMap,\r\n\tInputSelection,\r\n\tEditorExtension,\r\n} from \"./types.js\"\r\nimport { highlightTokens, languages, tokenizeText, TokenStream } from \"./prism/index.js\"\r\n\r\n/**\r\n * Creates a code editor using the specified container and options.\r\n * @param container Element to append the editor to or a selector.\r\n * This can also be a `ShadowRoot` or `DocumentFragment` for example.\r\n * If omitted, you must manually append `editor.container` to the DOM.\r\n * @param options Options the editor is initialized with.\r\n * If omitted, the editor won't function until you call `editor.setOptions`.\r\n * @param extensions Extensions added before the first render. You can still add extensions later.\r\n * @returns Object to interact with the created editor.\r\n */\r\nconst createEditor = \u003cT extends {} = {}\u003e(\r\n\tcontainer?: ParentNode | string | null,\r\n\toptions?: (Partial\u003cEditorOptions\u003cT\u003e\u003e \u0026 Omit\u003cT, keyof EditorOptions\u003e) | null,\r\n\t...extensions: EditorExtension\u003cT\u003e[]\r\n): PrismEditor\u003cT\u003e =\u003e {\r\n\tlet language: string\r\n\tlet prevLines: string[] = []\r\n\tlet activeLine: HTMLDivElement\r\n\tlet value = \"\"\r\n\tlet activeLineNumber: number\r\n\tlet focused = false\r\n\tlet handleSelectionChange = true\r\n\tlet tokens: TokenStream = []\r\n\tlet readOnly: boolean\r\n\tlet lineCount = 0\r\n\r\n\tconst scrollContainer = editorTemplate()\r\n\tconst wrapper = \u003cHTMLDivElement\u003escrollContainer.firstChild\r\n\tconst lines = \u003cHTMLCollectionOf\u003cHTMLDivElement\u003e\u003ewrapper.children\r\n\tconst overlays = lines[0]\r\n\tconst textarea = \u003cHTMLTextAreaElement\u003eoverlays.firstChild\r\n\tconst currentOptions = { language: \"text\", value } as EditorOptions \u0026 T\r\n\tconst currentExtensions = new Set(extensions)\r\n\tconst listeners: {\r\n\t\t[P in keyof EditorEventMap]?: Set\u003cEditorEventMap\u003cT\u003e[P]\u003e\r\n\t} = {}\r\n\r\n\tconst setOptions = (options: Partial\u003cEditorOptions\u003e \u0026 Partial\u003cOmit\u003cT, keyof EditorOptions\u003e\u003e) =\u003e {\r\n\t\tObject.assign(currentOptions, options)\r\n\t\tlet isNewVal = value != (value = options.value ?? value)\r\n\t\tlet isNewLang = language != (language = currentOptions.language)\r\n\r\n\t\treadOnly = !!currentOptions.readOnly\r\n\t\tscrollContainer.style.tabSize = \u003cany\u003ecurrentOptions.tabSize || 2\r\n\t\ttextarea.inputMode = readOnly ? \"none\" : \"\"\r\n\t\ttextarea.setAttribute(\"aria-readonly\", \u003cany\u003ereadOnly)\r\n\t\tupdateClassName()\r\n\t\tupdateExtensions()\r\n\r\n\t\tif (isNewVal) {\r\n\t\t\tif (!focused) textarea.remove()\r\n\t\t\ttextarea.value = value\r\n\t\t\ttextarea.selectionEnd = 0\r\n\t\t\tif (!focused) overlays.prepend(textarea)\r\n\t\t}\r\n\r\n\t\tif (isNewVal || isNewLang) {\r\n\t\t\tupdate()\r\n\t\t}\r\n\t}\r\n\r\n\tconst update = () =\u003e {\r\n\t\ttokens = tokenizeText((value = textarea.value), languages[language] || {})\r\n\t\tdispatchEvent(\"tokenize\", tokens, language, value)\r\n\r\n\t\tlet newLines = highlightTokens(tokens).split(\"\\n\")\r\n\t\tlet start = 0\r\n\t\tlet end2 = lineCount\r\n\t\tlet end1 = (lineCount = newLines.length)\r\n\r\n\t\twhile (newLines[start] == prevLines[start] \u0026\u0026 start \u003c end1) ++start\r\n\t\twhile (end1 \u0026\u0026 newLines[--end1] == prevLines[--end2]);\r\n\r\n\t\tif (start == end1 \u0026\u0026 start == end2) lines[start + 1].innerHTML = newLines[start] + \"\\n\"\r\n\t\telse {\r\n\t\t\tlet insertStart = end2 \u003c start ? end2 : start - 1\r\n\t\t\tlet i = insertStart\r\n\t\t\tlet newHTML = \"\"\r\n\r\n\t\t\twhile (i \u003c end1) newHTML += `\u003cdiv class=pce-line aria-hidden=true\u003e${newLines[++i]}\\n\u003c/div\u003e`\r\n\t\t\tfor (i = end1 \u003c start ? end1 : start - 1; i \u003c end2; i++) lines[start + 1].remove()\r\n\t\t\tif (newHTML) lines[insertStart + 1].insertAdjacentHTML(\"afterend\", newHTML)\r\n\t\t\tscrollContainer.style.setProperty(\r\n\t\t\t\t\"--number-width\",\r\n\t\t\t\t(0 | Math.log10(lineCount)) + 1 + \".001ch\",\r\n\t\t\t)\r\n\t\t}\r\n\r\n\t\tdispatchEvent(\"update\", value)\r\n\t\tdispatchSelection(true)\r\n\t\tif (handleSelectionChange) setTimeout(setTimeout, 0, () =\u003e (handleSelectionChange = true))\r\n\r\n\t\tprevLines = newLines\r\n\t\thandleSelectionChange = false\r\n\t}\r\n\r\n\tconst updateExtensions = (newExtensions?: EditorExtension\u003cT\u003e[]) =\u003e {\r\n\t\t;(newExtensions || currentExtensions).forEach(extension =\u003e {\r\n\t\t\tif (typeof extension == \"object\") {\r\n\t\t\t\textension.update(self, currentOptions)\r\n\t\t\t\tif (newExtensions) currentExtensions.add(extension)\r\n\t\t\t} else {\r\n\t\t\t\textension(self, currentOptions)\r\n\t\t\t\tif (!newExtensions) currentExtensions.delete(extension)\r\n\t\t\t}\r\n\t\t})\r\n\t}\r\n\r\n\tconst updateClassName = ([start, end] = getInputSelection()) =\u003e {\r\n\t\tscrollContainer.className = `prism-code-editor language-${language}${\r\n\t\t\tcurrentOptions.lineNumbers == false ? \"\" : \" show-line-numbers\"\r\n\t\t} pce-${currentOptions.wordWrap ? \"\" : \"no\"}wrap${currentOptions.rtl ? \" pce-rtl\" : \"\"} pce-${\r\n\t\t\tstart \u003c end ? \"has\" : \"no\"\r\n\t\t}-selection${focused ? \" pce-focus\" : \"\"}${readOnly ? \" pce-readonly\" : \"\"}${\r\n\t\t\tcurrentOptions.class ? \" \" + currentOptions.class : \"\"\r\n\t\t}`\r\n\t}\r\n\r\n\tconst getInputSelection = (): InputSelection =\u003e [\r\n\t\ttextarea.selectionStart,\r\n\t\ttextarea.selectionEnd,\r\n\t\ttextarea.selectionDirection,\r\n\t]\r\n\r\n\tconst keyCommandMap: Record\u003cstring, KeyCommandCallback | null\u003e = {\r\n\t\tEscape() {\r\n\t\t\ttextarea.blur()\r\n\t\t},\r\n\t}\r\n\r\n\tconst inputCommandMap: Record\u003cstring, InputCommandCallback | null\u003e = {}\r\n\r\n\tconst dispatchEvent = \u003cT extends keyof EditorEventMap\u003e(\r\n\t\tname: T,\r\n\t\t...args: Parameters\u003cEditorEventMap[T]\u003e\r\n\t) =\u003e {\r\n\t\t// @ts-expect-error\r\n\t\tlisteners[name]?.forEach(handler =\u003e handler.apply(self, args))\r\n\t\t// @ts-expect-error\r\n\t\tcurrentOptions[\"on\" + name[0].toUpperCase() + name.slice(1)]?.(...args, self)\r\n\t}\r\n\r\n\tconst dispatchSelection = (force?: boolean) =\u003e {\r\n\t\tif (force || handleSelectionChange) {\r\n\t\t\tconst selection = getInputSelection()\r\n\t\t\tconst newLine =\r\n\t\t\t\tlines[(activeLineNumber = numLines(value, 0, selection[selection[2] \u003c \"f\" ? 0 : 1]))]\r\n\r\n\t\t\tif (newLine != activeLine) {\r\n\t\t\t\tactiveLine?.classList.remove(\"active-line\")\r\n\t\t\t\tnewLine.classList.add(\"active-line\")\r\n\t\t\t\tactiveLine = newLine\r\n\t\t\t}\r\n\t\t\tupdateClassName(selection)\r\n\t\t\tdispatchEvent(\"selectionChange\", selection, value)\r\n\t\t}\r\n\t}\r\n\r\n\tconst self: PrismEditor\u003cT\u003e = {\r\n\t\tcontainer: scrollContainer,\r\n\t\twrapper,\r\n\t\tlines,\r\n\t\ttextarea,\r\n\t\tget activeLine() {\r\n\t\t\treturn activeLineNumber\r\n\t\t},\r\n\t\tget value() {\r\n\t\t\treturn value\r\n\t\t},\r\n\t\toptions: currentOptions,\r\n\t\tget focused() {\r\n\t\t\treturn focused\r\n\t\t},\r\n\t\tget tokens() {\r\n\t\t\treturn tokens\r\n\t\t},\r\n\t\tinputCommandMap,\r\n\t\tkeyCommandMap,\r\n\t\textensions: {},\r\n\t\tsetOptions,\r\n\t\tupdate,\r\n\t\tgetSelection: getInputSelection,\r\n\t\taddExtensions(...extensions) {\r\n\t\t\tupdateExtensions(extensions)\r\n\t\t},\r\n\t\ton: (name, handler) =\u003e {\r\n\t\t\t;(listeners[name] ||= new Set\u003cany\u003e()).add(handler)\r\n\t\t\treturn () =\u003e listeners[name]!.delete(handler)\r\n\t\t},\r\n\t\tremove() {\r\n\t\t\tscrollContainer.remove()\r\n\t\t},\r\n\t}\r\n\r\n\taddListener(textarea, \"keydown\", e =\u003e {\r\n\t\tkeyCommandMap[e.key]?.(e, getInputSelection(), value) \u0026\u0026 preventDefault(e)\r\n\t})\r\n\r\n\taddListener(textarea, \"beforeinput\", e =\u003e {\r\n\t\tif (\r\n\t\t\treadOnly ||\r\n\t\t\t(e.inputType == \"insertText\" \u0026\u0026 inputCommandMap[e.data!]?.(e, getInputSelection(), value))\r\n\t\t)\r\n\t\t\tpreventDefault(e)\r\n\t})\r\n\taddListener(textarea, \"input\", update)\r\n\taddListener(textarea, \"blur\", () =\u003e {\r\n\t\tselectionChange = null\r\n\t\tfocused = false\r\n\t\tupdateClassName()\r\n\t})\r\n\taddListener(textarea, \"focus\", () =\u003e {\r\n\t\tselectionChange = dispatchSelection\r\n\t\tfocused = true\r\n\t\tupdateClassName()\r\n\t})\r\n\t// For browsers that support selectionchange on textareas\r\n\taddListener(textarea, \"selectionchange\", e =\u003e {\r\n\t\tdispatchSelection(!e.isTrusted)\r\n\t\tpreventDefault(e)\r\n\t})\r\n\r\n\tgetElement(container)?.append(scrollContainer)\r\n\toptions \u0026\u0026 setOptions(options)\r\n\treturn self\r\n}\r\n\r\n/**\r\n * Almost identical to {@link createEditor}, but instead of appending the editor to your\r\n * element, the editor replaces it.\r\n *\r\n * The `textContent` of the placeholder will be the code in the editor unless `options.value` is defined.\r\n * @param placeholder Node or selector which will be replaced by the editor.\r\n * @param options Options the editor is initialized with.\r\n * @param extensions Extensions added before the first render. You can still add extensions later.\r\n * @returns Object to interact with the created editor.\r\n */\r\nconst editorFromPlaceholder = \u003cT extends {} = {}\u003e(\r\n\tplaceholder: string | ChildNode,\r\n\toptions: Partial\u003cEditorOptions\u003cT\u003e\u003e \u0026 Omit\u003cT, keyof EditorOptions\u003e,\r\n\t...extensions: EditorExtension\u003cT\u003e[]\r\n) =\u003e {\r\n\tconst el = getElement(placeholder)!\r\n\tconst editor = createEditor\u003cT\u003e(\r\n\t\tnull,\r\n\t\tObject.assign({ value: el.textContent }, options),\r\n\t\t...extensions,\r\n\t)\r\n\tel.replaceWith(editor.container)\r\n\treturn editor\r\n}\r\n\r\n/** Equivalent to `document` in a browser setting, `null` otherwise. */\r\nconst doc = \"u\" \u003e typeof window ? document : null\r\n\r\nconst templateEl = /* @__PURE__ */ doc?.createElement(\"div\")\r\n\r\nconst createTemplate = \u003cT extends Element = HTMLDivElement\u003e(html: string, node?: Node) =\u003e {\r\n\tif (templateEl) {\r\n\t\ttemplateEl.innerHTML = html\r\n\t\tnode = templateEl.firstChild!\r\n\t}\r\n\treturn () =\u003e \u003cT\u003enode!.cloneNode(true)\r\n}\r\n\r\nconst addListener = \u003cT extends keyof HTMLElementEventMap\u003e(\r\n\ttarget: HTMLElement,\r\n\ttype: T,\r\n\tlistener: (this: HTMLElement, ev: HTMLElementEventMap[T]) =\u003e any,\r\n\toptions?: boolean | AddEventListenerOptions,\r\n) =\u003e target.addEventListener(type, listener, options)\r\n\r\nconst getElement = \u003cT extends Node\u003e(el?: T | string | null) =\u003e\r\n\ttypeof el == \"string\" ? doc!.querySelector\u003cHTMLElement\u003e(el) : el\r\n\r\n/**\r\n * Counts number of lines in the string between `start` and `end`.\r\n * If start and end are omitted, the whole string is searched.\r\n */\r\nconst numLines = (str: string, start = 0, end = Infinity) =\u003e {\r\n\tlet count = 1\r\n\tfor (; (start = str.indexOf(\"\\n\", start) + 1) \u0026\u0026 start \u003c= end; count++);\r\n\treturn count\r\n}\r\n\r\n/** Object storing all language specific behavior. */\r\nconst languageMap: Record\u003cstring, Language\u003e =\r\n\t((globalThis as any)[Symbol.for(\"prism-code-editor.languageMap\")] ||= {})\r\n\r\nconst editorTemplate = /* @__PURE__ */ createTemplate(\r\n\t\"\u003cdiv\u003e\u003cdiv class=pce-wrapper\u003e\u003cdiv class=pce-overlays\u003e\u003ctextarea class=pce-textarea spellcheck=false autocapitalize=off autocomplete=off\u003e\",\r\n)\r\n\r\nconst preventDefault = (e: Event) =\u003e {\r\n\te.preventDefault()\r\n\te.stopImmediatePropagation()\r\n}\r\n\r\nconst setSelectionChange = (f?: (force?: boolean) =\u003e void) =\u003e (selectionChange = f)\r\n\r\nlet selectionChange: null | undefined | ((force?: boolean) =\u003e void)\r\n\r\n// @ts-expect-error Allow adding listener to document\r\nif (doc) addListener(doc, \"selectionchange\", () =\u003e selectionChange?.())\r\n\r\nexport {\r\n\tcreateEditor,\r\n\tlanguageMap,\r\n\tnumLines,\r\n\tcreateTemplate,\r\n\tgetElement,\r\n\tpreventDefault,\r\n\teditorFromPlaceholder,\r\n\taddListener,\r\n\tsetSelectionChange,\r\n\tdoc,\r\n}\r\n","import { PrismEditor } from \"../index.js\"\nimport { addListener } from \"../core.js\"\nimport { isChrome } from \"./index.js\"\nimport { PrismCodeBlock } from \"../client/code-block.js\"\n\nconst scrollToEl = (editor: PrismEditor, el: HTMLElement, paddingTop = 0) =\u003e {\n\tconst style = editor.container.style\n\n\tstyle.setProperty(\"--_sp\", \"var(--pce-scroll-padding, 2ch)\")\n\tstyle.scrollPaddingBlock = `calc(var(--_sp) + ${paddingTop}px) calc(var(--_sp) + ${\n\t\tisChrome \u0026\u0026 !el.offsetWidth ? el.offsetHeight : 0\n\t}px)`\n\n\tel.scrollIntoView({ block: \"nearest\" })\n\tstyle.scrollPaddingBlock = \"\"\n\tstyle.removeProperty(\"--_sp\")\n}\n\nconst getLineStart = (text: string, position: number) =\u003e\n\tposition ? text.lastIndexOf(\"\\n\", position - 1) + 1 : 0\n\nconst getLineEnd = (text: string, position: number) =\u003e\n\t(position = text.indexOf(\"\\n\", position)) + 1 ? position : text.length\n\nconst addListener2 = \u003cT extends keyof HTMLElementEventMap\u003e(\n\telement: HTMLElement,\n\ttype: T,\n\tlistener: (this: HTMLElement, ev: HTMLElementEventMap[T]) =\u003e any,\n\toptions?: boolean | AddEventListenerOptions,\n) =\u003e {\n\taddListener(element, type, listener, options)\n\treturn () =\u003e element.removeEventListener(type, listener, options)\n}\n\nconst addTextareaListener = \u003cT extends keyof HTMLElementEventMap\u003e(\n\teditor: PrismEditor,\n\ttype: T,\n\tlistener: (this: HTMLElement, ev: HTMLElementEventMap[T]) =\u003e any,\n\toptions?: boolean | AddEventListenerOptions,\n) =\u003e addListener2(editor.textarea, type, listener, options)\n\nconst getStyleValue = (el: HTMLElement, prop: keyof CSSStyleDeclaration) =\u003e\n\tparseFloat(\u003cstring\u003egetComputedStyle(el)[prop])\n\nconst getPosition = (editor: PrismEditor | PrismCodeBlock, el: HTMLElement) =\u003e {\n\tconst rect1 = el.getBoundingClientRect()\n\tconst rect2 = editor.lines[0].getBoundingClientRect()\n\n\treturn {\n\t\ttop: rect1.y - rect2.y,\n\t\tbottom: rect2.bottom - rect1.bottom,\n\t\tleft: rect1.x - rect2.x,\n\t\tright: rect2.right - rect1.right,\n\t\theight: rect1.height,\n\t}\n}\n\nconst updateNode = (node: Text, text: string) =\u003e {\n\tif (node.data != text) node.data = text\n}\n\nconst voidlessLangs = new Set(\"xml,rss,atom,jsx,tsx,xquery,xeora,xeoracube,actionscript\".split(\",\"))\nconst voidTags = /^(?:area|base|w?br|col|embed|hr|img|input|link|meta|source|track)$/\n\nexport {\n\tscrollToEl,\n\tgetLineStart,\n\tgetLineEnd,\n\tgetStyleValue,\n\taddListener2,\n\taddTextareaListener,\n\tgetPosition,\n\tupdateNode,\n\tvoidTags,\n\tvoidlessLangs,\n}\n","import { InputSelection, PrismEditor } from \"../index.js\"\nimport { numLines, doc } from \"../core.js\"\nimport { addListener2, getLineEnd, getLineStart } from \"./local.js\"\nimport { PrismCodeBlock } from \"../client/code-block.js\"\nimport { escapeHtml } from \"../prism/core.js\"\n\n/**\n * If {@link insertText} has been called, this variable stores the editor's selection\n * before the call. This variable is set to 0 right before {@link insertText} returns.\n * This can therefore be used inside a `beforeinput` handler to determine if\n * {@link insertText} fired the event, and if true, get the selection before the call.\n *\n * Intended for internal use only.\n */\nlet prevSelection: InputSelection | 0\n\n/**\n * Escapes all regex syntax characters with a backslash and returns the escaped string.\n *\n * The returned string is not safe inside a character class.\n */\nconst regexEscape = (str: string) =\u003e str.replace(/[$+?|.^*()[\\]{}\\\\]/g, \"\\\\$\u0026\")\n\n/** Returns the string between the position and the previous \\n. */\nconst getLineBefore = (text: string, position: number) =\u003e\n\ttext.slice(getLineStart(text, position), position)\n\n/**\n * Gets all lines that are at least partially between `start` and `end`.\n * @param text Text to search in.\n * @param start Start of the selection.\n * @param end End of the selection. Defaults to `start`.\n * @returns A tuple containing an array of lines, the starting position of the first line,\n * and the ending position of the last line.\n */\nconst getLines = (text: string, start: number, end = start) =\u003e\n\t[\n\t\ttext.slice((start = getLineStart(text, start)), (end = getLineEnd(text, end))).split(\"\\n\"),\n\t\tstart,\n\t\tend,\n\t] as const\n\n/**\n * Searches a full line for a token that matches a selector and contains `position`\n * within the specified margins. Tokens are searched in reverse document order which means\n * children are searched before their parents.\n * @param editor Editor you want to search in.\n * @param selector CSS selector for the tokens you want to search for.\n * @param marginLeft How far to the left of the token the position can be. Defaults to 0.\n * @param marginRight How far to the right of the token the position can be. Defaults to `marginLeft`.\n * @param position Position to search in. Defaults to `selectionStart`.\n * @returns A span element if one's found or undefined if not.\n * @example\n * This will return a string token if the cursor\n * is at least 1 character inside a string token\n * ```javascript\n * getClosestToken(editor, '.string', -1)\n * ```\n */\nconst getClosestToken = (\n\teditor: PrismEditor,\n\tselector: string,\n\tmarginLeft = 0,\n\tmarginRight = marginLeft,\n\tposition = editor.getSelection()[0],\n) =\u003e {\n\tconst value = editor.value\n\tconst line = editor.lines[numLines(value, 0, position)]\n\t// We unfortunitely have to include elements, else we can't get empty tokens\n\tconst walker = doc!.createTreeWalker(line, 5)\n\n\tlet node = walker.lastChild()\n\tlet offset = getLineEnd(value, position) + 1 - position - (\u003cText\u003enode).length\n\n\twhile (-offset \u003c= marginRight \u0026\u0026 (node = walker.previousNode())) {\n\t\tif (node.lastChild) continue\n\t\toffset -= (\u003cText\u003enode).length || 0\n\n\t\tif (offset \u003c= marginLeft) {\n\t\t\tfor (; node != line; node = node.parentNode!) {\n\t\t\t\tif ((\u003cElement\u003enode).matches?.(selector)) return \u003cHTMLSpanElement\u003enode\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Gets the current language at a position.\n * Useful if you want to run different logic based on the language.\n * @param editor Editor to search in.\n * @param position Position to search in. Defaults to `selectionStart`.\n */\nconst getLanguage = (editor: PrismEditor, position?: number) =\u003e\n\tgetClosestToken(editor, \"[class*=language-]\", 0, 0, position)?.className.match(\n\t\t/language-(\\S*)/,\n\t)![1] || editor.options.language\n\n/**\n * Inserts text into the editor (unless it's read-only) while keeping undo/redo history.\n * Focuses the `textarea` if it isn't already.\n * @param editor Target editor.\n * @param text Text to insert.\n * @param start Position to start the insertion. Defaults to `selectionStart`.\n * @param end Position to end the insertion. Defaults to `start` if specified, else `selectionEnd`.\n * @param newCursorStart New starting position for the cursor. Defaults to the end of the inserted text.\n * @param newCursorEnd New ending position for the cursor. Defaults to `newCursorStart`.\n */\nconst insertText = (\n\teditor: PrismEditor,\n\ttext: string,\n\tstart?: number | null,\n\tend?: number | null,\n\tnewCursorStart?: number | null,\n\tnewCursorEnd?: number | null,\n) =\u003e {\n\tif (editor.options.readOnly) return\n\tprevSelection = editor.getSelection()\n\tend ??= start\n\n\tlet textarea = editor.textarea\n\tlet value = editor.value\n\t// Bug inserting new lines at the end if the editor ends with an empty line\n\tlet avoidBug =\n\t\tisChrome \u0026\u0026 !value[end ?? prevSelection[1]] \u0026\u0026 /\\n$/.test(text) \u0026\u0026 /^$|\\n$/.test(value)\n\tlet removeListener: () =\u003e any\n\n\teditor.focused || textarea.focus()\n\tif (start != null) textarea.setSelectionRange(start, end!)\n\n\tif (newCursorStart != null) {\n\t\tremoveListener = editor.on(\"update\", () =\u003e {\n\t\t\ttextarea.setSelectionRange(\n\t\t\t\tnewCursorStart,\n\t\t\t\tnewCursorEnd ?? newCursorStart,\n\t\t\t\t(\u003cInputSelection\u003eprevSelection)[2],\n\t\t\t)\n\t\t\tremoveListener()\n\t\t})\n\t}\n\n\t// Only Safari dispatches a beforeinput event\n\tisWebKit || textarea.dispatchEvent(new InputEvent(\"beforeinput\", { data: text }))\n\n\t// Inserting escaped HTML in Chrome and Safari instead for much better performance\n\tif (isChrome || isWebKit) {\n\t\tif (avoidBug) {\n\t\t\t// This means the last new line won't be inserted if there's\n\t\t\t// no selection, but that's less annoying than the bug.\n\t\t\ttextarea.selectionEnd--\n\t\t\ttext = text.slice(0, -1)\n\t\t}\n\t\t// New line at the end is always ignored in Safari\n\t\tif (isWebKit) text += \"\\n\"\n\t\tdoc!.execCommand(text ? \"insertHTML\" : \"delete\", false, escapeHtml(text, /\u003c/g, \"\u0026lt;\"))\n\t\tif (avoidBug) textarea.selectionStart++\n\t} else doc!.execCommand(text ? \"insertText\" : \"delete\", false, text)\n\n\tprevSelection = 0\n}\n\n/**\n * Sets the selection for the `textarea` and synchronously runs the selectionChange listeners.\n * If you don't want to synchronously run the listeners, use `textarea.setSelectionRange` instead.\n * @param editor Editor you want to change the selection of.\n * @param start New selectionStart.\n * @param end New selectionEnd. Defaults to `start`.\n * @param direction New direction.\n */\nconst setSelection = (\n\teditor: PrismEditor,\n\tstart: number,\n\tend = start,\n\tdirection?: \"backward\" | \"forward\" | \"none\",\n) =\u003e {\n\tlet textarea = editor.textarea\n\t// Webkit 18.3 and earlier focuses the textarea when changing the selection\n\tlet removeHandler = addListener2(textarea, \"focus\", e =\u003e {\n\t\tlet target = e.relatedTarget as HTMLElement\n\t\ttarget ? target.focus() : textarea.blur()\n\t})\n\n\ttextarea.setSelectionRange(start, end, direction)\n\tremoveHandler()\n\ttextarea.dispatchEvent(new Event(\"selectionchange\"))\n}\n\nconst userAgent = doc ? navigator.userAgent : \"\"\n/** Whether the user is on an Apple-based system. */\nconst isMac = doc ? /Mac|iPhone|iP[ao]d/.test(navigator.platform) : false\n/** Whether the browser is Chromium based. */\nconst isChrome = /Chrome\\//.test(userAgent)\n/** Whether the browser uses the WebKit browser engine. */\nconst isWebKit = !isChrome \u0026\u0026 /AppleWebKit\\//.test(userAgent)\n\n/**\n * Returns a 4 bit integer where each bit represents whether\n * each modifier is pressed in the order Shift, Meta, Ctrl, Alt.\n *\n * ```javascript\n * e.shiftKey \u0026\u0026 !e.metaKey \u0026\u0026 e.ctrlKey \u0026\u0026 e.altKey\n * // is equivalent to\n * getModifierCode(e) == 0b1011\n * ```\n */\nconst getModifierCode = (\n\te: KeyboardEvent, // @ts-expect-error\n): number =\u003e e.altKey + e.ctrlKey * 2 + e.metaKey * 4 + e.shiftKey * 8\n\n/**\n * Adds an overlay by appending the element to the editor's overlays.\n * Equivalent to calling `editor.lines[0].append(overlay)`.\n * @param editor Editor or code block you want to add an overlay to.\n * @param overlay The overlay you want to add.\n */\nconst addOverlay = (editor: PrismEditor | PrismCodeBlock, overlay: HTMLElement) =\u003e\n\teditor.lines[0].append(overlay)\n\n/**\n * Gets the position of the cursor in the document. It returns a tuple with three numbers:\n *\n * 1. The line number of the cursor\n * 2. The 1-based column of the cursor\n * 3. Number of characters selected\n *\n * @example\n * const [line, col, selected] = getDocumentPosition(editor)\n */\nconst getDocumentPosition = (editor: PrismEditor): [number, number, number] =\u003e {\n\tlet [start, end, dir] = editor.getSelection()\n\tlet pos = dir \u003c \"f\" ? start : end\n\tlet value = editor.value\n\tlet col = 0\n\tlet chars = 0\n\tlet tabSize = editor.options.tabSize || 2\n\n\tfor (const char of getLineBefore(value, pos)) {\n\t\tcol += char == \"\\t\" ? tabSize - (col % tabSize) : 1\n\t}\n\n\tfor (const _ of value.slice(start, end)) chars++\n\n\treturn [editor.activeLine, col + 1, chars]\n}\n\nexport {\n\tregexEscape,\n\tgetLineBefore,\n\tgetLines,\n\tgetClosestToken,\n\tgetLanguage,\n\tinsertText,\n\tgetModifierCode,\n\tsetSelection,\n\taddOverlay,\n\tgetDocumentPosition,\n\tisMac,\n\tisChrome,\n\tisWebKit,\n\tprevSelection,\n}\n","/** @module match-tags */\r\n\r\nimport { PrismEditor, BasicExtension } from \"../index.js\"\r\nimport { Token, TokenStream } from \"../prism/index.js\"\r\nimport { getClosestToken } from \"../utils/index.js\"\r\nimport { addTextareaListener, voidlessLangs, voidTags } from \"../utils/local.js\"\r\n\r\n/**\r\n * Tuple containing in the following order:\r\n * - The tag's `Token`\r\n * - Its starting position\r\n * - Its ending position\r\n * - Its tag name\r\n * - Whether it's a closing tag\r\n * - Whether it's not self-closing\r\n */\r\nexport type Tag = [Token, number, number, string, boolean, boolean]\r\n\r\nexport interface TagMatcher {\r\n\t/**\r\n\t * Array of tuples containing in the following order:\r\n\t * - The tag's `Token`\r\n\t * - Its starting position\r\n\t * - Its ending position\r\n\t * - Its tag name\r\n\t * - Whether it's a closing tag\r\n\t * - Whether it's not self-closing\r\n\t *\r\n\t * The tags are sorted by their ending position\r\n\t */\r\n\treadonly tags: Tag[]\r\n\t/** Array mapping the index of a tag to the index of its matching tag. */\r\n\treadonly pairs: (number | undefined)[]\r\n}\r\n\r\n/**\r\n * Function that adds tag matching to the editor.\r\n * @returns An object containing all tags and pairs.\r\n */\r\nexport const createTagMatcher = (editor: PrismEditor): TagMatcher =\u003e {\r\n\tlet pairMap: number[] = []\r\n\tlet code: string\r\n\tlet tags: Tag[] = []\r\n\tlet tagIndex: number\r\n\tlet sp: number\r\n\tlet stack: [number, string][] = []\r\n\r\n\tlet matchTags = (tokens: TokenStream, language: string, value: string) =\u003e {\r\n\t\tcode = value\r\n\t\ttags.length = pairMap.length = tagIndex = sp = 0\r\n\t\tmatchTagsRecursive(tokens, language, 0)\r\n\t}\r\n\r\n\tlet matchTagsRecursive = (tokens: TokenStream, language: string, position: number) =\u003e {\r\n\t\tlet noVoidTags = voidlessLangs.has(language)\r\n\t\tlet i = 0\r\n\t\tlet l = tokens.length\r\n\t\tfor (; i \u003c l; ) {\r\n\t\t\tconst token = \u003cToken\u003etokens[i++]\r\n\t\t\tconst content = token.content\r\n\t\t\tconst length = token.length\r\n\t\t\tif (Array.isArray(content)) {\r\n\t\t\t\tif (token.type == \"tag\" \u0026\u0026 code[position] == \"\u003c\") {\r\n\t\t\t\t\tconst openLen = content[0].length\r\n\t\t\t\t\tconst name = content[2] ? code.substr(position + openLen, content[1].length) : \"\"\r\n\t\t\t\t\tconst tagName = noVoidTags ? name : name.toLowerCase()\r\n\t\t\t\t\tconst notSelfClosing =\r\n\t\t\t\t\t\tcontent[content.length - 1].length \u003c 2 \u0026\u0026 (noVoidTags || !voidTags.test(tagName))\r\n\r\n\t\t\t\t\tif (content[2]) matchTagsRecursive(content, language, position)\r\n\r\n\t\t\t\t\tif (notSelfClosing) {\r\n\t\t\t\t\t\tif (openLen \u003e 1) {\r\n\t\t\t\t\t\t\tfor (let i = sp; i; ) {\r\n\t\t\t\t\t\t\t\tif (tagName == stack[--i][1]) {\r\n\t\t\t\t\t\t\t\t\tpairMap[(pairMap[tagIndex] = stack[(sp = i)][0])] = tagIndex\r\n\t\t\t\t\t\t\t\t\ti = 0\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\tstack[sp++] = [tagIndex, tagName]\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\ttags[tagIndex++] = [\r\n\t\t\t\t\t\ttoken,\r\n\t\t\t\t\t\tposition,\r\n\t\t\t\t\t\tposition + length,\r\n\t\t\t\t\t\ttagName,\r\n\t\t\t\t\t\topenLen \u003e 1,\r\n\t\t\t\t\t\tnotSelfClosing,\r\n\t\t\t\t\t]\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlet lang = token.alias || token.type\r\n\t\t\t\t\tmatchTagsRecursive(\r\n\t\t\t\t\t\tcontent,\r\n\t\t\t\t\t\tlang.slice(0, 9) == \"language-\" ? lang.slice(9) : language,\r\n\t\t\t\t\t\tposition,\r\n\t\t\t\t\t)\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tposition += length\r\n\t\t}\r\n\t}\r\n\r\n\teditor.on(\"tokenize\", matchTags)\r\n\r\n\tmatchTags(editor.tokens, editor.options.language, editor.value)\r\n\r\n\treturn {\r\n\t\ttags,\r\n\t\tpairs: pairMap,\r\n\t}\r\n}\r\n\r\nconst getClosestTagIndex = (pos: number, tags: TagMatcher[\"tags\"]) =\u003e {\r\n\tfor (let i = 0, l = tags.length; i \u003c l; i++) if (tags[i][1] \u003c= pos \u0026\u0026 tags[i][2] \u003e= pos) return i\r\n}\r\n\r\n/**\r\n * Extension that adds an `active-tagname` class to matching HTML/XML/JSX tags when the\r\n * cursor is on either tag. If the editor doesn't have a {@link TagMatcher}, one is\r\n * created. Use the CSS selector `.active-tagname` to style the elements. Obviously don't\r\n * add this if the languages used don't have tags.\r\n */\r\nexport const matchTags = (): BasicExtension =\u003e editor =\u003e {\r\n\tlet openEl: Element | undefined, closeEl: Element | undefined\r\n\tconst { tags, pairs } = (editor.extensions.matchTags ||= createTagMatcher(editor))\r\n\tconst highlight = (remove?: boolean) =\u003e\r\n\t\t[openEl, closeEl].forEach(el =\u003e {\r\n\t\t\tel \u0026\u0026 el.classList.toggle(\"active-tagname\", !remove)\r\n\t\t})\r\n\r\n\teditor.on(\"selectionChange\", ([start, end]) =\u003e {\r\n\t\tlet newEl1: Element | undefined\r\n\t\tlet newEl2: Element | undefined\r\n\t\tlet index: number\r\n\t\tif (start == end \u0026\u0026 editor.focused) {\r\n\t\t\tindex = getClosestTagIndex(start, tags)!\r\n\r\n\t\t\tif (index + 1) {\r\n\t\t\t\tindex = pairs[index]!\r\n\r\n\t\t\t\tif (index + 1 \u0026\u0026 (newEl1 = getClosestToken(editor, \".tag\u003e.tag\"))) {\r\n\t\t\t\t\tnewEl2 = getClosestToken(editor, \".tag\u003e.tag\", 2, 0, tags[index][1])\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tif (openEl != newEl1) {\r\n\t\t\thighlight(true)\r\n\t\t\topenEl = newEl1\r\n\t\t\tcloseEl = newEl2\r\n\t\t\thighlight()\r\n\t\t}\r\n\t})\r\n}\r\n\r\n/**\r\n * Extension that highlights `\u003c` and `\u003e` punctuation in XML tags.\r\n * @param className Class added to the active punctuation you can use to style them with CSS.\r\n * @param alwaysHighlight If true, the punctuation will always be highlighted when the cursor\r\n * is inside a tag. If not it will only be highlighted when the cursor is on the punctuation.\r\n */\r\nexport const highlightTagPunctuation =\r\n\t(className: string, alwaysHighlight?: boolean): BasicExtension =\u003e\r\n\teditor =\u003e {\r\n\t\tlet openEl: HTMLSpanElement, closeEl: HTMLSpanElement\r\n\t\tconst { tags } = (editor.extensions.matchTags ||= createTagMatcher(editor))\r\n\t\tconst getPunctuation = (pos?: number) =\u003e getClosestToken(editor, \".tag\u003e.punctuation\", 0, 0, pos)\r\n\t\tconst highlight = (remove?: boolean) =\u003e\r\n\t\t\t[openEl, closeEl].forEach(el =\u003e {\r\n\t\t\t\tel \u0026\u0026 el.classList.toggle(className, !remove)\r\n\t\t\t})\r\n\r\n\t\tconst selectionChange = () =\u003e {\r\n\t\t\tlet [start, end] = editor.getSelection()\r\n\t\t\tlet newEl1: HTMLSpanElement\r\n\t\t\tlet newEl2: HTMLSpanElement\r\n\t\t\tif (start == end \u0026\u0026 editor.focused) {\r\n\t\t\t\tlet tag = tags[getClosestTagIndex(start, tags)!]\r\n\r\n\t\t\t\tif (\r\n\t\t\t\t\ttag \u0026\u0026\r\n\t\t\t\t\t(alwaysHighlight || (!getClosestToken(editor, \".tag\u003e.tag\") \u0026\u0026 getPunctuation()))\r\n\t\t\t\t) {\r\n\t\t\t\t\tnewEl1 = getPunctuation(tag[1])!\r\n\t\t\t\t\tnewEl2 = getPunctuation(tag[2] - 1)!\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (openEl != newEl1! || closeEl != newEl2!) {\r\n\t\t\t\thighlight(true)\r\n\t\t\t\topenEl = newEl1!\r\n\t\t\t\tcloseEl = newEl2!\r\n\t\t\t\thighlight()\r\n\t\t\t}\r\n\t\t}\r\n\t\teditor.on(\"selectionChange\", selectionChange)\r\n\t\taddTextareaListener(editor, \"focus\", selectionChange)\r\n\t\taddTextareaListener(editor, \"blur\", selectionChange)\r\n\t}\r\n"],"version":3}
